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
informat...@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