On Dec 7, 2010, at 11:58 PM, Jacques Le Roux wrote: > From: "David E Jones" <[email protected]> >> On Dec 7, 2010, at 3:39 AM, Jacques Le Roux wrote: >> >>> If there are no plans to prevent the use of the new fields for the other >>> types Price Types than Default, I'd like latter to add a small js script to >>> handle it. Of course being able to use it with Price Rules and Promo would >>> be great. >> >> Why would we want to prevent using these fields for other price types? > > I thought only the Default Price is really using VAT included price. So to > prevent users thinking they can use VAT included price with other price types.
Any price could, and should if applicable, have tax authority settings and have VAT tax included. >>> Also what for is used taxAuthCombinedId? I don't see an use at the UI level. >> >> With a combined ID you can select the tax authority in a drop-down instead >> of forcing the user to handle two separate fields that are not correlated, >> making the UI significantly more cumbersome and difficult to use. > > Yes but then I think we need to expose a bit more how to enter a combined ID, > I will add a tooltip somewhere in the screen A user wouldn't generally enter the combined ID manually, that would be a bad UI. The combined ID is just meant to be the ID in a drop-down (or perhaps a lookup). -David >> >>> From: "David E Jones" <[email protected]> >>>> I commented before that my previous efforts only made calculations more >>>> accurate, and those efforts made it clear that the only way to be sure is >>>> to calculate things based on prices with tax included. >>>> >>>> So, now OFBiz has low-level support for prices with tax included and >>>> calculating VAT instead of sales tax with adjustments that represent >>>> amounts already included in the prices. >>>> >>>> Note that this does not include any UI changes, and so unless changes are >>>> done to allow entry of prices with tax included and to show the VAT_TAX >>>> adjustments (specifically the new amountAlreadyIncluded field, which was >>>> added so that it wouldn't interfere with the current amount field). >>>> >>>> -David >>>> >>>> >>>> On Dec 6, 2010, at 8:39 AM, Jacques Le Roux wrote: >>>> >>>>> Hi David, >>>>> >>>>> Interesting, what decided you to finally shift your ground? >>>>> >>>>> Is it still true that only Default Price can be used, and if yes is there >>>>> any blocking reasons? >>>>> >>>>> Thanks >>>>> >>>>> Jacques >>>>> >>>>> From: <[email protected]> >>>>>> Author: jonesde >>>>>> Date: Mon Dec 6 08:05:44 2010 >>>>>> New Revision: 1042542 >>>>>> >>>>>> URL: http://svn.apache.org/viewvc?rev=1042542&view=rev >>>>>> Log: >>>>>> Implemented alternative way of saving ProductPrices with tax included in >>>>>> the price, and then calculating VAT tax as an exclusive instead of >>>>>> inclusive amount that goes on a new field on OrderAdjustment; also added >>>>>> a method to OrderReadHelper to get tax for display; there are various >>>>>> changes to service descriptions and entity fields to describe these >>>>>> changes >>>>>> >>>>>> Modified: >>>>>> ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/tax/TaxAuthorityServices.java >>>>>> ofbiz/trunk/applications/order/data/OrderTypeData.xml >>>>>> ofbiz/trunk/applications/order/entitydef/entitymodel.xml >>>>>> ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderReadHelper.java >>>>>> ofbiz/trunk/applications/product/entitydef/entitymodel.xml >>>>>> ofbiz/trunk/applications/product/script/org/ofbiz/product/price/PriceServices.xml >>>>>> ofbiz/trunk/applications/product/servicedef/services.xml >>>>>> >>>>>> Modified: >>>>>> ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/tax/TaxAuthorityServices.java >>>>>> URL: >>>>>> http://svn.apache.org/viewvc/ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/tax/TaxAuthorityServices.java?rev=1042542&r1=1042541&r2=1042542&view=diff >>>>>> ============================================================================== >>>>>> --- >>>>>> ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/tax/TaxAuthorityServices.java >>>>>> (original) >>>>>> +++ >>>>>> ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/tax/TaxAuthorityServices.java >>>>>> Mon Dec 6 08:05:44 2010 >>>>>> @@ -391,11 +391,38 @@ public class TaxAuthorityServices { >>>>>> // 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, >>>>>> + "productPriceTypeId", "DEFAULT_PRICE", >>>>>> "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"); >>>>>> - taxAdjValue.set("taxAuthorityRateSeqId", >>>>>> taxAuthorityRateProduct.getString("taxAuthorityRateSeqId")); >>>>>> - taxAdjValue.set("amount", taxAmount); >>>>>> + >>>>>> + if ("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("orderAdjustmentTypeId", "SALES_TAX"); >>>>>> + 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")); >>>>>> @@ -421,62 +448,50 @@ public class TaxAuthorityServices { >>>>>> } >>>>>> >>>>>> adjustments.add(taxAdjValue); >>>>>> - >>>>>> - // 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 get this to work with price rules changing the >>>>>> default price (right now only works where itemPrice==defaultPrice >>>>>> - // 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 >>>>>> - >>>>>> - //Debug.logInfo("=================== itemQuantity=" + >>>>>> itemQuantity, module); >>>>>> - //Debug.logInfo("=================== taxAuthPartyId=" + >>>>>> taxAuthPartyId, module); >>>>>> - //Debug.logInfo("=================== taxAuthGeoId=" + >>>>>> taxAuthGeoId, module); >>>>>> - if (product != null && itemQuantity != 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, >>>>>> - "productPriceTypeId", "DEFAULT_PRICE", >>>>>> "productPricePurposeId", "PURCHASE"); >>>>>> - List<GenericValue> productPriceList = >>>>>> delegator.findByAnd("ProductPrice", priceFindMap, >>>>>> UtilMisc.toList("-fromDate")); >>>>>> - productPriceList = >>>>>> EntityUtil.filterByDate(productPriceList, true); >>>>>> - GenericValue productPrice = (productPriceList != >>>>>> null && productPriceList.size() > 0) ? productPriceList.get(0): null; >>>>>> - //Debug.logInfo("=================== productId=" + >>>>>> product.getString("productId"), module); >>>>>> - //Debug.logInfo("=================== productPrice=" >>>>>> + productPrice, module); >>>>>> + >>>>>> + 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 >>>>>> >>>>>> - if (productPrice != null && >>>>>> productPrice.getBigDecimal("priceWithTax") != null) { >>>>>> - 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); >>>>>> - BigDecimal enteredTotalPriceWithTax = >>>>>> priceWithTax.multiply(itemQuantity); >>>>>> - BigDecimal calcedTotalPriceWithTax = >>>>>> (baseSubtotal).add(baseTaxAmount); >>>>>> - //Debug.logInfo("=================== >>>>>> priceWithTax=" + priceWithTax, module); >>>>>> - //Debug.logInfo("=================== >>>>>> enteredTotalPriceWithTax=" + enteredTotalPriceWithTax, module); >>>>>> - //Debug.logInfo("=================== >>>>>> calcedTotalPriceWithTax=" + calcedTotalPriceWithTax, module); >>>>>> + 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); >>>>>> >>>>>> - 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); >>>>>> - } >>>>>> + 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); >>>>>> } >>>>>> } >>>>>> } >>>>>> >>>>>> Modified: ofbiz/trunk/applications/order/data/OrderTypeData.xml >>>>>> URL: >>>>>> http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/data/OrderTypeData.xml?rev=1042542&r1=1042541&r2=1042542&view=diff >>>>>> ============================================================================== >>>>>> --- ofbiz/trunk/applications/order/data/OrderTypeData.xml (original) >>>>>> +++ ofbiz/trunk/applications/order/data/OrderTypeData.xml Mon Dec 6 >>>>>> 08:05:44 2010 >>>>>> @@ -42,6 +42,7 @@ under the License. >>>>>> <OrderAdjustmentType description="Fee" hasTable="N" >>>>>> orderAdjustmentTypeId="FEE" parentTypeId=""/> >>>>>> <OrderAdjustmentType description="Miscellaneous Charges" hasTable="N" >>>>>> orderAdjustmentTypeId="MISCELLANEOUS_CHARGE" parentTypeId=""/> >>>>>> <OrderAdjustmentType description="Sales Tax" hasTable="N" >>>>>> orderAdjustmentTypeId="SALES_TAX" parentTypeId=""/> >>>>>> + <OrderAdjustmentType description="VAT Tax (not added to totals)" >>>>>> hasTable="N" orderAdjustmentTypeId="VAT_TAX" parentTypeId=""/> >>>>>> <OrderAdjustmentType description="VAT Price Correction" hasTable="N" >>>>>> orderAdjustmentTypeId="VAT_PRICE_CORRECT" parentTypeId=""/> >>>>>> <OrderAdjustmentType description="Shipping and Handling" hasTable="N" >>>>>> orderAdjustmentTypeId="SHIPPING_CHARGES" parentTypeId=""/> >>>>>> <OrderAdjustmentType description="Surcharge" hasTable="N" >>>>>> orderAdjustmentTypeId="SURCHARGE_ADJUSTMENT" parentTypeId=""/> >>>>>> >>>>>> Modified: ofbiz/trunk/applications/order/entitydef/entitymodel.xml >>>>>> URL: >>>>>> http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/entitydef/entitymodel.xml?rev=1042542&r1=1042541&r2=1042542&view=diff >>>>>> ============================================================================== >>>>>> --- ofbiz/trunk/applications/order/entitydef/entitymodel.xml (original) >>>>>> +++ ofbiz/trunk/applications/order/entitydef/entitymodel.xml Mon Dec 6 >>>>>> 08:05:44 2010 >>>>>> @@ -59,6 +59,7 @@ under the License. >>>>>> <field name="description" type="description"></field> >>>>>> <field name="amount" type="currency-precise"></field> >>>>>> <field name="recurringAmount" type="currency-precise"></field> >>>>>> + <field name="amountAlreadyIncluded" >>>>>> type="currency-precise"><description>The amount here is already >>>>>> represented in the price, such as VAT taxes.</description></field> >>>>>> <field name="productPromoId" type="id"></field> >>>>>> <field name="productPromoRuleId" type="id"></field> >>>>>> <field name="productPromoActionSeqId" type="id"></field> >>>>>> >>>>>> Modified: >>>>>> ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderReadHelper.java >>>>>> URL: >>>>>> http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderReadHelper.java?rev=1042542&r1=1042541&r2=1042542&view=diff >>>>>> ============================================================================== >>>>>> --- >>>>>> ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderReadHelper.java >>>>>> (original) >>>>>> +++ >>>>>> ofbiz/trunk/applications/order/src/org/ofbiz/order/order/OrderReadHelper.java >>>>>> Mon Dec 6 08:05:44 2010 >>>>>> @@ -2920,4 +2920,62 @@ public class OrderReadHelper { >>>>>> result.put("taxGrandTotal", taxGrandTotal); >>>>>> return result; >>>>>> } >>>>>> + >>>>>> + public static Map<String, Object> >>>>>> getOrderTaxByTaxAuthGeoAndPartyForDisplay(List<GenericValue> >>>>>> orderAdjustmentsOriginal) { >>>>>> + BigDecimal taxGrandTotal = BigDecimal.ZERO; >>>>>> + List<Map<String, Object>> taxByTaxAuthGeoAndPartyList = >>>>>> FastList.newInstance(); >>>>>> + List<GenericValue> orderAdjustmentsToUse = >>>>>> FastList.newInstance(); >>>>>> + if (UtilValidate.isNotEmpty(orderAdjustmentsOriginal)) { >>>>>> + // get orderAdjustment where orderAdjustmentTypeId is >>>>>> SALES_TAX. >>>>>> + >>>>>> orderAdjustmentsToUse.addAll(EntityUtil.filterByAnd(orderAdjustmentsOriginal, >>>>>> UtilMisc.toMap("orderAdjustmentTypeId", "SALES_TAX"))); >>>>>> + >>>>>> orderAdjustmentsToUse.addAll(EntityUtil.filterByAnd(orderAdjustmentsOriginal, >>>>>> UtilMisc.toMap("orderAdjustmentTypeId", "VAT_TAX"))); >>>>>> + orderAdjustmentsToUse = >>>>>> EntityUtil.orderBy(orderAdjustmentsToUse, >>>>>> UtilMisc.toList("taxAuthGeoId","taxAuthPartyId")); >>>>>> + >>>>>> + // get the list of all distinct taxAuthGeoId and >>>>>> taxAuthPartyId. It is for getting the number of taxAuthGeo and >>>>>> taxAuthPartyId in adjustments. >>>>>> + List<String> distinctTaxAuthGeoIdList = >>>>>> EntityUtil.getFieldListFromEntityList(orderAdjustmentsToUse, >>>>>> "taxAuthGeoId", true); >>>>>> + List<String> distinctTaxAuthPartyIdList = >>>>>> EntityUtil.getFieldListFromEntityList(orderAdjustmentsToUse, >>>>>> "taxAuthPartyId", true); >>>>>> + >>>>>> + // Keep a list of amount that have been added to make sure >>>>>> none are missed (if taxAuth* information is missing) >>>>>> + List<GenericValue> processedAdjustments = >>>>>> FastList.newInstance(); >>>>>> + // For each taxAuthGeoId get and add amount from >>>>>> orderAdjustment >>>>>> + for (String taxAuthGeoId : distinctTaxAuthGeoIdList) { >>>>>> + for (String taxAuthPartyId : distinctTaxAuthPartyIdList) >>>>>> { >>>>>> + //get all records for orderAdjustments filtered by >>>>>> taxAuthGeoId and taxAurhPartyId >>>>>> + List<GenericValue> orderAdjByTaxAuthGeoAndPartyIds = >>>>>> EntityUtil.filterByAnd(orderAdjustmentsToUse, >>>>>> UtilMisc.toMap("taxAuthGeoId", taxAuthGeoId, "taxAuthPartyId", >>>>>> taxAuthPartyId)); >>>>>> + if >>>>>> (UtilValidate.isNotEmpty(orderAdjByTaxAuthGeoAndPartyIds)) { >>>>>> + BigDecimal totalAmount = BigDecimal.ZERO; >>>>>> + //Now for each orderAdjustment record get and >>>>>> add amount. >>>>>> + for (GenericValue orderAdjustment : >>>>>> orderAdjByTaxAuthGeoAndPartyIds) { >>>>>> + BigDecimal amount = >>>>>> orderAdjustment.getBigDecimal("amount"); >>>>>> + if (amount != null) { >>>>>> + totalAmount = totalAmount.add(amount); >>>>>> + } >>>>>> + if >>>>>> ("VAT_TAX".equals(orderAdjustment.getString("orderAdjustmentTypeId")) && >>>>>> + >>>>>> orderAdjustment.get("amountAlreadyIncluded") != null) { >>>>>> + // this is the only case where the >>>>>> VAT_TAX amountAlreadyIncluded should be added in, and should just be for >>>>>> display and not to calculate the order grandTotal >>>>>> + totalAmount = >>>>>> totalAmount.add(orderAdjustment.getBigDecimal("amountAlreadyIncluded")); >>>>>> + } >>>>>> + totalAmount = >>>>>> totalAmount.setScale(taxCalcScale, taxRounding); >>>>>> + processedAdjustments.add(orderAdjustment); >>>>>> + } >>>>>> + totalAmount = >>>>>> totalAmount.setScale(taxFinalScale, taxRounding); >>>>>> + >>>>>> taxByTaxAuthGeoAndPartyList.add(UtilMisc.<String, >>>>>> Object>toMap("taxAuthPartyId", taxAuthPartyId, "taxAuthGeoId", >>>>>> taxAuthGeoId, "totalAmount", totalAmount)); >>>>>> + taxGrandTotal = taxGrandTotal.add(totalAmount); >>>>>> + } >>>>>> + } >>>>>> + } >>>>>> + // Process any adjustments that got missed >>>>>> + List<GenericValue> missedAdjustments = >>>>>> FastList.newInstance(); >>>>>> + missedAdjustments.addAll(orderAdjustmentsToUse); >>>>>> + missedAdjustments.removeAll(processedAdjustments); >>>>>> + for (GenericValue orderAdjustment : missedAdjustments) { >>>>>> + taxGrandTotal = >>>>>> taxGrandTotal.add(orderAdjustment.getBigDecimal("amount").setScale(taxCalcScale, >>>>>> taxRounding)); >>>>>> + } >>>>>> + taxGrandTotal = taxGrandTotal.setScale(taxFinalScale, >>>>>> taxRounding); >>>>>> + } >>>>>> + Map<String, Object> result = FastMap.newInstance(); >>>>>> + result.put("taxByTaxAuthGeoAndPartyList", >>>>>> taxByTaxAuthGeoAndPartyList); >>>>>> + result.put("taxGrandTotal", taxGrandTotal); >>>>>> + return result; >>>>>> + } >>>>>> } >>>>>> >>>>>> Modified: ofbiz/trunk/applications/product/entitydef/entitymodel.xml >>>>>> URL: >>>>>> http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/entitydef/entitymodel.xml?rev=1042542&r1=1042541&r2=1042542&view=diff >>>>>> ============================================================================== >>>>>> --- ofbiz/trunk/applications/product/entitydef/entitymodel.xml (original) >>>>>> +++ ofbiz/trunk/applications/product/entitydef/entitymodel.xml Mon Dec >>>>>> 6 08:05:44 2010 >>>>>> @@ -2375,11 +2375,13 @@ under the License. >>>>>> <field name="price" type="currency-precise"></field> >>>>>> <field name="termUomId" type="id"><description>Mainly used for >>>>>> recurring and usage prices to specify a time/freq measure, or a usage >>>>>> unit measure (bits, minutes, etc)</description></field> >>>>>> <field name="customPriceCalcService" type="id"><description>Points to >>>>>> a CustomMethod used to specify a service for the calculation of the unit >>>>>> price of the product (NOTE: a better name for this field might be >>>>>> priceCalcCustomMethodId)</description></field> >>>>>> - <field name="priceWithTax" type="currency-precise"/> >>>>>> + <field name="priceWithoutTax" >>>>>> type="currency-precise"><description>Always without tax if populated, >>>>>> regardless of if price does or does not include >>>>>> tax.</description></field> >>>>>> + <field name="priceWithTax" >>>>>> type="currency-precise"><description>Always with tax if populated, >>>>>> regardless of if price does or does not include >>>>>> tax.</description></field> >>>>>> <field name="taxAmount" type="currency-precise"/> >>>>>> <field name="taxPercentage" type="fixed-point"/> >>>>>> <field name="taxAuthPartyId" type="id-ne"/> >>>>>> <field name="taxAuthGeoId" type="id-ne"/> >>>>>> + <field name="taxInPrice" type="indicator"><description>If Y the >>>>>> price field has tax included for the given taxAuthPartyId/taxAuthGeoId >>>>>> at the taxPercentage.</description></field> >>>>>> <field name="createdDate" type="date-time"></field> >>>>>> <field name="createdByUserLogin" type="id-vlong"></field> >>>>>> <field name="lastModifiedDate" type="date-time"></field> >>>>>> >>>>>> Modified: >>>>>> ofbiz/trunk/applications/product/script/org/ofbiz/product/price/PriceServices.xml >>>>>> URL: >>>>>> http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/script/org/ofbiz/product/price/PriceServices.xml?rev=1042542&r1=1042541&r2=1042542&view=diff >>>>>> ============================================================================== >>>>>> --- >>>>>> ofbiz/trunk/applications/product/script/org/ofbiz/product/price/PriceServices.xml >>>>>> (original) >>>>>> +++ >>>>>> ofbiz/trunk/applications/product/script/org/ofbiz/product/price/PriceServices.xml >>>>>> Mon Dec 6 08:05:44 2010 >>>>>> @@ -106,6 +106,7 @@ under the License. >>>>>> </condition> >>>>>> <then> >>>>>> <set field="parameters.priceWithTax" >>>>>> from-field="parameters.price"/> >>>>>> + >>>>>> <!-- if taxPercentage not passed in look it up based on >>>>>> taxAuthGeoId and taxAuthPartyId --> >>>>>> <if-empty field="parameters.taxPercentage"> >>>>>> <!-- we only have basic data to constrain by here, so >>>>>> assume that if it is a VAT tax setup it should be pretty simple --> >>>>>> @@ -127,6 +128,7 @@ under the License. >>>>>> <check-errors/> >>>>>> </if-empty> >>>>>> >>>>>> + <!-- in short the formula is: taxAmount = priceWithTax >>>>>> - (priceWithTax/(1+taxPercentage/100)) --> >>>>>> <calculate field="parameters.taxAmount" type="BigDecimal" >>>>>> decimal-scale="3" rounding-mode="HalfUp"> >>>>>> <calcop operator="subtract"> >>>>>> <calcop operator="get" >>>>>> field="parameters.priceWithTax"/> >>>>>> @@ -142,12 +144,23 @@ under the License. >>>>>> </calcop> >>>>>> </calcop> >>>>>> </calculate> >>>>>> - <calculate field="parameters.price" type="BigDecimal" >>>>>> decimal-scale="3" rounding-mode="HalfUp"> >>>>>> + >>>>>> + <calculate field="parameters.priceWithoutTax" >>>>>> type="BigDecimal" decimal-scale="3" rounding-mode="HalfUp"> >>>>>> <calcop operator="subtract"> >>>>>> <calcop operator="get" >>>>>> field="parameters.priceWithTax"/> >>>>>> <calcop operator="get" >>>>>> field="parameters.taxAmount"></calcop> >>>>>> </calcop> >>>>>> </calculate> >>>>>> + >>>>>> + <if-compare field="parameters.taxInPrice" >>>>>> operator="equals" value="Y"> >>>>>> + <!-- the price passed in has tax included, and we >>>>>> want to store it with tax included --> >>>>>> + <set field="parameters.price" >>>>>> from-field="parameters.priceWithTax"/> >>>>>> + >>>>>> + <else> >>>>>> + <!-- the price passed in has tax included, but >>>>>> we want to store it without tax included --> >>>>>> + <set field="parameters.price" >>>>>> from-field="parameters.priceWithoutTax"/> >>>>>> + </else> >>>>>> + </if-compare> >>>>>> </then> >>>>>> </if> >>>>>> </simple-method> >>>>>> >>>>>> Modified: ofbiz/trunk/applications/product/servicedef/services.xml >>>>>> URL: >>>>>> http://svn.apache.org/viewvc/ofbiz/trunk/applications/product/servicedef/services.xml?rev=1042542&r1=1042541&r2=1042542&view=diff >>>>>> ============================================================================== >>>>>> --- ofbiz/trunk/applications/product/servicedef/services.xml (original) >>>>>> +++ ofbiz/trunk/applications/product/servicedef/services.xml Mon Dec 6 >>>>>> 08:05:44 2010 >>>>>> @@ -242,12 +242,15 @@ under the License. >>>>>> <description> >>>>>> Create an ProductPrice. >>>>>> Price is always stored without tax. >>>>>> - If a taxAuthGeoId and taxAuthPartyId are (or >>>>>> taxAuthCombinedId is) passed in then the price will be considered a price >>>>>> - with tax included and the tax will be removed before >>>>>> storing to the database >>>>>> - (the priceWithTax, taxAmount, and taxPercentage fields >>>>>> will also be populated). >>>>>> + If taxAuthGeoId and taxAuthPartyId are (or >>>>>> taxAuthCombinedId is) passed in then the price will be considered a price >>>>>> + with tax included (the priceWithoutTax, priceWithTax, >>>>>> taxAmount, and taxPercentage fields will also be populated). >>>>>> + If the taxInPrice field is 'Y' then the price field will be >>>>>> left with the price included (price will be equal to priceWithTax), >>>>>> + otherwise tax will be removed from the passed in price and >>>>>> the price field will be equal to the priceWithoutTax field. >>>>>> + If taxAuthGeoId or taxAuthPartyId empty, and >>>>>> taxAuthCombinedId is empty, then the taxInPrice field will be ignored. >>>>>> </description> >>>>>> <auto-attributes include="pk" mode="IN" optional="false"/> >>>>>> <auto-attributes include="nonpk" mode="IN" optional="true"> >>>>>> + <exclude field-name="priceWithoutTax"/> >>>>>> <exclude field-name="priceWithTax"/> >>>>>> <exclude field-name="taxAmount"/> >>>>>> <exclude field-name="createdDate"/> >>>>>> @@ -264,6 +267,7 @@ under the License. >>>>>> <description>Update an ProductPrice</description> >>>>>> <auto-attributes include="pk" mode="IN" optional="false"/> >>>>>> <auto-attributes include="nonpk" mode="IN" optional="true"> >>>>>> + <exclude field-name="priceWithoutTax"/> >>>>>> <exclude field-name="priceWithTax"/> >>>>>> <exclude field-name="taxAmount"/> >>>>>> <exclude field-name="createdDate"/> >>>>>> >>>>> >>>>> >>> >>> > >
