/** * Converts the supplied String into a String suitable for address line matching. Performs the * following transformations on the supplied String: - Converts to upper case - Retrieves all * records from the AddressMatchMap table and replaces all occurrences of addressMatchMap.mapKey * with addressMatchMap.mapValue using upper case matching. - Removes all non-word characters from * the String i.e. everything except A-Z, 0-9 and _ * * @param delegator A Delegator instance * @param address The address String to convert * @return The converted Address */ public static String makeMatchingString(Delegator delegator, String address) { if (address == null) { return null; } // upper case the address String str = address.trim().toUpperCase(); // replace mapped words List<GenericValue> addressMap = null; try { addressMap = delegator.findList( "AddressMatchMap", null, null, UtilMisc.toList("sequenceNum"), null, false); } catch (GenericEntityException e) { Debug.logError(e, module); } if (addressMap != null) { for (GenericValue v : addressMap) { str = str.replaceAll( v.getString("mapKey").toUpperCase(), v.getString("mapValue").toUpperCase()); } } // remove all non-word characters return str.replaceAll("\\W", ""); }
public static List<String> getAssociatedPartyIdsByRelationshipType( Delegator delegator, String partyIdFrom, String partyRelationshipTypeId) { List<GenericValue> partyList = FastList.newInstance(); List<String> partyIds = null; try { EntityConditionList<EntityExpr> baseExprs = EntityCondition.makeCondition( UtilMisc.toList( EntityCondition.makeCondition("partyIdFrom", partyIdFrom), EntityCondition.makeCondition( "partyRelationshipTypeId", partyRelationshipTypeId)), EntityOperator.AND); List<GenericValue> associatedParties = delegator.findList("PartyRelationship", baseExprs, null, null, null, true); partyList.addAll(associatedParties); while (UtilValidate.isNotEmpty(associatedParties)) { List<GenericValue> currentAssociatedParties = FastList.newInstance(); for (GenericValue associatedParty : associatedParties) { EntityConditionList<EntityExpr> innerExprs = EntityCondition.makeCondition( UtilMisc.toList( EntityCondition.makeCondition( "partyIdFrom", associatedParty.get("partyIdTo")), EntityCondition.makeCondition( "partyRelationshipTypeId", partyRelationshipTypeId)), EntityOperator.AND); List<GenericValue> associatedPartiesChilds = delegator.findList("PartyRelationship", innerExprs, null, null, null, true); if (UtilValidate.isNotEmpty(associatedPartiesChilds)) { currentAssociatedParties.addAll(associatedPartiesChilds); } partyList.add(associatedParty); } associatedParties = currentAssociatedParties; } partyIds = EntityUtil.getFieldListFromEntityList(partyList, "partyIdTo", true); } catch (GenericEntityException e) { Debug.logWarning(e, module); } return partyIds; }
/** * Returns a complete category trail - can be used for exporting proper category trees. This is * mostly useful when used in combination with bread-crumbs, for building a faceted index tree, or * to export a category tree for migration to another system. Will create the tree from root point * to categoryId. * * <p>This method is not meant to be run on every request. Its best use is to generate the trail * every so often and store somewhere (a lucene/solr tree, entities, cache or so). * * @param dctx The DispatchContext that this service is operating in * @param context Map containing the input parameters * @return Map organized trail from root point to categoryId. */ public static Map getCategoryTrail(DispatchContext dctx, Map context) { String productCategoryId = (String) context.get("productCategoryId"); Map<String, Object> results = ServiceUtil.returnSuccess(); Delegator delegator = dctx.getDelegator(); List<String> trailElements = FastList.newInstance(); trailElements.add(productCategoryId); String parentProductCategoryId = productCategoryId; while (UtilValidate.isNotEmpty(parentProductCategoryId)) { // find product category rollup try { List<EntityCondition> rolllupConds = FastList.newInstance(); rolllupConds.add( EntityCondition.makeCondition("productCategoryId", parentProductCategoryId)); rolllupConds.add(EntityUtil.getFilterByDateExpr()); List<GenericValue> productCategoryRollups = delegator.findList( "ProductCategoryRollup", EntityCondition.makeCondition(rolllupConds), null, UtilMisc.toList("sequenceNum"), null, true); if (UtilValidate.isNotEmpty(productCategoryRollups)) { // add only categories that belong to the top category to trail for (GenericValue productCategoryRollup : productCategoryRollups) { String trailCategoryId = productCategoryRollup.getString("parentProductCategoryId"); parentProductCategoryId = trailCategoryId; if (trailElements.contains(trailCategoryId)) { break; } else { trailElements.add(trailCategoryId); } } } else { parentProductCategoryId = null; } } catch (GenericEntityException e) { Map<String, String> messageMap = UtilMisc.toMap("errMessage", ". Cannot generate trail from product category. "); String errMsg = UtilProperties.getMessage( "CommonUiLabels", "CommonDatabaseProblem", messageMap, (Locale) context.get("locale")); Debug.logError(e, errMsg, module); return ServiceUtil.returnError(errMsg); } } Collections.reverse(trailElements); results.put("trail", trailElements); return results; }
public static Map<String, Object> convertOrderIdListToHeaders( DispatchContext dctx, Map<String, ? extends Object> context) { Delegator delegator = dctx.getDelegator(); List<GenericValue> orderHeaderList = UtilGenerics.checkList(context.get("orderHeaderList")); List<String> orderIdList = UtilGenerics.checkList(context.get("orderIdList")); // we don't want to process if there is already a header list if (orderHeaderList == null) { // convert the ID list to headers if (orderIdList != null) { List<EntityCondition> conditionList1 = FastList.newInstance(); List<EntityCondition> conditionList2 = FastList.newInstance(); // we are only concerned about approved sales orders conditionList2.add( EntityCondition.makeCondition("statusId", EntityOperator.EQUALS, "ORDER_APPROVED")); conditionList2.add( EntityCondition.makeCondition("orderTypeId", EntityOperator.EQUALS, "SALES_ORDER")); // build the expression list from the IDs for (String orderId : orderIdList) { conditionList1.add( EntityCondition.makeCondition("orderId", EntityOperator.EQUALS, orderId)); } // create the conditions EntityCondition idCond = EntityCondition.makeCondition(conditionList1, EntityOperator.OR); conditionList2.add(idCond); EntityCondition cond = EntityCondition.makeCondition(conditionList2, EntityOperator.AND); // run the query try { orderHeaderList = delegator.findList( "OrderHeader", cond, null, UtilMisc.toList("+orderDate"), null, false); } catch (GenericEntityException e) { Debug.logError(e, module); return ServiceUtil.returnError(e.getMessage()); } Debug.logInfo("Recieved orderIdList - " + orderIdList, module); Debug.logInfo("Found orderHeaderList - " + orderHeaderList, module); } } Map<String, Object> result = ServiceUtil.returnSuccess(); result.put("orderHeaderList", orderHeaderList); return result; }
public static void getCategoriesWithNoParent(ServletRequest request, String attributeName) { Delegator delegator = (Delegator) request.getAttribute("delegator"); Collection<GenericValue> results = FastList.newInstance(); try { Collection<GenericValue> allCategories = delegator.findList("ProductCategory", null, null, null, null, false); for (GenericValue curCat : allCategories) { Collection<GenericValue> parentCats = curCat.getRelatedCache("CurrentProductCategoryRollup"); if (parentCats.isEmpty()) results.add(curCat); } } catch (GenericEntityException e) { Debug.logWarning(e, module); } request.setAttribute(attributeName, results); }
@Override public boolean exec(MethodContext methodContext) throws MiniLangException { String entityName = entityNameFse.expandString(methodContext.getEnvMap()); boolean useCache = "true".equals(useCacheFse.expandString(methodContext.getEnvMap())); boolean useIterator = "true".equals(useIteratorFse.expandString(methodContext.getEnvMap())); List<String> orderByNames = orderByListFma.get(methodContext.getEnvMap()); Collection<String> fieldsToSelectList = fieldsToSelectListFma.get(methodContext.getEnvMap()); Delegator delegator = getDelegator(methodContext); try { EntityCondition whereCond = null; Map<String, ? extends Object> fieldMap = mapFma.get(methodContext.getEnvMap()); if (fieldMap != null) { whereCond = EntityCondition.makeCondition(fieldMap); } if (useIterator) { listFma.put( methodContext.getEnvMap(), delegator.find( entityName, whereCond, null, UtilMisc.toSet(fieldsToSelectList), orderByNames, null)); } else { listFma.put( methodContext.getEnvMap(), delegator.findList( entityName, whereCond, UtilMisc.toSet(fieldsToSelectList), orderByNames, null, useCache)); } } catch (GenericEntityException e) { String errMsg = "Exception thrown while performing entity find: " + e.getMessage(); Debug.logWarning(e, errMsg, module); simpleMethod.addErrorMessage(methodContext, errMsg); return false; } return true; }
private static void getTaxAuthorities( Delegator delegator, GenericValue shippingAddress, Set<GenericValue> taxAuthoritySet) throws GenericEntityException { Map<String, String> geoIdByTypeMap = FastMap.newInstance(); if (shippingAddress != null) { if (UtilValidate.isNotEmpty(shippingAddress.getString("countryGeoId"))) { geoIdByTypeMap.put("COUNTRY", shippingAddress.getString("countryGeoId")); } if (UtilValidate.isNotEmpty(shippingAddress.getString("stateProvinceGeoId"))) { geoIdByTypeMap.put("STATE", shippingAddress.getString("stateProvinceGeoId")); } if (UtilValidate.isNotEmpty(shippingAddress.getString("countyGeoId"))) { geoIdByTypeMap.put("COUNTY", shippingAddress.getString("countyGeoId")); } String postalCodeGeoId = ContactMechWorker.getPostalAddressPostalCodeGeoId(shippingAddress, delegator); if (UtilValidate.isNotEmpty(postalCodeGeoId)) { geoIdByTypeMap.put("POSTAL_CODE", postalCodeGeoId); } } else { Debug.logWarning("shippingAddress was null, adding nothing to taxAuthoritySet", module); } // Debug.logInfo("Tax calc geoIdByTypeMap before expand:" + geoIdByTypeMap + "; this is for // shippingAddress=" + shippingAddress, module); // get the most granular, or all available, geoIds and then find parents by GeoAssoc with // geoAssocTypeId="REGIONS" and geoIdTo=<granular geoId> and find the GeoAssoc.geoId geoIdByTypeMap = GeoWorker.expandGeoRegionDeep(geoIdByTypeMap, delegator); // Debug.logInfo("Tax calc geoIdByTypeMap after expand:" + geoIdByTypeMap, module); List<GenericValue> taxAuthorityRawList = delegator.findList( "TaxAuthority", EntityCondition.makeCondition( "taxAuthGeoId", EntityOperator.IN, geoIdByTypeMap.values()), null, null, null, true); taxAuthoritySet.addAll(taxAuthorityRawList); // Debug.logInfo("Tax calc taxAuthoritySet after expand:" + taxAuthoritySet, module); }
/** * Finds all matching parties based on the values provided. Excludes party records with a statusId * of PARTY_DISABLED. Results are ordered by descending PartyContactMech.fromDate. 1. Candidate * addresses are found by querying PartyAndPostalAddress using the supplied city and if provided, * stateProvinceGeoId, postalCode, postalCodeExt and countryGeoId 2. In-memory address line * comparisons are then performed against the supplied address1 and if provided, address2. Address * lines are compared after the strings have been converted using {@link * #makeMatchingString(Delegator, String)}. * * @param delegator Delegator instance * @param address1 PostalAddress.address1 to match against (Required). * @param address2 Optional PostalAddress.address2 to match against. * @param city PostalAddress.city value to match against (Required). * @param stateProvinceGeoId Optional PostalAddress.stateProvinceGeoId value to match against. If * null or "**" is passed then the value will be ignored during matching. "NA" can be passed * in place of "_NA_". * @param postalCode PostalAddress.postalCode value to match against. Cannot be null but can be * skipped by passing a value starting with an "*". If the length of the supplied string is 10 * characters and the string contains a "-" then the postal code will be split at the "-" and * the second half will be used as the postalCodeExt. * @param postalCodeExt Optional PostalAddress.postalCodeExt value to match against. Will be * overridden if a postalCodeExt value is retrieved from postalCode as described above. * @param countryGeoId Optional PostalAddress.countryGeoId value to match against. * @param partyTypeId Optional Party.partyTypeId to match against. * @return List of PartyAndPostalAddress GenericValue objects that match the supplied criteria. * @throws GenericEntityException */ public static List<GenericValue> findMatchingPartyPostalAddress( Delegator delegator, String address1, String address2, String city, String stateProvinceGeoId, String postalCode, String postalCodeExt, String countryGeoId, String partyTypeId) throws GenericEntityException { if (address1 == null || city == null || postalCode == null) { throw new IllegalArgumentException(); } List<EntityCondition> addrExprs = FastList.newInstance(); if (stateProvinceGeoId != null) { if ("**".equals(stateProvinceGeoId)) { Debug.logWarning("Illegal state code passed!", module); } else if ("NA".equals(stateProvinceGeoId)) { addrExprs.add( EntityCondition.makeCondition("stateProvinceGeoId", EntityOperator.EQUALS, "_NA_")); } else { addrExprs.add( EntityCondition.makeCondition( "stateProvinceGeoId", EntityOperator.EQUALS, stateProvinceGeoId.toUpperCase())); } } if (!postalCode.startsWith("*")) { if (postalCode.length() == 10 && postalCode.indexOf("-") != -1) { String[] zipSplit = postalCode.split("-", 2); postalCode = zipSplit[0]; postalCodeExt = zipSplit[1]; } addrExprs.add(EntityCondition.makeCondition("postalCode", EntityOperator.EQUALS, postalCode)); } if (postalCodeExt != null) { addrExprs.add( EntityCondition.makeCondition("postalCodeExt", EntityOperator.EQUALS, postalCodeExt)); } addrExprs.add( EntityCondition.makeCondition( EntityFunction.UPPER_FIELD("city"), EntityOperator.EQUALS, EntityFunction.UPPER(city))); if (countryGeoId != null) { addrExprs.add( EntityCondition.makeCondition( "countryGeoId", EntityOperator.EQUALS, countryGeoId.toUpperCase())); } // limit to only non-disabled status addrExprs.add( EntityCondition.makeCondition( EntityCondition.makeCondition("statusId", EntityOperator.EQUALS, null), EntityOperator.OR, EntityCondition.makeCondition("statusId", EntityOperator.NOT_EQUAL, "PARTY_DISABLED"))); if (partyTypeId != null) { addrExprs.add( EntityCondition.makeCondition("partyTypeId", EntityOperator.EQUALS, partyTypeId)); } List<String> sort = UtilMisc.toList("-fromDate"); EntityCondition addrCond = EntityCondition.makeCondition(addrExprs, EntityOperator.AND); List<GenericValue> addresses = EntityUtil.filterByDate( delegator.findList("PartyAndPostalAddress", addrCond, null, sort, null, false)); // Debug.logInfo("Checking for matching address: " + addrCond.toString() + "[" + // addresses.size() + "]", module); if (UtilValidate.isEmpty(addresses)) { // No address matches, return an empty list return addresses; } List<GenericValue> validFound = FastList.newInstance(); // check the address line for (GenericValue address : addresses) { // address 1 field String addr1Source = PartyWorker.makeMatchingString(delegator, address1); String addr1Target = PartyWorker.makeMatchingString(delegator, address.getString("address1")); if (addr1Target != null) { Debug.logInfo("Comparing address1 : " + addr1Source + " / " + addr1Target, module); if (addr1Target.equals(addr1Source)) { // address 2 field if (address2 != null) { String addr2Source = PartyWorker.makeMatchingString(delegator, address2); String addr2Target = PartyWorker.makeMatchingString(delegator, address.getString("address2")); if (addr2Target != null) { Debug.logInfo("Comparing address2 : " + addr2Source + " / " + addr2Target, module); if (addr2Source.equals(addr2Target)) { Debug.logInfo("Matching address2; adding valid address", module); validFound.add(address); // validParty.put(address.getString("partyId"), address.getString("contactMechId")); } } } else { if (address.get("address2") == null) { Debug.logInfo("No address2; adding valid address", module); validFound.add(address); // validParty.put(address.getString("partyId"), address.getString("contactMechId")); } } } } } return validFound; }
private static void handlePartyTaxExempt( GenericValue adjValue, Set<String> billToPartyIdSet, String taxAuthGeoId, String taxAuthPartyId, BigDecimal taxAmount, Timestamp nowTimestamp, Delegator delegator) throws GenericEntityException { Debug.logInfo("Checking for tax exemption : " + taxAuthGeoId + " / " + taxAuthPartyId, module); List<EntityCondition> ptiConditionList = UtilMisc.<EntityCondition>toList( EntityCondition.makeCondition("partyId", EntityOperator.IN, billToPartyIdSet), EntityCondition.makeCondition("taxAuthGeoId", EntityOperator.EQUALS, taxAuthGeoId), EntityCondition.makeCondition("taxAuthPartyId", EntityOperator.EQUALS, taxAuthPartyId)); ptiConditionList.add( EntityCondition.makeCondition("fromDate", EntityOperator.LESS_THAN_EQUAL_TO, nowTimestamp)); ptiConditionList.add( EntityCondition.makeCondition( EntityCondition.makeCondition("thruDate", EntityOperator.EQUALS, null), EntityOperator.OR, EntityCondition.makeCondition("thruDate", EntityOperator.GREATER_THAN, nowTimestamp))); EntityCondition ptiCondition = EntityCondition.makeCondition(ptiConditionList, EntityOperator.AND); // sort by -fromDate to get the newest (largest) first, just in case there is more than one, we // only want the most recent valid one, should only be one per jurisdiction... List<GenericValue> partyTaxInfos = delegator.findList( "PartyTaxAuthInfo", ptiCondition, null, UtilMisc.toList("-fromDate"), null, false); boolean foundExemption = false; if (partyTaxInfos.size() > 0) { GenericValue partyTaxInfo = partyTaxInfos.get(0); adjValue.set("customerReferenceId", partyTaxInfo.get("partyTaxId")); if ("Y".equals(partyTaxInfo.getString("isExempt"))) { adjValue.set("amount", BigDecimal.ZERO); adjValue.set("exemptAmount", taxAmount); foundExemption = true; } } // if no exceptions were found for the current; try the parent if (!foundExemption) { // try the "parent" TaxAuthority List<GenericValue> taxAuthorityAssocList = delegator.findByAndCache( "TaxAuthorityAssoc", UtilMisc.toMap( "toTaxAuthGeoId", taxAuthGeoId, "toTaxAuthPartyId", taxAuthPartyId, "taxAuthorityAssocTypeId", "EXEMPT_INHER"), UtilMisc.toList("-fromDate")); taxAuthorityAssocList = EntityUtil.filterByDate(taxAuthorityAssocList, true); GenericValue taxAuthorityAssoc = EntityUtil.getFirst(taxAuthorityAssocList); // Debug.logInfo("Parent assoc to " + taxAuthGeoId + " : " + taxAuthorityAssoc, module); if (taxAuthorityAssoc != null) { handlePartyTaxExempt( adjValue, billToPartyIdSet, taxAuthorityAssoc.getString("taxAuthGeoId"), taxAuthorityAssoc.getString("taxAuthPartyId"), taxAmount, nowTimestamp, delegator); } } }
public static Map<String, Object> rateProductTaxCalcForDisplay( DispatchContext dctx, Map<String, ? extends Object> context) { Delegator delegator = dctx.getDelegator(); String productStoreId = (String) context.get("productStoreId"); String billToPartyId = (String) context.get("billToPartyId"); String productId = (String) context.get("productId"); BigDecimal quantity = (BigDecimal) context.get("quantity"); BigDecimal basePrice = (BigDecimal) context.get("basePrice"); BigDecimal shippingPrice = (BigDecimal) context.get("shippingPrice"); Locale locale = (Locale) context.get("locale"); if (quantity == null) quantity = ONE_BASE; BigDecimal amount = basePrice.multiply(quantity); BigDecimal taxTotal = ZERO_BASE; BigDecimal taxPercentage = ZERO_BASE; BigDecimal priceWithTax = basePrice; if (shippingPrice != null) priceWithTax = priceWithTax.add(shippingPrice); try { GenericValue product = delegator.findByPrimaryKeyCache("Product", UtilMisc.toMap("productId", productId)); GenericValue productStore = delegator.findByPrimaryKeyCache( "ProductStore", UtilMisc.toMap("productStoreId", productStoreId)); if (productStore == null) { throw new IllegalArgumentException( "Could not find ProductStore with ID [" + productStoreId + "] for tax calculation"); } if ("Y".equals(productStore.getString("showPricesWithVatTax"))) { Set<GenericValue> taxAuthoritySet = FastSet.newInstance(); if (productStore.get("vatTaxAuthPartyId") == null) { List<GenericValue> taxAuthorityRawList = delegator.findList( "TaxAuthority", EntityCondition.makeCondition( "taxAuthGeoId", EntityOperator.EQUALS, productStore.get("vatTaxAuthGeoId")), null, null, null, true); taxAuthoritySet.addAll(taxAuthorityRawList); } else { GenericValue taxAuthority = delegator.findByPrimaryKeyCache( "TaxAuthority", UtilMisc.toMap( "taxAuthGeoId", productStore.get("vatTaxAuthGeoId"), "taxAuthPartyId", productStore.get("vatTaxAuthPartyId"))); taxAuthoritySet.add(taxAuthority); } if (taxAuthoritySet.size() == 0) { throw new IllegalArgumentException( "Could not find any Tax Authories for store with ID [" + productStoreId + "] for tax calculation; the store settings may need to be corrected."); } List<GenericValue> taxAdustmentList = getTaxAdjustments( delegator, product, productStore, null, billToPartyId, taxAuthoritySet, basePrice, quantity, amount, shippingPrice, ZERO_BASE); if (taxAdustmentList.size() == 0) { // this is something that happens every so often for different products and such, so don't // blow up on it... Debug.logWarning( "Could not find any Tax Authories Rate Rules for store with ID [" + productStoreId + "], productId [" + productId + "], basePrice [" + basePrice + "], amount [" + amount + "], for tax calculation; the store settings may need to be corrected.", module); } // add up amounts from adjustments (amount OR exemptAmount, sourcePercentage) for (GenericValue taxAdjustment : taxAdustmentList) { if ("SALES_TAX".equals(taxAdjustment.getString("orderAdjustmentTypeId"))) { taxPercentage = taxPercentage.add(taxAdjustment.getBigDecimal("sourcePercentage")); BigDecimal adjAmount = taxAdjustment.getBigDecimal("amount"); taxTotal = taxTotal.add(adjAmount); priceWithTax = priceWithTax.add( adjAmount.divide(quantity, salestaxCalcDecimals, salestaxRounding)); Debug.logInfo( "For productId [" + productId + "] added [" + adjAmount.divide(quantity, salestaxCalcDecimals, salestaxRounding) + "] of tax to price for geoId [" + taxAdjustment.getString("taxAuthGeoId") + "], new price is [" + priceWithTax + "]", module); } } } } catch (GenericEntityException e) { Debug.logError(e, "Data error getting tax settings: " + e.toString(), module); return ServiceUtil.returnError( UtilProperties.getMessage( resource, "AccountingTaxSettingError", UtilMisc.toMap("errorString", e.toString()), locale)); } // round to 2 decimal places for display/etc taxTotal = taxTotal.setScale(salestaxFinalDecimals, salestaxRounding); priceWithTax = priceWithTax.setScale(salestaxFinalDecimals, salestaxRounding); Map<String, Object> result = ServiceUtil.returnSuccess(); result.put("taxTotal", taxTotal); result.put("taxPercentage", taxPercentage); result.put("priceWithTax", priceWithTax); return result; }
private static List<GenericValue> getTaxAdjustments( Delegator delegator, GenericValue product, GenericValue productStore, String payToPartyId, String billToPartyId, Set<GenericValue> taxAuthoritySet, BigDecimal itemPrice, BigDecimal itemQuantity, BigDecimal itemAmount, BigDecimal shippingAmount, BigDecimal orderPromotionsAmount) { Timestamp nowTimestamp = UtilDateTime.nowTimestamp(); List<GenericValue> adjustments = FastList.newInstance(); if (payToPartyId == null) { if (productStore != null) { payToPartyId = productStore.getString("payToPartyId"); } } // store expr EntityCondition storeCond = null; if (productStore != null) { storeCond = EntityCondition.makeCondition( EntityCondition.makeCondition( "productStoreId", EntityOperator.EQUALS, productStore.get("productStoreId")), EntityOperator.OR, EntityCondition.makeCondition("productStoreId", EntityOperator.EQUALS, null)); } else { storeCond = EntityCondition.makeCondition("productStoreId", EntityOperator.EQUALS, null); } // build the TaxAuthority expressions (taxAuthGeoId, taxAuthPartyId) List<EntityCondition> taxAuthCondOrList = FastList.newInstance(); // start with the _NA_ TaxAuthority... taxAuthCondOrList.add( EntityCondition.makeCondition( EntityCondition.makeCondition("taxAuthPartyId", EntityOperator.EQUALS, "_NA_"), EntityOperator.AND, EntityCondition.makeCondition("taxAuthGeoId", EntityOperator.EQUALS, "_NA_"))); for (GenericValue taxAuthority : taxAuthoritySet) { EntityCondition taxAuthCond = EntityCondition.makeCondition( EntityCondition.makeCondition( "taxAuthPartyId", EntityOperator.EQUALS, taxAuthority.getString("taxAuthPartyId")), EntityOperator.AND, EntityCondition.makeCondition( "taxAuthGeoId", EntityOperator.EQUALS, taxAuthority.getString("taxAuthGeoId"))); taxAuthCondOrList.add(taxAuthCond); } EntityCondition taxAuthoritiesCond = EntityCondition.makeCondition(taxAuthCondOrList, EntityOperator.OR); try { EntityCondition productCategoryCond = null; if (product != null) { // find the tax categories associated with the product and filter by those, with an IN // clause or some such // if this product is variant, find the virtual product id and consider also the categories // of the virtual // question: get all categories, or just a special type? for now let's do all categories... String virtualProductId = null; if ("Y".equals(product.getString("isVariant"))) { virtualProductId = ProductWorker.getVariantVirtualId(product); } Set<String> productCategoryIdSet = FastSet.newInstance(); EntityCondition productIdCond = null; if (virtualProductId != null) { productIdCond = EntityCondition.makeCondition( EntityCondition.makeCondition( "productId", EntityOperator.EQUALS, product.getString("productId")), EntityOperator.OR, EntityCondition.makeCondition( "productId", EntityOperator.EQUALS, virtualProductId)); } else { productIdCond = EntityCondition.makeCondition( "productId", EntityOperator.EQUALS, product.getString("productId")); } List<GenericValue> pcmList = delegator.findList( "ProductCategoryMember", productIdCond, UtilMisc.toSet("productCategoryId", "fromDate", "thruDate"), null, null, true); pcmList = EntityUtil.filterByDate(pcmList, true); for (GenericValue pcm : pcmList) { productCategoryIdSet.add(pcm.getString("productCategoryId")); } if (productCategoryIdSet.size() == 0) { productCategoryCond = EntityCondition.makeCondition("productCategoryId", EntityOperator.EQUALS, null); } else { productCategoryCond = EntityCondition.makeCondition( EntityCondition.makeCondition("productCategoryId", EntityOperator.EQUALS, null), EntityOperator.OR, EntityCondition.makeCondition( "productCategoryId", EntityOperator.IN, productCategoryIdSet)); } } else { productCategoryCond = EntityCondition.makeCondition("productCategoryId", EntityOperator.EQUALS, null); } // FIXME handles shipping and promo tax. Simple solution, see // https://issues.apache.org/jira/browse/OFBIZ-4160 for a better one if (product == null && shippingAmount != null) { EntityCondition taxShippingCond = EntityCondition.makeCondition( EntityCondition.makeCondition("taxShipping", EntityOperator.EQUALS, null), EntityOperator.OR, EntityCondition.makeCondition("taxShipping", EntityOperator.EQUALS, "Y")); if (productCategoryCond != null) { productCategoryCond = EntityCondition.makeCondition( productCategoryCond, EntityOperator.OR, taxShippingCond); } } if (product == null && orderPromotionsAmount != null) { EntityCondition taxOrderPromotionsCond = EntityCondition.makeCondition( EntityCondition.makeCondition("taxPromotions", EntityOperator.EQUALS, null), EntityOperator.OR, EntityCondition.makeCondition("taxPromotions", EntityOperator.EQUALS, "Y")); if (productCategoryCond != null) { productCategoryCond = EntityCondition.makeCondition( productCategoryCond, EntityOperator.OR, taxOrderPromotionsCond); } } // build the main condition clause List<EntityCondition> mainExprs = UtilMisc.toList(storeCond, taxAuthoritiesCond, productCategoryCond); mainExprs.add( EntityCondition.makeCondition( EntityCondition.makeCondition("minItemPrice", EntityOperator.EQUALS, null), EntityOperator.OR, EntityCondition.makeCondition( "minItemPrice", EntityOperator.LESS_THAN_EQUAL_TO, itemPrice))); mainExprs.add( EntityCondition.makeCondition( EntityCondition.makeCondition("minPurchase", EntityOperator.EQUALS, null), EntityOperator.OR, EntityCondition.makeCondition( "minPurchase", EntityOperator.LESS_THAN_EQUAL_TO, itemAmount))); EntityCondition mainCondition = EntityCondition.makeCondition(mainExprs, EntityOperator.AND); // create the orderby clause List<String> orderList = UtilMisc.<String>toList("minItemPrice", "minPurchase", "fromDate"); // finally ready... do the rate query List<GenericValue> lookupList = delegator.findList( "TaxAuthorityRateProduct", mainCondition, null, orderList, null, false); List<GenericValue> filteredList = EntityUtil.filterByDate(lookupList, true); if (filteredList.size() == 0) { Debug.logWarning( "In TaxAuthority Product Rate no records were found for condition:" + mainCondition.toString(), module); return adjustments; } // find the right entry(s) based on purchase amount for (GenericValue taxAuthorityRateProduct : filteredList) { BigDecimal taxRate = taxAuthorityRateProduct.get("taxPercentage") != null ? taxAuthorityRateProduct.getBigDecimal("taxPercentage") : ZERO_BASE; BigDecimal taxable = ZERO_BASE; if (product != null && (product.get("taxable") == null || (product.get("taxable") != null && product.getBoolean("taxable").booleanValue()))) { taxable = taxable.add(itemAmount); } if (shippingAmount != null && taxAuthorityRateProduct != null && (taxAuthorityRateProduct.get("taxShipping") == null || (taxAuthorityRateProduct.get("taxShipping") != null && taxAuthorityRateProduct.getBoolean("taxShipping").booleanValue()))) { taxable = taxable.add(shippingAmount); } if (orderPromotionsAmount != null && taxAuthorityRateProduct != null && (taxAuthorityRateProduct.get("taxPromotions") == null || (taxAuthorityRateProduct.get("taxPromotions") != null && taxAuthorityRateProduct.getBoolean("taxPromotions").booleanValue()))) { taxable = taxable.add(orderPromotionsAmount); } if (taxable.compareTo(BigDecimal.ZERO) == 0) { // this should make it less confusing if the taxable flag on the product is not Y/true, // and there is no shipping and such continue; } // taxRate is in percentage, so needs to be divided by 100 BigDecimal taxAmount = (taxable.multiply(taxRate)) .divide(PERCENT_SCALE, salestaxCalcDecimals, salestaxRounding); String taxAuthGeoId = taxAuthorityRateProduct.getString("taxAuthGeoId"); String taxAuthPartyId = taxAuthorityRateProduct.getString("taxAuthPartyId"); // get glAccountId from TaxAuthorityGlAccount entity using the payToPartyId as the // organizationPartyId GenericValue taxAuthorityGlAccount = delegator.findByPrimaryKey( "TaxAuthorityGlAccount", UtilMisc.toMap( "taxAuthPartyId", taxAuthPartyId, "taxAuthGeoId", taxAuthGeoId, "organizationPartyId", payToPartyId)); String taxAuthGlAccountId = null; if (taxAuthorityGlAccount != null) { taxAuthGlAccountId = taxAuthorityGlAccount.getString("glAccountId"); } else { // TODO: what to do if no TaxAuthorityGlAccount found? Use some default, or is that done // elsewhere later on? } GenericValue productPrice = null; if (product != null && taxAuthPartyId != null && taxAuthGeoId != null) { // find a ProductPrice for the productId and taxAuth* valxues, and see if it has a // priceWithTax value Map<String, String> priceFindMap = UtilMisc.toMap( "productId", product.getString("productId"), "taxAuthPartyId", taxAuthPartyId, "taxAuthGeoId", taxAuthGeoId, "productPricePurposeId", "PURCHASE"); List<GenericValue> productPriceList = delegator.findByAnd("ProductPrice", priceFindMap, UtilMisc.toList("-fromDate")); productPriceList = EntityUtil.filterByDate(productPriceList, true); productPrice = (productPriceList != null && productPriceList.size() > 0) ? productPriceList.get(0) : null; // Debug.logInfo("=================== productId=" + product.getString("productId"), // module); // Debug.logInfo("=================== productPrice=" + productPrice, module); } GenericValue taxAdjValue = delegator.makeValue("OrderAdjustment"); if (productPrice != null && "Y".equals(productPrice.getString("taxInPrice"))) { // tax is in the price already, so we want the adjustment to be a VAT_TAX adjustment to be // subtracted instead of a SALES_TAX adjustment to be added taxAdjValue.set("orderAdjustmentTypeId", "VAT_TAX"); // the amount will be different because we want to figure out how much of the price was // tax, and not how much tax needs to be added // the formula is: taxAmount = priceWithTax - (priceWithTax/(1+taxPercentage/100)) BigDecimal taxAmountIncluded = itemAmount.subtract( itemAmount.divide( BigDecimal.ONE.add( taxRate.divide(PERCENT_SCALE, 4, BigDecimal.ROUND_HALF_UP)), 3, BigDecimal.ROUND_HALF_UP)); taxAdjValue.set("amountAlreadyIncluded", taxAmountIncluded); taxAdjValue.set("amount", BigDecimal.ZERO); } else { taxAdjValue.set("orderAdjustmentTypeId", "SALES_TAX"); taxAdjValue.set("amount", taxAmount); } taxAdjValue.set("sourcePercentage", taxRate); taxAdjValue.set( "taxAuthorityRateSeqId", taxAuthorityRateProduct.getString("taxAuthorityRateSeqId")); // the primary Geo should be the main jurisdiction that the tax is for, and the secondary // would just be to define a parent or wrapping jurisdiction of the primary taxAdjValue.set("primaryGeoId", taxAuthGeoId); taxAdjValue.set("comments", taxAuthorityRateProduct.getString("description")); if (taxAuthPartyId != null) taxAdjValue.set("taxAuthPartyId", taxAuthPartyId); if (taxAuthGlAccountId != null) taxAdjValue.set("overrideGlAccountId", taxAuthGlAccountId); if (taxAuthGeoId != null) taxAdjValue.set("taxAuthGeoId", taxAuthGeoId); // check to see if this party has a tax ID for this, and if the party is tax exempt in the // primary (most-local) jurisdiction if (UtilValidate.isNotEmpty(billToPartyId) && UtilValidate.isNotEmpty(taxAuthGeoId)) { // see if partyId is a member of any groups, if so honor their tax exemptions // look for PartyRelationship with partyRelationshipTypeId=GROUP_ROLLUP, the partyIdTo is // the group member, so the partyIdFrom is the groupPartyId Set<String> billToPartyIdSet = FastSet.newInstance(); billToPartyIdSet.add(billToPartyId); List<GenericValue> partyRelationshipList = EntityUtil.filterByDate( delegator.findByAndCache( "PartyRelationship", UtilMisc.toMap( "partyIdTo", billToPartyId, "partyRelationshipTypeId", "GROUP_ROLLUP")), true); for (GenericValue partyRelationship : partyRelationshipList) { billToPartyIdSet.add(partyRelationship.getString("partyIdFrom")); } handlePartyTaxExempt( taxAdjValue, billToPartyIdSet, taxAuthGeoId, taxAuthPartyId, taxAmount, nowTimestamp, delegator); } else { Debug.logInfo( "NOTE: A tax calculation was done without a billToPartyId or taxAuthGeoId, so no tax exemptions or tax IDs considered; billToPartyId=[" + billToPartyId + "] taxAuthGeoId=[" + taxAuthGeoId + "]", module); } adjustments.add(taxAdjValue); if (productPrice != null && itemQuantity != null && productPrice.getBigDecimal("priceWithTax") != null && !"Y".equals(productPrice.getString("taxInPrice"))) { BigDecimal priceWithTax = productPrice.getBigDecimal("priceWithTax"); BigDecimal price = productPrice.getBigDecimal("price"); BigDecimal baseSubtotal = price.multiply(itemQuantity); BigDecimal baseTaxAmount = (baseSubtotal.multiply(taxRate)) .divide(PERCENT_SCALE, salestaxCalcDecimals, salestaxRounding); // Debug.logInfo("=================== priceWithTax=" + priceWithTax, module); // Debug.logInfo("=================== enteredTotalPriceWithTax=" + // enteredTotalPriceWithTax, module); // Debug.logInfo("=================== calcedTotalPriceWithTax=" + calcedTotalPriceWithTax, // module); // tax is not already in price so we want to add it in, but this is a VAT situation so // adjust to make it as accurate as possible // for VAT taxes if the calculated total item price plus calculated taxes is different // from what would be // expected based on the original entered price with taxes (if the price was entered this // way), then create // an adjustment that corrects for the difference, and this correction will be effectively // subtracted from the // price and not from the tax (the tax is meant to be calculated based on Tax Authority // rules and so should // not be shorted) // TODO (don't think this is needed, but just to keep it in mind): get this to work with // multiple VAT tax authorities instead of just one (right now will get incorrect totals // if there are multiple taxes included in the price) // TODO add constraint to ProductPrice lookup by any productStoreGroupId associated with // the current productStore BigDecimal enteredTotalPriceWithTax = priceWithTax.multiply(itemQuantity); BigDecimal calcedTotalPriceWithTax = (baseSubtotal).add(baseTaxAmount); if (!enteredTotalPriceWithTax.equals(calcedTotalPriceWithTax)) { // if the calced amount is higher than the entered amount we want the value to be // negative // to get it down to match the entered amount // so, subtract the calced amount from the entered amount (ie: correction = entered - // calced) BigDecimal correctionAmount = enteredTotalPriceWithTax.subtract(calcedTotalPriceWithTax); // Debug.logInfo("=================== correctionAmount=" + correctionAmount, module); GenericValue correctionAdjValue = delegator.makeValue("OrderAdjustment"); correctionAdjValue.set( "taxAuthorityRateSeqId", taxAuthorityRateProduct.getString("taxAuthorityRateSeqId")); correctionAdjValue.set("amount", correctionAmount); // don't set this, causes a doubling of the tax rate because calling code adds up all // tax rates: correctionAdjValue.set("sourcePercentage", taxRate); correctionAdjValue.set("orderAdjustmentTypeId", "VAT_PRICE_CORRECT"); // the primary Geo should be the main jurisdiction that the tax is for, and the // secondary would just be to define a parent or wrapping jurisdiction of the primary correctionAdjValue.set("primaryGeoId", taxAuthGeoId); correctionAdjValue.set("comments", taxAuthorityRateProduct.getString("description")); if (taxAuthPartyId != null) correctionAdjValue.set("taxAuthPartyId", taxAuthPartyId); if (taxAuthGlAccountId != null) correctionAdjValue.set("overrideGlAccountId", taxAuthGlAccountId); if (taxAuthGeoId != null) correctionAdjValue.set("taxAuthGeoId", taxAuthGeoId); adjustments.add(correctionAdjValue); } } } } catch (GenericEntityException e) { Debug.logError(e, "Problems looking up tax rates", module); return FastList.newInstance(); } return adjustments; }