You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ofbiz.apache.org by Jacques Le Roux <ja...@les7arts.com> on 2016/05/01 19:47:57 UTC

Re: Triangular VAT in Europe ?

Le 27/01/2016 � 16:37, Nicolas Malin a �crit :
> Hello, In Europe with the B2B drop shipment process we have a specific rule to calculate the VAT. The particularity comes from the purchase order, 
> applying the VAT of the product origin country if billing address OR shipping address is in the same country. 1. I'm a French society that ordered 
> product from Italy to sale in Denmark -> No VAT 2. I'm a French society that ordered product from Italy to sale in Italy -> Italian VAT 3. I'm a 
> French society that ordered product from France to sale in Italy -> French VAT Currently to resolve the taxAuth in OFBiz we use the shipping address 
> only, so we can see that the point 3. isn't covered because the product wasn't shipped in France. Does anyone ever met the same problem ? I propose 
> to improve the taxAuth resolution with also the origin address and the billing address. After that, I have the problem to say when we need to check 
> the origin instead of the shipping. Hmmmm ... my first idea would be to check if the payToPartyId is an internal organisation. If yes, resolve 
> taxAuth with the shipping else, I check if the origin is the same country than the shipping or billing. After TaxAuth the rate resolved come without 
> change. However, I suggest to add a customMethodId on taxAuthRateProduct to increase the configuration of complex cases.
>
> Any suggest ?
>
> -- 
> #jeSuisCharlie
> logoNrd <http://nereide.fr/>
> 	Nicolas Malin
> Ing�nieur d'�tude. Dernier sujet : "Les vaches portant un pr�nom pouvent trouver la sortie d'un labyrinthe en cas de toxoplasmose
> information@nereide.fr
> 8 rue des D�port�s 37000 TOURS, 02 47 50 30 54
>
> Apache OFBiz <http://ofbiz.apache.org/> | ofbiz-fr <http://www.ofbiz-fr.org/> | | r�seau LE <http://www.libre-entreprise.org/>
Hi Nicolas,

Did you finally implement this? If yes would you contribute (just curious)?

Thanks

Jacques


Re: Triangular VAT in Europe ?

Posted by Nicolas Malin <ni...@nereide.fr>.
Le 27/05/2016 07:35, Jacques Le Roux a �crit :
>
> Thanks Nicolas,
>
> Could you please open a Jira improvement with your comments below?
>
Ok done Jacques,

https://issues.apache.org/jira/browse/OFBIZ-7138

All suggest are welcome because my solution came from my brain ... warn !!

