Author: mbrohl
Date: Fri Dec 15 20:15:42 2017 New Revision: 1818325 URL: http://svn.apache.org/viewvc?rev=1818325&view=rev Log: Improved: General refactoring and code improvements, package org.apache.ofbiz.accounting.tax. (OFBIZ-9877) Thanks Julian Leichert for reporting and providing the patches. Modified: ofbiz/ofbiz-framework/trunk/applications/accounting/src/main/java/org/apache/ofbiz/accounting/tax/TaxAuthorityServices.java Modified: ofbiz/ofbiz-framework/trunk/applications/accounting/src/main/java/org/apache/ofbiz/accounting/tax/TaxAuthorityServices.java URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/applications/accounting/src/main/java/org/apache/ofbiz/accounting/tax/TaxAuthorityServices.java?rev=1818325&r1=1818324&r2=1818325&view=diff ============================================================================== --- ofbiz/ofbiz-framework/trunk/applications/accounting/src/main/java/org/apache/ofbiz/accounting/tax/TaxAuthorityServices.java (original) +++ ofbiz/ofbiz-framework/trunk/applications/accounting/src/main/java/org/apache/ofbiz/accounting/tax/TaxAuthorityServices.java Fri Dec 15 20:15:42 2017 @@ -63,7 +63,8 @@ public class TaxAuthorityServices { public static final int salestaxRounding = UtilNumber.getBigDecimalRoundingMode("salestax.rounding"); public static final String resource = "AccountingUiLabels"; - public static Map<String, Object> rateProductTaxCalcForDisplay(DispatchContext dctx, Map<String, ? extends Object> context) { + 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"); @@ -73,40 +74,54 @@ public class TaxAuthorityServices { BigDecimal shippingPrice = (BigDecimal) context.get("shippingPrice"); Locale locale = (Locale) context.get("locale"); - if (quantity == null) quantity = ONE_BASE; + 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); + if (shippingPrice != null) { + priceWithTax = priceWithTax.add(shippingPrice); + } try { - GenericValue product = EntityQuery.use(delegator).from("Product").where("productId", productId).cache().queryOne(); - GenericValue productStore = EntityQuery.use(delegator).from("ProductStore").where("productStoreId", productStoreId).cache().queryOne(); + GenericValue product = EntityQuery.use(delegator).from("Product").where("productId", productId).cache() + .queryOne(); + GenericValue productStore = EntityQuery.use(delegator).from("ProductStore").where("productStoreId", + productStoreId).cache().queryOne(); if (productStore == null) { - throw new IllegalArgumentException("Could not find ProductStore with ID [" + productStoreId + "] for tax calculation"); + throw new IllegalArgumentException("Could not find ProductStore with ID [" + productStoreId + + "] for tax calculation"); } if ("Y".equals(productStore.getString("showPricesWithVatTax"))) { - Set<GenericValue> taxAuthoritySet = new HashSet<GenericValue>(); + Set<GenericValue> taxAuthoritySet = new HashSet<>(); if (productStore.get("vatTaxAuthPartyId") == null) { List<GenericValue> taxAuthorityRawList = EntityQuery.use(delegator).from("TaxAuthority") .where("taxAuthGeoId", productStore.get("vatTaxAuthGeoId")).cache().queryList(); taxAuthoritySet.addAll(taxAuthorityRawList); } else { - GenericValue taxAuthority = EntityQuery.use(delegator).from("TaxAuthority").where("taxAuthGeoId", productStore.get("vatTaxAuthGeoId"), "taxAuthPartyId", productStore.get("vatTaxAuthPartyId")).cache().queryOne(); + GenericValue taxAuthority = EntityQuery.use(delegator).from("TaxAuthority").where("taxAuthGeoId", + productStore.get("vatTaxAuthGeoId"), "taxAuthPartyId", productStore.get( + "vatTaxAuthPartyId")).cache().queryOne(); 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."); + 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); + 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); + // 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) @@ -115,14 +130,19 @@ public class TaxAuthorityServices { 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); + 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)); + return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingTaxSettingError", UtilMisc + .toMap("errorString", e.toString()), locale)); } // round to 2 decimal places for display/etc @@ -155,53 +175,66 @@ public class TaxAuthorityServices { GenericValue facility = null; try { if (productStoreId != null) { - productStore = EntityQuery.use(delegator).from("ProductStore").where("productStoreId", productStoreId).queryOne(); + productStore = EntityQuery.use(delegator).from("ProductStore").where("productStoreId", productStoreId) + .queryOne(); } if (facilityId != null) { facility = EntityQuery.use(delegator).from("Facility").where("facilityId", facilityId).queryOne(); } } 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)); + return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingTaxSettingError", UtilMisc + .toMap("errorString", e.toString()), locale)); } if (productStore == null && payToPartyId == null) { throw new IllegalArgumentException("Could not find payToPartyId or ProductStore for tax calculation"); } - + if (shippingAddress == null && facility != null) { - // if there is no shippingAddress and there is a facility it means it is a face-to-face sale so get facility's address + // if there is no shippingAddress and there is a facility it means it is a + // face-to-face sale so get facility's address try { - GenericValue facilityContactMech = ContactMechWorker.getFacilityContactMechByPurpose(delegator, facilityId, UtilMisc.toList("SHIP_ORIG_LOCATION", "PRIMARY_LOCATION")); + GenericValue facilityContactMech = ContactMechWorker.getFacilityContactMechByPurpose(delegator, + facilityId, UtilMisc.toList("SHIP_ORIG_LOCATION", "PRIMARY_LOCATION")); if (facilityContactMech != null) { - shippingAddress = EntityQuery.use(delegator).from("PostalAddress").where("contactMechId", facilityContactMech.get("contactMechId")).queryOne(); + shippingAddress = EntityQuery.use(delegator).from("PostalAddress").where("contactMechId", + facilityContactMech.get("contactMechId")).queryOne(); } } 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)); + return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingTaxSettingError", UtilMisc + .toMap("errorString", e.toString()), locale)); } } - if (shippingAddress == null || (shippingAddress.get("countryGeoId") == null && shippingAddress.get("stateProvinceGeoId") == null && shippingAddress.get("postalCodeGeoId") == null)) { + if (shippingAddress == null || (shippingAddress.get("countryGeoId") == null && shippingAddress.get( + "stateProvinceGeoId") == null && shippingAddress.get("postalCodeGeoId") == null)) { String errMsg = UtilProperties.getMessage(resource, "AccountingTaxNoAddressSpecified", locale); if (shippingAddress != null) { - errMsg += UtilProperties.getMessage(resource, "AccountingTaxNoAddressSpecifiedDetails", UtilMisc.toMap("contactMechId", shippingAddress.getString("contactMechId"), "address1", shippingAddress.get("address1"), "postalCodeGeoId", shippingAddress.get("postalCodeGeoId"), "stateProvinceGeoId", shippingAddress.get("stateProvinceGeoId"), "countryGeoId", shippingAddress.get("countryGeoId")), locale); + errMsg += UtilProperties.getMessage(resource, "AccountingTaxNoAddressSpecifiedDetails", UtilMisc.toMap( + "contactMechId", shippingAddress.getString("contactMechId"), "address1", shippingAddress.get( + "address1"), "postalCodeGeoId", shippingAddress.get("postalCodeGeoId"), + "stateProvinceGeoId", shippingAddress.get("stateProvinceGeoId"), "countryGeoId", shippingAddress + .get("countryGeoId")), locale); Debug.logError(errMsg, module); } return ServiceUtil.returnError(errMsg); } - // without knowing the TaxAuthority parties, just find all TaxAuthories for the set of IDs... - Set<GenericValue> taxAuthoritySet = new HashSet<GenericValue>(); + // without knowing the TaxAuthority parties, just find all TaxAuthories for the + // set of IDs... + Set<GenericValue> taxAuthoritySet = new HashSet<>(); try { getTaxAuthorities(delegator, shippingAddress, taxAuthoritySet); } 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)); + return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingTaxSettingError", UtilMisc + .toMap("errorString", e.toString()), locale)); } // Setup the return lists. - List<GenericValue> orderAdjustments = new LinkedList<GenericValue>(); - List<List<GenericValue>> itemAdjustments = new LinkedList<List<GenericValue>>(); + List<GenericValue> orderAdjustments = new LinkedList<>(); + List<List<GenericValue>> itemAdjustments = new LinkedList<>(); // Loop through the products; get the taxCategory; and lookup each in the cache. for (int i = 0; i < itemProductList.size(); i++) { @@ -210,18 +243,22 @@ public class TaxAuthorityServices { BigDecimal itemPrice = itemPriceList.get(i); BigDecimal itemQuantity = itemQuantityList != null ? itemQuantityList.get(i) : null; BigDecimal shippingAmount = itemShippingList != null ? itemShippingList.get(i) : null; - - List<GenericValue> taxList = getTaxAdjustments(delegator, product, productStore, payToPartyId, billToPartyId, taxAuthoritySet, itemPrice, itemQuantity, itemAmount, shippingAmount, ZERO_BASE); - - // this is an add and not an addAll because we want a List of Lists of GenericValues, one List of Adjustments per item + + List<GenericValue> taxList = getTaxAdjustments(delegator, product, productStore, payToPartyId, + billToPartyId, taxAuthoritySet, itemPrice, itemQuantity, itemAmount, shippingAmount, ZERO_BASE); + + // this is an add and not an addAll because we want a List of Lists of + // GenericValues, one List of Adjustments per item itemAdjustments.add(taxList); } if (orderShippingAmount != null && orderShippingAmount.compareTo(BigDecimal.ZERO) > 0) { - List<GenericValue> taxList = getTaxAdjustments(delegator, null, productStore, payToPartyId, billToPartyId, taxAuthoritySet, ZERO_BASE, ZERO_BASE, ZERO_BASE, orderShippingAmount, ZERO_BASE); + List<GenericValue> taxList = getTaxAdjustments(delegator, null, productStore, payToPartyId, billToPartyId, + taxAuthoritySet, ZERO_BASE, ZERO_BASE, ZERO_BASE, orderShippingAmount, ZERO_BASE); orderAdjustments.addAll(taxList); } if (orderPromotionsAmount != null && orderPromotionsAmount.compareTo(BigDecimal.ZERO) != 0) { - List<GenericValue> taxList = getTaxAdjustments(delegator, null, productStore, payToPartyId, billToPartyId, taxAuthoritySet, ZERO_BASE, ZERO_BASE, ZERO_BASE, ZERO_BASE, orderPromotionsAmount); + List<GenericValue> taxList = getTaxAdjustments(delegator, null, productStore, payToPartyId, billToPartyId, + taxAuthoritySet, ZERO_BASE, ZERO_BASE, ZERO_BASE, ZERO_BASE, orderPromotionsAmount); orderAdjustments.addAll(taxList); } @@ -232,8 +269,9 @@ public class TaxAuthorityServices { return result; } - private static void getTaxAuthorities(Delegator delegator, GenericValue shippingAddress, Set<GenericValue> taxAuthoritySet) throws GenericEntityException { - Map<String, String> geoIdByTypeMap = new HashMap<String, String>(); + private static void getTaxAuthorities(Delegator delegator, GenericValue shippingAddress, + Set<GenericValue> taxAuthoritySet) throws GenericEntityException { + Map<String, String> geoIdByTypeMap = new HashMap<>(); if (shippingAddress != null) { if (UtilValidate.isNotEmpty(shippingAddress.getString("countryGeoId"))) { geoIdByTypeMap.put("COUNTRY", shippingAddress.getString("countryGeoId")); @@ -252,20 +290,24 @@ public class TaxAuthorityServices { Debug.logWarning("shippingAddress was null, adding nothing to taxAuthoritySet", 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 + // 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); List<GenericValue> taxAuthorityRawList = EntityQuery.use(delegator) - .from("TaxAuthority").where(EntityCondition.makeCondition("taxAuthGeoId", EntityOperator.IN, geoIdByTypeMap.values())).cache().queryList(); + .from("TaxAuthority").where(EntityCondition.makeCondition("taxAuthGeoId", EntityOperator.IN, + geoIdByTypeMap.values())).cache().queryList(); taxAuthoritySet.addAll(taxAuthorityRawList); } - private static List<GenericValue> getTaxAdjustments(Delegator delegator, GenericValue product, GenericValue productStore, - String payToPartyId, String billToPartyId, Set<GenericValue> taxAuthoritySet, - BigDecimal itemPrice, BigDecimal itemQuantity, BigDecimal itemAmount, + 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 = new LinkedList<GenericValue>(); + List<GenericValue> adjustments = new LinkedList<>(); if (payToPartyId == null) { if (productStore != null) { @@ -277,7 +319,8 @@ public class TaxAuthorityServices { EntityCondition storeCond = null; if (productStore != null) { storeCond = EntityCondition.makeCondition( - EntityCondition.makeCondition("productStoreId", EntityOperator.EQUALS, productStore.get("productStoreId")), + EntityCondition.makeCondition("productStoreId", EntityOperator.EQUALS, productStore.get( + "productStoreId")), EntityOperator.OR, EntityCondition.makeCondition("productStoreId", EntityOperator.EQUALS, null)); } else { @@ -285,7 +328,7 @@ public class TaxAuthorityServices { } // build the TaxAuthority expressions (taxAuthGeoId, taxAuthPartyId) - List<EntityCondition> taxAuthCondOrList = new LinkedList<EntityCondition>(); + List<EntityCondition> taxAuthCondOrList = new LinkedList<>(); // start with the _NA_ TaxAuthority... taxAuthCondOrList.add(EntityCondition.makeCondition( EntityCondition.makeCondition("taxAuthPartyId", EntityOperator.EQUALS, "_NA_"), @@ -294,9 +337,11 @@ public class TaxAuthorityServices { for (GenericValue taxAuthority : taxAuthoritySet) { EntityCondition taxAuthCond = EntityCondition.makeCondition( - EntityCondition.makeCondition("taxAuthPartyId", EntityOperator.EQUALS, taxAuthority.getString("taxAuthPartyId")), + EntityCondition.makeCondition("taxAuthPartyId", EntityOperator.EQUALS, taxAuthority.getString( + "taxAuthPartyId")), EntityOperator.AND, - EntityCondition.makeCondition("taxAuthGeoId", EntityOperator.EQUALS, taxAuthority.getString("taxAuthGeoId"))); + EntityCondition.makeCondition("taxAuthGeoId", EntityOperator.EQUALS, taxAuthority.getString( + "taxAuthGeoId"))); taxAuthCondOrList.add(taxAuthCond); } EntityCondition taxAuthoritiesCond = EntityCondition.makeCondition(taxAuthCondOrList, EntityOperator.OR); @@ -305,29 +350,36 @@ public class TaxAuthorityServices { EntityCondition productCategoryCond; productCategoryCond = setProductCategoryCond(delegator, product); - // FIXME handles shipping and promo tax. Simple solution, see https://issues.apache.org/jira/browse/OFBIZ-4160 for a better one + // 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")); + EntityCondition.makeCondition("taxShipping", EntityOperator.EQUALS, null), + EntityOperator.OR, + EntityCondition.makeCondition("taxShipping", EntityOperator.EQUALS, "Y")); - productCategoryCond = EntityCondition.makeCondition(productCategoryCond, EntityOperator.OR, taxShippingCond); + 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")); + EntityCondition.makeCondition("taxPromotions", EntityOperator.EQUALS, null), + EntityOperator.OR, + EntityCondition.makeCondition("taxPromotions", EntityOperator.EQUALS, "Y")); - productCategoryCond = EntityCondition.makeCondition(productCategoryCond, EntityOperator.OR, taxOrderPromotionsCond); + 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))); + 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); // finally ready... do the rate query @@ -335,109 +387,141 @@ public class TaxAuthorityServices { .where(mainCondition).orderBy("minItemPrice", "minPurchase", "fromDate").filterByDate().queryList(); if (lookupList.size() == 0) { - Debug.logWarning("In TaxAuthority Product Rate no records were found for condition:" + mainCondition.toString(), module); + 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 : lookupList) { - BigDecimal taxRate = taxAuthorityRateProduct.get("taxPercentage") != null ? taxAuthorityRateProduct.getBigDecimal("taxPercentage") : ZERO_BASE; + 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()))) { + if (product != null && (product.get("taxable") == null || (product.get("taxable") != null && product + .getBoolean("taxable").booleanValue()))) { taxable = taxable.add(itemAmount); } - if (shippingAmount != null && (taxAuthorityRateProduct.get("taxShipping") == null || (taxAuthorityRateProduct.get("taxShipping") != null && taxAuthorityRateProduct.getBoolean("taxShipping").booleanValue()))) { + if (shippingAmount != null && (taxAuthorityRateProduct.get("taxShipping") == null + || (taxAuthorityRateProduct.get("taxShipping") != null && taxAuthorityRateProduct.getBoolean( + "taxShipping").booleanValue()))) { taxable = taxable.add(shippingAmount); } - if (orderPromotionsAmount != null && (taxAuthorityRateProduct.get("taxPromotions") == null || (taxAuthorityRateProduct.get("taxPromotions") != null && taxAuthorityRateProduct.getBoolean("taxPromotions").booleanValue()))) { + if (orderPromotionsAmount != 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 + // 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); + 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 + // get glAccountId from TaxAuthorityGlAccount entity using the payToPartyId as + // the organizationPartyId GenericValue taxAuthorityGlAccount = EntityQuery.use(delegator).from("TaxAuthorityGlAccount") - .where("taxAuthPartyId", taxAuthPartyId, "taxAuthGeoId", taxAuthGeoId, "organizationPartyId", payToPartyId).queryOne(); + .where("taxAuthPartyId", taxAuthPartyId, "taxAuthGeoId", taxAuthGeoId, "organizationPartyId", + payToPartyId).queryOne(); 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? + // 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* values, and see if it has a priceWithTax value + // find a ProductPrice for the productId and taxAuth* values, and see if it has + // a priceWithTax value productPrice = EntityQuery.use(delegator).from("ProductPrice") - .where("productId", product.get("productId"), - "taxAuthPartyId", taxAuthPartyId, "taxAuthGeoId", taxAuthGeoId, + .where("productId", product.get("productId"), + "taxAuthPartyId", taxAuthPartyId, "taxAuthGeoId", taxAuthGeoId, "productPricePurposeId", "PURCHASE") .orderBy("-fromDate").filterByDate().queryFirst(); - - + if (productPrice == null) { - GenericValue virtualProduct = ProductWorker.getParentProduct(product.getString("productId"), delegator); - if (virtualProduct != null) { - productPrice = EntityQuery.use(delegator).from("ProductPrice") - .where("productId", virtualProduct.get("productId"), - "taxAuthPartyId", taxAuthPartyId, "taxAuthGeoId", taxAuthGeoId, + GenericValue virtualProduct = ProductWorker.getParentProduct(product.getString("productId"), + delegator); + if (virtualProduct != null) { + productPrice = EntityQuery.use(delegator).from("ProductPrice") + .where("productId", virtualProduct.get("productId"), + "taxAuthPartyId", taxAuthPartyId, "taxAuthGeoId", taxAuthGeoId, "productPricePurposeId", "PURCHASE") .orderBy("-fromDate").filterByDate().queryFirst(); - } + } } } GenericValue taxAdjValue = delegator.makeValue("OrderAdjustment"); BigDecimal discountedSalesTax = BigDecimal.ZERO; taxAdjValue.set("orderAdjustmentTypeId", "SALES_TAX"); - if (productPrice != null && "Y".equals(productPrice.getString("taxInPrice")) && itemQuantity != BigDecimal.ZERO) { - // For example product price is 43 with 20% VAT(means product actual price is 35.83). + if (productPrice != null && "Y".equals(productPrice.getString("taxInPrice")) + && itemQuantity != BigDecimal.ZERO) { + // For example product price is 43 with 20% VAT(means product actual price is + // 35.83). // itemPrice = 43; // itemQuantity = 3; // taxAmountIncludedInFullPrice = (43-(43/(1+(20/100))))*3 = 21.51 taxAdjValue.set("orderAdjustmentTypeId", "VAT_TAX"); - BigDecimal taxAmountIncludedInFullPrice = itemPrice.subtract(itemPrice.divide(BigDecimal.ONE.add(taxRate.divide(PERCENT_SCALE, 4, RoundingMode.HALF_UP)), 2, RoundingMode.HALF_UP)).multiply(itemQuantity); - // If 1 quantity has 50% discount then itemAmount = 107.5 otherwise 129 (In case of no discount) + BigDecimal taxAmountIncludedInFullPrice = itemPrice.subtract(itemPrice.divide(BigDecimal.ONE.add( + taxRate.divide(PERCENT_SCALE, 4, RoundingMode.HALF_UP)), 2, RoundingMode.HALF_UP)).multiply( + itemQuantity); + // If 1 quantity has 50% discount then itemAmount = 107.5 otherwise 129 (In case + // of no discount) // Net price for each item // netItemPrice = itemAmount / quantity = 107.5 / 3 = 35.833333333 BigDecimal netItemPrice = itemAmount.divide(itemQuantity, RoundingMode.HALF_UP); - // Calculate tax on the discounted price, be sure to round to 2 decimal places before multiplying by quantity + // Calculate tax on the discounted price, be sure to round to 2 decimal places + // before multiplying by quantity // netTax = (netItemPrice - netItemPrice / (1 + (taxRate/100))) * quantity // netTax = (35.833333333-(35.833333333/(1+(20/100))))*3 = 17.92 - BigDecimal netTax = netItemPrice.subtract(netItemPrice.divide(BigDecimal.ONE.add(taxRate.divide(PERCENT_SCALE, 4, RoundingMode.HALF_UP)), 2, RoundingMode.HALF_UP)).multiply(itemQuantity); - //Subtract net tax from base tax (taxAmountIncludedFullPrice) to get the negative promotion tax adjustment amount - // discountedSalesTax = 17.92 - 21.51 = â3.59 (If no discounted item quantity then discountedSalesTax will be ZERO) + BigDecimal netTax = netItemPrice.subtract(netItemPrice.divide(BigDecimal.ONE.add(taxRate.divide( + PERCENT_SCALE, 4, RoundingMode.HALF_UP)), 2, RoundingMode.HALF_UP)).multiply(itemQuantity); + // Subtract net tax from base tax (taxAmountIncludedFullPrice) to get the + // negative promotion tax adjustment amount + // discountedSalesTax = 17.92 - 21.51 = â3.59 (If no discounted item quantity + // then discountedSalesTax will be ZERO) discountedSalesTax = netTax.subtract(taxAmountIncludedInFullPrice); taxAdjValue.set("amountAlreadyIncluded", taxAmountIncludedInFullPrice); taxAdjValue.set("amount", BigDecimal.ZERO); } else { 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 + // 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); + 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 + // 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 = new HashSet<String>(); + // look for PartyRelationship with partyRelationshipTypeId=GROUP_ROLLUP, the + // partyIdTo is the group member, so the partyIdFrom is the groupPartyId + Set<String> billToPartyIdSet = new HashSet<>(); billToPartyIdSet.add(billToPartyId); List<GenericValue> partyRelationshipList = EntityQuery.use(delegator).from("PartyRelationship") .where("partyIdTo", billToPartyId, "partyRelationshipTypeId", "GROUP_ROLLUP") @@ -446,9 +530,12 @@ public class TaxAuthorityServices { for (GenericValue partyRelationship : partyRelationshipList) { billToPartyIdSet.add(partyRelationship.getString("partyIdFrom")); } - handlePartyTaxExempt(taxAdjValue, billToPartyIdSet, taxAuthGeoId, taxAuthPartyId, taxAmount, nowTimestamp, delegator); + 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); + 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); } if (discountedSalesTax.compareTo(BigDecimal.ZERO) < 0) { GenericValue taxAdjValueNegative = delegator.makeValue("OrderAdjustment"); @@ -458,60 +545,81 @@ public class TaxAuthorityServices { } adjustments.add(taxAdjValue); - if (productPrice != null && itemQuantity != null && - productPrice.getBigDecimal("priceWithTax") != null && + 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); - - // 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 + BigDecimal baseTaxAmount = (baseSubtotal.multiply(taxRate)).divide(PERCENT_SCALE, + salestaxCalcDecimals, salestaxRounding); + + // 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 - + + // 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 calculated 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 calculated amount from the entered amount (ie: correction = entered - calculated) + // if the calculated 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 calculated amount from the entered amount (ie: correction = + // entered - calculated) BigDecimal correctionAmount = enteredTotalPriceWithTax.subtract(calcedTotalPriceWithTax); - + GenericValue correctionAdjValue = delegator.makeValue("OrderAdjustment"); - correctionAdjValue.set("taxAuthorityRateSeqId", taxAuthorityRateProduct.getString("taxAuthorityRateSeqId")); + 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); + // 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 + // 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); + 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 new LinkedList<GenericValue>(); + return new LinkedList<>(); } return adjustments; } /** - * Private helper method which determines, based on the state of the - * product, how the ProdCondition should be set for the main condition. - * + * Private helper method which determines, based on the state of the product, + * how the ProdCondition should be set for the main condition. + * * @param delegator * @param product * which may be null @@ -535,7 +643,7 @@ public class TaxAuthorityServices { if ("Y".equals(product.getString("isVariant"))) { virtualProductId = ProductWorker.getVariantVirtualId(product); } - Set<String> productCategoryIdSet = new HashSet<String>(); + Set<String> productCategoryIdSet = new HashSet<>(); EntityCondition productIdCond = null; if (virtualProductId != null) { productIdCond = EntityCondition.makeCondition( @@ -562,17 +670,25 @@ public class TaxAuthorityServices { } - private static void handlePartyTaxExempt(GenericValue adjValue, Set<String> billToPartyIdSet, String taxAuthGeoId, String taxAuthPartyId, BigDecimal taxAmount, Timestamp nowTimestamp, Delegator delegator) throws GenericEntityException { + 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))); + 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... - GenericValue partyTaxInfo = EntityQuery.use(delegator).from("PartyTaxAuthInfo").where(ptiCondition).orderBy("-fromDate").queryFirst(); + // 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... + GenericValue partyTaxInfo = EntityQuery.use(delegator).from("PartyTaxAuthInfo").where(ptiCondition).orderBy( + "-fromDate").queryFirst(); boolean foundExemption = false; if (partyTaxInfo != null) { @@ -588,10 +704,12 @@ public class TaxAuthorityServices { if (!foundExemption) { // try the "parent" TaxAuthority GenericValue taxAuthorityAssoc = EntityQuery.use(delegator).from("TaxAuthorityAssoc") - .where("toTaxAuthGeoId", taxAuthGeoId, "toTaxAuthPartyId", taxAuthPartyId, "taxAuthorityAssocTypeId", "EXEMPT_INHER") + .where("toTaxAuthGeoId", taxAuthGeoId, "toTaxAuthPartyId", taxAuthPartyId, + "taxAuthorityAssocTypeId", "EXEMPT_INHER") .orderBy("-fromDate").filterByDate().queryFirst(); if (taxAuthorityAssoc != null) { - handlePartyTaxExempt(adjValue, billToPartyIdSet, taxAuthorityAssoc.getString("taxAuthGeoId"), taxAuthorityAssoc.getString("taxAuthPartyId"), taxAmount, nowTimestamp, delegator); + handlePartyTaxExempt(adjValue, billToPartyIdSet, taxAuthorityAssoc.getString("taxAuthGeoId"), + taxAuthorityAssoc.getString("taxAuthPartyId"), taxAmount, nowTimestamp, delegator); } } } |
Free forum by Nabble | Edit this page |