[
https://issues.apache.org/jira/browse/OFBIZ-7138?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15304070#comment-15304070
]
Nicolas Malin commented on OFBIZ-7138:
--------------------------------------
To manage this I extend the function TaxAuthorityServices.getTaxAuthorities
with this (I implement the solution on 13.07):
{code}
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);
}
{code}
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 :
{code}
<eca service="setInvoiceStatus" event="invoke">
<condition operator="equals" field-name="statusId"
value="INVOICE_READY"/>
<action service="checkInvoiceForVATExemptReason" mode="sync"
ignore-error="false"/>
</eca>
{code}
The service checkInvoiceForVATExemptReason, check if the invoice have vat line
and if not call this :
{code}
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;
}
}
}
}
{code}
* 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 :
{code}
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;
{code}
Or
{code}
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;
{code}
> Manage Triangular European VAT
> -------------------------------
>
> Key: OFBIZ-7138
> URL: https://issues.apache.org/jira/browse/OFBIZ-7138
> Project: OFBiz
> Issue Type: Bug
> Affects Versions: Trunk
> Reporter: Nicolas Malin
> Assignee: Nicolas Malin
> Priority: Minor
> Labels: tax, vat
>
> I open an issue related to mailing thread
> https://lists.apache.org/thread.html/Z8ksgxdskmbcg9n
> The origin came from here :
> {quote}
> 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.
> {quote}
> I will load a patch in few week ;)
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)