Nicolas
>
> Thanks
>
> Jacques
>
>
> Le 26/05/2016 � 22:25, Nicolas Malin a �crit :
>> Hi Jacques,
>>
>>
>> Le 01/05/2016 19:47, Jacques Le Roux a �crit :
>>> Le 27/01/2016 � 16:37, Nicolas Malin a �crit :
>>>> Hello, In Europe with the B2B drop shipment process we have a 
>>>> specific rule to calculate the VAT. The particularity comes from 
>>>> the purchase order, applying the VAT of the product origin country 
>>>> if billing address OR shipping address is in the same country. 1. 
>>>> I'm a French society that ordered product from Italy to sale in 
>>>> Denmark -> No VAT 2. I'm a French society that ordered product from 
>>>> Italy to sale in Italy -> Italian VAT 3. I'm a French society that 
>>>> ordered product from France to sale in Italy -> French VAT 
>>>> Currently to resolve the taxAuth in OFBiz we use the shipping 
>>>> address only, so we can see that the point 3. isn't covered because 
>>>> the product wasn't shipped in France. Does anyone ever met the same 
>>>> problem ? I propose to improve the taxAuth resolution with also the 
>>>> origin address and the billing address. After that, I have the 
>>>> problem to say when we need to check the origin instead of the 
>>>> shipping. Hmmmm ... my first idea would be to check if the 
>>>> payToPartyId is an internal organisation. If yes, resolve taxAuth 
>>>> with the shipping else, I check if the origin is the same country 
>>>> than the shipping or billing. After TaxAuth the rate resolved come 
>>>> without change. However, I suggest to add a customMethodId on 
>>>> taxAuthRateProduct to increase the configuration of complex cases.
>>>>
>>>> Any suggest ?
>>>>
>>>> -- 
>>>> #jeSuisCharlie
>>>> logoNrd <http://nereide.fr/>
>>>> 	Nicolas Malin
>>>> Ing�nieur d'�tude. Dernier sujet : "Les vaches portant un pr�nom 
>>>> pouvent trouver la sortie d'un labyrinthe en cas de toxoplasmose
>>>> information@nereide.fr
>>>> 8 rue des D�port�s 37000 TOURS, 02 47 50 30 54
>>>>
>>>> Apache OFBiz <http://ofbiz.apache.org/> | ofbiz-fr 
>>>> <http://www.ofbiz-fr.org/> | | r�seau LE 
>>>> <http://www.libre-entreprise.org/>
>>> Hi Nicolas,
>>>
>>> Did you finally implement this? If yes would you contribute (just 
>>> curious)?
>> I sharing with pleasure, because is now in production ;)
>>
>> To manage this I extend the function 
>> TaxAuthorityServices.getTaxAuthorities with this  (I implement the 
>> solution on 13.07):
>> ****************************************
>> EntityCondition cond = EntityCondition.makeCondition(UtilMisc.toList(
>>                 EntityCondition.makeCondition("partyId", payToPartyId),
>>                 EntityCondition.makeCondition("isNexus", "Y")));
>>         List<GenericValue> taxAuthorityRawList = 
>> delegator.findList("PartyTaxAuthInfo", cond, null, null, null, true);
>>         List<EntityCondition> taxCondList = new 
>> ArrayList<EntityCondition>();
>>         if (!taxAuthorityRawList.isEmpty()) {
>> taxCondList.add(EntityCondition.makeCondition("taxAuthPartyId", 
>> EntityOperator.IN, 
>> EntityUtil.getFieldListFromEntityList(taxAuthorityRawList, 
>> "taxAuthPartyId", true)));
>>             if (Debug.infoOn()) Debug.logInfo("Search tax authority 
>> relation for " + payToPartyId + " " + taxCondList, module);
>>             if (originAddress == null) {
>>                 originAddress = 
>> ContactMechWorker.getTaxOriginAddress(delegator, payToPartyId);
>>             }
>>             if (billingAddress == null) {
>>                 billingAddress = 
>> ContactMechWorker.getTaxBillingAddress(delegator, billToPartyId);
>>             }
>>             if (originAddress != null && billingAddress != null) {
>>                 if 
>> (originAddress.getString("countryGeoId").equals(billingAddress.getString("countryGeoId"))) 
>> {
>>                     //ok we will analyse the country where come from 
>> the flow
>>                     address = originAddress;
>>                 }
>>             }
>>             if (Debug.infoOn()) {
>>                 Debug.logInfo("          shipping found " + 
>> shippingAddress.getString("countryGeoId"), module);
>>                 Debug.logInfo("          origin found " + 
>> originAddress.getString("countryGeoId"), module);
>>                 Debug.logInfo("          billing found " + 
>> billingAddress.getString("countryGeoId"), module);
>>                 Debug.logInfo("          country found " + 
>> address.getString("countryGeoId"), module);
>>             }
>>         } else {
>>             if (Debug.infoOn()) Debug.logInfo("No specific relation, 
>> run the std resolution", module);
>>         }
>> ******************************************
>>
>> The idea, when an order check the vat, I resolv the taxAuth to use 
>> first with the payToParty. I check if this party have a dedicate 
>> relation with a specific tax authority by PartyTaxAuthInfo. If yes, I 
>> check with the shipping and the billing adress to understand if this 
>> order is cover by the same country.
>>
>> If no I continue with the standard method.
>>
>> This is a simple hack, because a better solution would be use the 
>> orderContachMech to resolve the shipping, billing and origin adress, 
>> but this works fine like that :) .
>>
>> The rest is only data configuration on the PartyAuth and bill from 
>> vendor.
>>
>>
>> After to implement a specific text on invoice template and resolve 
>> the reason of an exoneration, I use a seca like this :
>>
>>     <eca service="setInvoiceStatus" event="invoke">
>>          <condition operator="equals" field-name="statusId" 
>> value="INVOICE_READY"/>
>>          <action service="checkInvoiceForVATExemptReason" mode="sync" 
>> ignore-error="false"/>
>>     </eca>
>>
>> The service checkInvoiceForVATExemptReason, check if the invoice have 
>> vat line and if not call this :
>>
>> ****************
>>  GenericValue partyAuth = EntityUtil.getFirst(partyAuths);
>>         EntityCondition condTax = 
>> EntityCondition.makeCondition(UtilMisc.toList(
>>                 EntityExpr.makeCondition("taxAuthPartyId", 
>> partyAuth.get("taxAuthPartyId")),
>>                 EntityExpr.makeCondition("taxAuthGeoId", 
>> partyAuth.get("taxAuthGeoId")),
>>                 EntityExpr.makeCondition("taxPercentage", 
>> GenericEntity.NULL_FIELD),
>> EntityExpr.makeCondition("taxAmount",GenericEntity.NULL_FIELD),
>> EntityCondition.makeCondition("taxAuthorityRateTypeId", 
>> EntityOperator.NOT_EQUAL, "SALES_TAX")));
>>         List<GenericValue> taxRates = 
>> delegator.findList("TaxAuthorityRateProduct", condTax, null, 
>> UtilMisc.toList("sequenceNum"), null, true);
>>         if (Debug.infoOn()) Debug.logInfo(" ### taxRates " + 
>> taxRates.size(), module);
>>         taxRates = EntityUtil.filterByDate(taxRates, 
>> invoice.getTimestamp("invoiceDate"));
>>         if (UtilValidate.isEmpty(taxRates)) {
>>             if (Debug.infoOn()) Debug.logInfo(" no specific rules 
>> find", module);
>>             return result;
>>         }
>>         //check match case rate
>>         for (GenericValue taxRate : taxRates) {
>>             GenericValue custMethod = null;
>>             String serviceName = null;
>>             if 
>> (UtilValidate.isNotEmpty(taxRate.getString("customMethodId"))) {
>>                 custMethod = delegator.findOne("CustomMethod", true, 
>> "customMethodId", taxRate.get("customMethodId"));
>>             }
>>             if (custMethod != null) serviceName = 
>> custMethod.getString("customMethodName");
>>             if (UtilValidate.isNotEmpty(serviceName)) {
>>                 ModelService service = dctx.getModelService(serviceName);
>>                 if (service != null) {
>>                     if (Debug.infoOn()) Debug.logInfo(" call service 
>> " + serviceName + "related to taxRate : " + 
>> taxRate.get("taxAuthorityRateSeqId"), module);
>>                     Map<String,Object> serviceCtx = 
>> service.makeValid(context, ModelService.IN_PARAM);
>>                     serviceCtx.put("taxAuthorityRateSeqId", 
>> taxRate.get("taxAuthorityRateSeqId"));
>>                     Map<String,Object> serviceResult = 
>> dctx.getDispatcher().runSync(serviceName, serviceCtx);
>>                     if (ServiceUtil.isError(serviceResult)) {
>>                         throw new 
>> GeneralException(ServiceUtil.getErrorMessage(serviceResult));
>>                     }
>>                     if ("Y".equalsIgnoreCase((String) 
>> serviceResult.get("taxExempt"))) {
>>                         Map<String, Object> invoiceItemMap = 
>> dctx.makeValidContext("createInvoiceItem", "IN", context);
>> invoiceItemMap.put("taxAuthorityRateSeqId", 
>> taxRate.get("taxAuthorityRateSeqId"));
>>                         invoiceItemMap.put("taxAuthPartyId", 
>> taxRate.get("taxAuthPartyId"));
>>                         invoiceItemMap.put("taxAuthGeoId", 
>> taxRate.get("taxAuthGeoId"));
>>                         invoiceItemMap.put("invoiceItemTypeId", 
>> "ITM_SALES_TAX");
>>                         invoiceItemMap.put("quantity", 0);
>>                         invoiceItemMap.put("amount", 0);
>>                         if (Debug.infoOn()) Debug.logInfo( "Nice is 
>> an exempt reason, store it on invoice.", module);
>>                         serviceResult = 
>> dctx.getDispatcher().runSync("createInvoiceItem", invoiceItemMap);
>>                         if (ServiceUtil.isError(serviceResult)) {
>>                             throw new 
>> GeneralException(ServiceUtil.getErrorMessage(serviceResult));
>>                         }
>>                         break;
>>                     }
>>                 }
>>             }
>>         }
>> **********************
>> * You check the related TaxAuth with empty rate (I used Export type)
>> * For each find, you call a customMethod to understand why you don't 
>> have VAT
>>
>> Example :
>>     private static boolean isEuropeanIntracomCountry(Delegator 
>> delegator, String countryGeoId) throws GenericEntityException {
>>         if (countryGeoId != null) {
>>             return delegator.findOne("GeoAssoc", true, "geoIdTo", 
>> "EU", "geoId", countryGeoId) != null;
>>         }
>>         return false;
>> Or
>>     public static Map<String, Object> 
>> checkEuropeanVatExempt(DispatchContext dctx, Map<String, Object> 
>> context)
>>     throws GeneralException {
>>         Delegator delegator = dctx.getDelegator();
>>         Map<String, Object> result = ServiceUtil.returnSuccess();
>>         String invoiceId = (String) context.get("invoiceId");
>>         result.put("taxExempt", "N");
>>
>>         String deliveryCountryGeoId = 
>> resolvDeliveryCountryGeoId(delegator, invoiceId);
>>         //Si pas TVA triangulaire dans l'UE alors TVA export Intra com
>>         if (isEuropeanIntracomCountry(delegator, deliveryCountryGeoId)) {
>>             result.put("taxExempt", "Y");
>>         }
>>         return result;
>>
>> My apologies for the big raw email and congrat to read up to here ! :)
>> Nicolas
>>>
>>> Thanks
>>> Jacques
>>
>


Re: Triangular VAT in Europe ?

Posted by Jacques Le Roux <ja...@les7arts.com>.
Thanks Nicolas,

Could you please open a Jira improvement with your comments below?

Thanks

Jacques


Le 26/05/2016 � 22:25, Nicolas Malin a �crit :
> Hi Jacques,
>
>
> Le 01/05/2016 19:47, Jacques Le Roux a �crit :
>> Le 27/01/2016 � 16:37, Nicolas Malin a �crit :
>>> Hello, In Europe with the B2B drop shipment process we have a specific rule to calculate the VAT. The particularity comes from the purchase order, 
>>> applying the VAT of the product origin country if billing address OR shipping address is in the same country. 1. I'm a French society that ordered 
>>> product from Italy to sale in Denmark -> No VAT 2. I'm a French society that ordered product from Italy to sale in Italy -> Italian VAT 3. I'm a 
>>> French society that ordered product from France to sale in Italy -> French VAT Currently to resolve the taxAuth in OFBiz we use the shipping 
>>> address only, so we can see that the point 3. isn't covered because the product wasn't shipped in France. Does anyone ever met the same problem ? 
>>> I propose to improve the taxAuth resolution with also the origin address and the billing address. After that, I have the problem to say when we 
>>> need to check the origin instead of the shipping. Hmmmm ... my first idea would be to check if the payToPartyId is an internal organisation. If 
>>> yes, resolve taxAuth with the shipping else, I check if the origin is the same country than the shipping or billing. After TaxAuth the rate 
>>> resolved come without change. However, I suggest to add a customMethodId on taxAuthRateProduct to increase the configuration of complex cases.
>>>
>>> Any suggest ?
>>>
>>> -- 
>>> #jeSuisCharlie
>>> logoNrd <http://nereide.fr/>
>>> 	Nicolas Malin
>>> Ing�nieur d'�tude. Dernier sujet : "Les vaches portant un pr�nom pouvent trouver la sortie d'un labyrinthe en cas de toxoplasmose
>>> information@nereide.fr
>>> 8 rue des D�port�s 37000 TOURS, 02 47 50 30 54
>>>
>>> Apache OFBiz <http://ofbiz.apache.org/> | ofbiz-fr <http://www.ofbiz-fr.org/> | | r�seau LE <http://www.libre-entreprise.org/>
>> Hi Nicolas,
>>
>> Did you finally implement this? If yes would you contribute (just curious)?
> I sharing with pleasure, because is now in production ;)
>
> To manage this I extend the function TaxAuthorityServices.getTaxAuthorities with this  (I implement the solution on 13.07):
> ****************************************
> EntityCondition cond = EntityCondition.makeCondition(UtilMisc.toList(
>                 EntityCondition.makeCondition("partyId", payToPartyId),
>                 EntityCondition.makeCondition("isNexus", "Y")));
>         List<GenericValue> taxAuthorityRawList = delegator.findList("PartyTaxAuthInfo", cond, null, null, null, true);
>         List<EntityCondition> taxCondList = new ArrayList<EntityCondition>();
>         if (!taxAuthorityRawList.isEmpty()) {
> taxCondList.add(EntityCondition.makeCondition("taxAuthPartyId", EntityOperator.IN, EntityUtil.getFieldListFromEntityList(taxAuthorityRawList, 
> "taxAuthPartyId", true)));
>             if (Debug.infoOn()) Debug.logInfo("Search tax authority relation for " + payToPartyId + " " + taxCondList, module);
>             if (originAddress == null) {
>                 originAddress = ContactMechWorker.getTaxOriginAddress(delegator, payToPartyId);
>             }
>             if (billingAddress == null) {
>                 billingAddress = ContactMechWorker.getTaxBillingAddress(delegator, billToPartyId);
>             }
>             if (originAddress != null && billingAddress != null) {
>                 if (originAddress.getString("countryGeoId").equals(billingAddress.getString("countryGeoId"))) {
>                     //ok we will analyse the country where come from the flow
>                     address = originAddress;
>                 }
>             }
>             if (Debug.infoOn()) {
>                 Debug.logInfo("          shipping found " + shippingAddress.getString("countryGeoId"), module);
>                 Debug.logInfo("          origin found " + originAddress.getString("countryGeoId"), module);
>                 Debug.logInfo("          billing found " + billingAddress.getString("countryGeoId"), module);
>                 Debug.logInfo("          country found " + address.getString("countryGeoId"), module);
>             }
>         } else {
>             if (Debug.infoOn()) Debug.logInfo("No specific relation, run the std resolution", module);
>         }
> ******************************************
>
> The idea, when an order check the vat, I resolv the taxAuth to use first with the payToParty. I check if this party have a dedicate relation with a 
> specific tax authority by PartyTaxAuthInfo. If yes, I check with the shipping and the billing adress to understand if this order is cover by the 
> same country.
>
> If no I continue with the standard method.
>
> This is a simple hack, because a better solution would be use the orderContachMech to resolve the shipping, billing and origin adress, but this 
> works fine like that :) .
>
> The rest is only data configuration on the PartyAuth and bill from vendor.
>
>
> After to implement a specific text on invoice template and resolve the reason of an exoneration, I use a seca like this :
>
>     <eca service="setInvoiceStatus" event="invoke">
>          <condition operator="equals" field-name="statusId" value="INVOICE_READY"/>
>          <action service="checkInvoiceForVATExemptReason" mode="sync" ignore-error="false"/>
>     </eca>
>
> The service checkInvoiceForVATExemptReason, check if the invoice have vat line and if not call this :
>
> ****************
>  GenericValue partyAuth = EntityUtil.getFirst(partyAuths);
>         EntityCondition condTax = EntityCondition.makeCondition(UtilMisc.toList(
>                 EntityExpr.makeCondition("taxAuthPartyId", partyAuth.get("taxAuthPartyId")),
>                 EntityExpr.makeCondition("taxAuthGeoId", partyAuth.get("taxAuthGeoId")),
>                 EntityExpr.makeCondition("taxPercentage", GenericEntity.NULL_FIELD),
> EntityExpr.makeCondition("taxAmount",GenericEntity.NULL_FIELD),
> EntityCondition.makeCondition("taxAuthorityRateTypeId", EntityOperator.NOT_EQUAL, "SALES_TAX")));
>         List<GenericValue> taxRates = delegator.findList("TaxAuthorityRateProduct", condTax, null, UtilMisc.toList("sequenceNum"), null, true);
>         if (Debug.infoOn()) Debug.logInfo(" ### taxRates " + taxRates.size(), module);
>         taxRates = EntityUtil.filterByDate(taxRates, invoice.getTimestamp("invoiceDate"));
>         if (UtilValidate.isEmpty(taxRates)) {
>             if (Debug.infoOn()) Debug.logInfo(" no specific rules find", module);
>             return result;
>         }
>         //check match case rate
>         for (GenericValue taxRate : taxRates) {
>             GenericValue custMethod = null;
>             String serviceName = null;
>             if (UtilValidate.isNotEmpty(taxRate.getString("customMethodId"))) {
>                 custMethod = delegator.findOne("CustomMethod", true, "customMethodId", taxRate.get("customMethodId"));
>             }
>             if (custMethod != null) serviceName = custMethod.getString("customMethodName");
>             if (UtilValidate.isNotEmpty(serviceName)) {
>                 ModelService service = dctx.getModelService(serviceName);
>                 if (service != null) {
>                     if (Debug.infoOn()) Debug.logInfo(" call service " + serviceName + "related to taxRate : " + 
> taxRate.get("taxAuthorityRateSeqId"), module);
>                     Map<String,Object> serviceCtx = service.makeValid(context, ModelService.IN_PARAM);
>                     serviceCtx.put("taxAuthorityRateSeqId", taxRate.get("taxAuthorityRateSeqId"));
>                     Map<String,Object> serviceResult = dctx.getDispatcher().runSync(serviceName, serviceCtx);
>                     if (ServiceUtil.isError(serviceResult)) {
>                         throw new GeneralException(ServiceUtil.getErrorMessage(serviceResult));
>                     }
>                     if ("Y".equalsIgnoreCase((String) serviceResult.get("taxExempt"))) {
>                         Map<String, Object> invoiceItemMap = dctx.makeValidContext("createInvoiceItem", "IN", context);
> invoiceItemMap.put("taxAuthorityRateSeqId", taxRate.get("taxAuthorityRateSeqId"));
>                         invoiceItemMap.put("taxAuthPartyId", taxRate.get("taxAuthPartyId"));
>                         invoiceItemMap.put("taxAuthGeoId", taxRate.get("taxAuthGeoId"));
>                         invoiceItemMap.put("invoiceItemTypeId", "ITM_SALES_TAX");
>                         invoiceItemMap.put("quantity", 0);
>                         invoiceItemMap.put("amount", 0);
>                         if (Debug.infoOn()) Debug.logInfo( "Nice is an exempt reason, store it on invoice.", module);
>                         serviceResult = dctx.getDispatcher().runSync("createInvoiceItem", invoiceItemMap);
>                         if (ServiceUtil.isError(serviceResult)) {
>                             throw new GeneralException(ServiceUtil.getErrorMessage(serviceResult));
>                         }
>                         break;
>                     }
>                 }
>             }
>         }
> **********************
> * You check the related TaxAuth with empty rate (I used Export type)
> * For each find, you call a customMethod to understand why you don't have VAT
>
> Example :
>     private static boolean isEuropeanIntracomCountry(Delegator delegator, String countryGeoId) throws GenericEntityException {
>         if (countryGeoId != null) {
>             return delegator.findOne("GeoAssoc", true, "geoIdTo", "EU", "geoId", countryGeoId) != null;
>         }
>         return false;
> Or
>     public static Map<String, Object> checkEuropeanVatExempt(DispatchContext dctx, Map<String, Object> context)
>     throws GeneralException {
>         Delegator delegator = dctx.getDelegator();
>         Map<String, Object> result = ServiceUtil.returnSuccess();
>         String invoiceId = (String) context.get("invoiceId");
>         result.put("taxExempt", "N");
>
>         String deliveryCountryGeoId = resolvDeliveryCountryGeoId(delegator, invoiceId);
>         //Si pas TVA triangulaire dans l'UE alors TVA export Intra com
>         if (isEuropeanIntracomCountry(delegator, deliveryCountryGeoId)) {
>             result.put("taxExempt", "Y");
>         }
>         return result;
>
> My apologies for the big raw email and congrat to read up to here ! :)
> Nicolas
>>
>> Thanks
>> Jacques
>


Re: Triangular VAT in Europe ?

Posted by Nicolas Malin <ni...@nereide.fr>.
Hi Jacques,


Le 01/05/2016 19:47, Jacques Le Roux a �crit :
> Le 27/01/2016 � 16:37, Nicolas Malin a �crit :
>> Hello, In Europe with the B2B drop shipment process we have a 
>> specific rule to calculate the VAT. The particularity comes from the 
>> purchase order, applying the VAT of the product origin country if 
>> billing address OR shipping address is in the same country. 1. I'm a 
>> French society that ordered product from Italy to sale in Denmark -> 
>> No VAT 2. I'm a French society that ordered product from Italy to 
>> sale in Italy -> Italian VAT 3. I'm a French society that ordered 
>> product from France to sale in Italy -> French VAT Currently to 
>> resolve the taxAuth in OFBiz we use the shipping address only, so we 
>> can see that the point 3. isn't covered because the product wasn't 
>> shipped in France. Does anyone ever met the same problem ? I propose 
>> to improve the taxAuth resolution with also the origin address and 
>> the billing address. After that, I have the problem to say when we 
>> need to check the origin instead of the shipping. Hmmmm ... my first 
>> idea would be to check if the payToPartyId is an internal 
>> organisation. If yes, resolve taxAuth with the shipping else, I check 
>> if the origin is the same country than the shipping or billing. After 
>> TaxAuth the rate resolved come without change. However, I suggest to 
>> add a customMethodId on taxAuthRateProduct to increase the 
>> configuration of complex cases.
>>
>> Any suggest ?
>>
>> -- 
>> #jeSuisCharlie
>> logoNrd <http://nereide.fr/>
>> 	Nicolas Malin
>> Ing�nieur d'�tude. Dernier sujet : "Les vaches portant un pr�nom 
>> pouvent trouver la sortie d'un labyrinthe en cas de toxoplasmose
>> information@nereide.fr
>> 8 rue des D�port�s 37000 TOURS, 02 47 50 30 54
>>
>> Apache OFBiz <http://ofbiz.apache.org/> | ofbiz-fr 
>> <http://www.ofbiz-fr.org/> | | r�seau LE 
>> <http://www.libre-entreprise.org/>
> Hi Nicolas,
>
> Did you finally implement this? If yes would you contribute (just 
> curious)?
I sharing with pleasure, because is now in production ;)

To manage this I extend the function 
TaxAuthorityServices.getTaxAuthorities with this  (I implement the 
solution on 13.07):
****************************************
EntityCondition cond = EntityCondition.makeCondition(UtilMisc.toList(
                 EntityCondition.makeCondition("partyId", payToPartyId),
                 EntityCondition.makeCondition("isNexus", "Y")));
         List<GenericValue> taxAuthorityRawList = 
delegator.findList("PartyTaxAuthInfo", cond, null, null, null, true);
         List<EntityCondition> taxCondList = new 
ArrayList<EntityCondition>();
         if (!taxAuthorityRawList.isEmpty()) {
taxCondList.add(EntityCondition.makeCondition("taxAuthPartyId", 
EntityOperator.IN, 
EntityUtil.getFieldListFromEntityList(taxAuthorityRawList, 
"taxAuthPartyId", true)));
             if (Debug.infoOn()) Debug.logInfo("Search tax authority 
relation for " + payToPartyId + " " + taxCondList, module);
             if (originAddress == null) {
                 originAddress = 
ContactMechWorker.getTaxOriginAddress(delegator, payToPartyId);
             }
             if (billingAddress == null) {
                 billingAddress = 
ContactMechWorker.getTaxBillingAddress(delegator, billToPartyId);
             }
             if (originAddress != null && billingAddress != null) {
                 if 
(originAddress.getString("countryGeoId").equals(billingAddress.getString("countryGeoId"))) 
{
                     //ok we will analyse the country where come from 
the flow
                     address = originAddress;
                 }
             }
             if (Debug.infoOn()) {
                 Debug.logInfo("          shipping found " + 
shippingAddress.getString("countryGeoId"), module);
                 Debug.logInfo("          origin found " + 
originAddress.getString("countryGeoId"), module);
                 Debug.logInfo("          billing found " + 
billingAddress.getString("countryGeoId"), module);
                 Debug.logInfo("          country found " + 
address.getString("countryGeoId"), module);
             }
         } else {
             if (Debug.infoOn()) Debug.logInfo("No specific relation, 
run the std resolution", module);
         }
******************************************

The idea, when an order check the vat, I resolv the taxAuth to use first 
with the payToParty. I check if this party have a dedicate relation with 
a specific tax authority by PartyTaxAuthInfo. If yes, I check with the 
shipping and the billing adress to understand if this order is cover by 
the same country.

If no I continue with the standard method.

This is a simple hack, because a better solution would be use the 
orderContachMech to resolve the shipping, billing and origin adress, but 
this works fine like that :) .

The rest is only data configuration on the PartyAuth and bill from vendor.


After to implement a specific text on invoice template and resolve the 
reason of an exoneration, I use a seca like this :

     <eca service="setInvoiceStatus" event="invoke">
          <condition operator="equals" field-name="statusId" 
value="INVOICE_READY"/>
          <action service="checkInvoiceForVATExemptReason" mode="sync" 
ignore-error="false"/>
     </eca>

The service checkInvoiceForVATExemptReason, check if the invoice have 
vat line and if not call this :

****************
  GenericValue partyAuth = EntityUtil.getFirst(partyAuths);
         EntityCondition condTax = 
EntityCondition.makeCondition(UtilMisc.toList(
                 EntityExpr.makeCondition("taxAuthPartyId", 
partyAuth.get("taxAuthPartyId")),
                 EntityExpr.makeCondition("taxAuthGeoId", 
partyAuth.get("taxAuthGeoId")),
                 EntityExpr.makeCondition("taxPercentage", 
GenericEntity.NULL_FIELD),
EntityExpr.makeCondition("taxAmount",GenericEntity.NULL_FIELD),
EntityCondition.makeCondition("taxAuthorityRateTypeId", 
EntityOperator.NOT_EQUAL, "SALES_TAX")));
         List<GenericValue> taxRates = 
delegator.findList("TaxAuthorityRateProduct", condTax, null, 
UtilMisc.toList("sequenceNum"), null, true);
         if (Debug.infoOn()) Debug.logInfo(" ### taxRates " + 
taxRates.size(), module);
         taxRates = EntityUtil.filterByDate(taxRates, 
invoice.getTimestamp("invoiceDate"));
         if (UtilValidate.isEmpty(taxRates)) {
             if (Debug.infoOn()) Debug.logInfo(" no specific rules 
find", module);
             return result;
         }
         //check match case rate
         for (GenericValue taxRate : taxRates) {
             GenericValue custMethod = null;
             String serviceName = null;
             if 
(UtilValidate.isNotEmpty(taxRate.getString("customMethodId"))) {
                 custMethod = delegator.findOne("CustomMethod", true, 
"customMethodId", taxRate.get("customMethodId"));
             }
             if (custMethod != null) serviceName = 
custMethod.getString("customMethodName");
             if (UtilValidate.isNotEmpty(serviceName)) {
                 ModelService service = dctx.getModelService(serviceName);
                 if (service != null) {
                     if (Debug.infoOn()) Debug.logInfo(" call service " 
+ serviceName + "related to taxRate : " + 
taxRate.get("taxAuthorityRateSeqId"), module);
                     Map<String,Object> serviceCtx = 
service.makeValid(context, ModelService.IN_PARAM);
                     serviceCtx.put("taxAuthorityRateSeqId", 
taxRate.get("taxAuthorityRateSeqId"));
                     Map<String,Object> serviceResult = 
dctx.getDispatcher().runSync(serviceName, serviceCtx);
                     if (ServiceUtil.isError(serviceResult)) {
                         throw new 
GeneralException(ServiceUtil.getErrorMessage(serviceResult));
                     }
                     if ("Y".equalsIgnoreCase((String) 
serviceResult.get("taxExempt"))) {
                         Map<String, Object> invoiceItemMap = 
dctx.makeValidContext("createInvoiceItem", "IN", context);
                         invoiceItemMap.put("taxAuthorityRateSeqId", 
taxRate.get("taxAuthorityRateSeqId"));
                         invoiceItemMap.put("taxAuthPartyId", 
taxRate.get("taxAuthPartyId"));
                         invoiceItemMap.put("taxAuthGeoId", 
taxRate.get("taxAuthGeoId"));
                         invoiceItemMap.put("invoiceItemTypeId", 
"ITM_SALES_TAX");
                         invoiceItemMap.put("quantity", 0);
                         invoiceItemMap.put("amount", 0);
                         if (Debug.infoOn()) Debug.logInfo( "Nice is an 
exempt reason, store it on invoice.", module);
                         serviceResult = 
dctx.getDispatcher().runSync("createInvoiceItem", invoiceItemMap);
                         if (ServiceUtil.isError(serviceResult)) {
                             throw new 
GeneralException(ServiceUtil.getErrorMessage(serviceResult));
                         }
                         break;
                     }
                 }
             }
         }
**********************
* You check the related TaxAuth with empty rate (I used Export type)
* For each find, you call a customMethod to understand why you don't 
have VAT

Example :
     private static boolean isEuropeanIntracomCountry(Delegator 
delegator, String countryGeoId) throws GenericEntityException {
         if (countryGeoId != null) {
             return delegator.findOne("GeoAssoc", true, "geoIdTo", "EU", 
"geoId", countryGeoId) != null;
         }
         return false;
Or
     public static Map<String, Object> 
checkEuropeanVatExempt(DispatchContext dctx, Map<String, Object> context)
     throws GeneralException {
         Delegator delegator = dctx.getDelegator();
         Map<String, Object> result = ServiceUtil.returnSuccess();
         String invoiceId = (String) context.get("invoiceId");
         result.put("taxExempt", "N");

         String deliveryCountryGeoId = 
resolvDeliveryCountryGeoId(delegator, invoiceId);
         //Si pas TVA triangulaire dans l'UE alors TVA export Intra com
         if (isEuropeanIntracomCountry(delegator, deliveryCountryGeoId)) {
             result.put("taxExempt", "Y");
         }
         return result;

My apologies for the big raw email and congrat to read up to here ! :)
Nicolas
>
> Thanks
> Jacques