[ 
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)

Reply via email to