[ 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) |
Free forum by Nabble | Edit this page |