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? > 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. -David > From: "David E Jones" <d...@me.com> >> 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: <jone...@apache.org> >>>> 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"/> >>>> >>> >>> > >