You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ofbiz.apache.org by Nicolas Malin <ni...@nereide.fr> on 2016/01/27 16:37:12 UTC
Triangular VAT in Europe ?
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/>
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
Re: Triangular VAT in Europe ?
Posted by Jacques Le Roux <ja...@les7arts.com>.
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 Pierre Smits <pi...@gmail.com>.
In the EU VAT number checking is required and vat numbers must be stated on
the invoice when it comes to cross border sales (B2B), aka export of goods
and services. A site to check the VAT number is
http://ec.europa.eu/taxation_customs/vies/. See my posting in this related
thread:
http://ofbiz.markmail.org/message/7jtninjzwusf2d37?q=%22German+store+with+vat%22
If, and when, the vat number can't be verified the selling party is
required to state the domestic VAT on the export invoice. And when it can
be verified the selling party must apply the domestic VAT regime/rules to
export invoices, whereby many countries follow the principle to allow 0%
due to complications in the VAT collections process (countries don't want
to pay other countries) . Start here for a reference:
http://europa.eu/youreurope/business/vat-customs/cross-border/index_en.htm
The VAT on the purchase invoice has little to do with that, and must be
processed according to the country's regulations.
Best regards,
Pierre Smits
ORRTIZ.COM <http://www.orrtiz.com>
OFBiz based solutions & services
OFBiz Extensions Marketplace
http://oem.ofbizci.net/oci-2/
Re: Triangular VAT in Europe ?
Posted by Nicolas Malin <ni...@nereide.fr>.
Le 27/01/2016 17:28, Heidi Dehaes - Olagos a écrit :
> Hello,
>
> There exists also something like the refutation of the VAT (la
> réfutation du TVA).
> If a company in Belgium purchases a product or service in the
> Netherlands, the company in the Netherlands can send an invoice to the
> Belgium company without VAT by mentioning "refutation of VAT" and it
> is the Belgium company who will have to pay the VAT afterwards to the
> VAT organisation.
And in your purchase invoice, the VAT is from Belgium ?
In other words : What vat you want to see/have on the purchase order
present in OFBiz ?
On the other way : for the sales invoices you set a vat to 0 with the
indication "refutation of VAT" ?
Nicolas
>
> Eric
>
> Olagos bvba
> Heidi Dehaes
> Kerkstraat 34
> 2570 Duffel
> Belgium
> Tel. : 015/31 53 04
> GSM : 0485/22 35 80
> E-mail : info.olagos@gmail.com <ma...@gmail.com>
> http://www.olagos.eu <http://www.olagos.eu/>
> http://www.olagos.com <http://www.olagos.com/>
> http://www.olagos.be <http://www.olagos.be/>
> http://www.olagos.nl <http://www.olagos.nl/>
>
> 2016-01-27 16:37 GMT+01:00 Nicolas Malin <nicolas.malin@nereide.fr
> <ma...@nereide.fr>>:
>
> 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 <ma...@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/>
>
>
Re: Triangular VAT in Europe ?
Posted by Heidi Dehaes - Olagos <in...@gmail.com>.
Hello,
There exists also something like the refutation of the VAT (la réfutation
du TVA).
If a company in Belgium purchases a product or service in the Netherlands,
the company in the Netherlands can send an invoice to the Belgium company
without VAT by mentioning "refutation of VAT" and it is the Belgium company
who will have to pay the VAT afterwards to the VAT organisation.
Eric
Olagos bvba
Heidi Dehaes
Kerkstraat 34
2570 Duffel
Belgium
Tel. : 015/31 53 04
GSM : 0485/22 35 80
E-mail : info.olagos@gmail.com
http://www.olagos.eu
http://www.olagos.com
http://www.olagos.be
http://www.olagos.nl
2016-01-27 16:37 GMT+01:00 Nicolas Malin <ni...@nereide.fr>:
> 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
> [image: 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/>
>