diff --git a/web/org.openbravo.retail.posterminal/app/model/business-logic/taxes-engine/engine/tax-engine.js b/web/org.openbravo.retail.posterminal/app/model/business-logic/taxes-engine/engine/tax-engine.js
index 91357ddac..69df3255e 100644
--- a/web/org.openbravo.retail.posterminal/app/model/business-logic/taxes-engine/engine/tax-engine.js
+++ b/web/org.openbravo.retail.posterminal/app/model/business-logic/taxes-engine/engine/tax-engine.js
@@ -1,6 +1,6 @@
 /*
  ************************************************************************************
- * Copyright (C) 2020 Openbravo S.L.U.
+ * Copyright (C) 2020-2022 Openbravo S.L.U.
  * Licensed under the Openbravo Commercial License version 1.0
  * You may obtain a copy of the License at http://www.openbravo.com/legal/obcl.html
  * or in the legal folder of this module distribution.
@@ -34,23 +34,27 @@
   };
 
   OB.Taxes.filterRulesByTicket = (ticket, rules) => {
-    const ticketLinesTaxCategories = [
-      ...new Set(
-        ticket.lines.flatMap(line =>
-          [line.product.taxCategory].concat(
-            (line.product.productBOM || []).map(
-              bomLine => bomLine.product.taxCategory
+    const ticketLinesSet = ticket.lines.reduce(
+      (obj, line) => ({
+        taxCategory: new Set([
+          ...obj.taxCategory,
+          ...new Set(
+            [line.product.taxCategory].concat(
+              (line.product.productBOM || []).map(
+                bomLine => bomLine.product.taxCategory
+              )
             )
           )
-        )
-      )
-    ];
-    const ticketLinesCountries = [
-      ...new Set(ticket.lines.map(line => line.country))
-    ];
-    const ticketLinesRegions = [
-      ...new Set(ticket.lines.map(line => line.region))
-    ];
+        ]),
+        country: obj.country.add(line.country),
+        region: obj.region.add(line.region)
+      }),
+      {
+        taxCategory: new Set(),
+        country: new Set(ticket.country),
+        region: new Set(ticket.region)
+      }
+    );
     const checkTaxCategory = rule => {
       return (
         (rule.taxExempt ||
@@ -58,9 +62,7 @@
             rule.businessPartnerTaxCategory,
             ticket.businessPartner.taxCategory
           )) &&
-        ticketLinesTaxCategories.some(taxCategory =>
-          equals(taxCategory, rule.taxCategory)
-        )
+        ticketLinesSet.taxCategory.has(rule.taxCategory)
       );
     };
     const checkValidFromDate = rule => {
@@ -75,6 +77,42 @@
         (!rule.isCashVAT && (rule.withholdingTax || equals(rule.rate, 0)))
       );
     };
+    const checkandUpdateLocation = rule => {
+      const matchLocation = (
+        ruleOrZone,
+        ruleOrZoneLocationProperty,
+        lineLocationProperty
+      ) => {
+        // Rules apply if they match for each location property or if location property is empty
+        const ruleLocation = ruleOrZone[ruleOrZoneLocationProperty];
+        const lineLocation = ticketLinesSet[lineLocationProperty];
+        return !ruleLocation || lineLocation.has(ruleLocation);
+      };
+
+      // If rule matches any line country and region we keep the rule as it is
+      if (
+        matchLocation(rule, 'destinationCountry', 'country') &&
+        matchLocation(rule, 'destinationRegion', 'region')
+      ) {
+        return rule;
+      }
+
+      // If rule includes a zone that matches any line country and region we keep the zone
+      const taxZones = (rule.taxZones || []).filter(
+        zone =>
+          matchLocation(zone, 'zoneDestinationCountry', 'country') &&
+          matchLocation(zone, 'zoneDestinationRegion', 'region')
+      );
+      if (taxZones.length) {
+        return {
+          ...rule,
+          taxZones
+        };
+      }
+
+      // If no match we remove the rule
+      return [];
+    };
     const sortByDate = (rule1, rule2) => {
       return new Date(rule2.validFromDate) - new Date(rule1.validFromDate);
     };
@@ -99,68 +137,6 @@
       }
       return 0;
     };
-    const joinRuleAndZone = rule => {
-      const updatedRule = rule.taxZones
-        ? rule.taxZones.map(zone => ({ ...zone, ...rule }))
-        : rule;
-      updatedRule.zoneCountry = rule.zoneCountry || rule.country;
-      updatedRule.zoneRegion = rule.zoneRegion || rule.region;
-      updatedRule.zoneDestinationCountry =
-        rule.zoneDestinationCountry || rule.destinationCountry;
-      updatedRule.zoneDestinationRegion =
-        rule.zoneDestinationRegion || rule.destinationRegion;
-      return updatedRule;
-    };
-    const checkCountry = rule => {
-      return (
-        equals(rule.destinationCountry, ticket.country) ||
-        equals(rule.zoneDestinationCountry, ticket.country) ||
-        ticketLinesCountries.some(country =>
-          equals(country, rule.destinationCountry)
-        ) ||
-        ticketLinesCountries.some(country =>
-          equals(country, rule.zoneDestinationCountry)
-        ) ||
-        (!rule.destinationCountry && !rule.zoneDestinationCountry)
-      );
-    };
-    const checkRegion = rule => {
-      return (
-        equals(rule.destinationRegion, ticket.country) ||
-        equals(rule.zoneDestinationRegion, ticket.country) ||
-        ticketLinesRegions.some(country =>
-          equals(country, rule.destinationRegion)
-        ) ||
-        ticketLinesRegions.some(country =>
-          equals(country, rule.zoneDestinationRegion)
-        ) ||
-        (!rule.destinationRegion && !rule.zoneDestinationRegion)
-      );
-    };
-    const sortByRegionFrom = (rule1, rule2) => {
-      const checkRegionFrom = rule => {
-        return rule.region || rule.zoneRegion;
-      };
-      if (checkRegionFrom(rule1) && !checkRegionFrom(rule2)) {
-        return -1;
-      }
-      if (!checkRegionFrom(rule1) && checkRegionFrom(rule2)) {
-        return 1;
-      }
-      return 0;
-    };
-    const sortByCountryFrom = (rule1, rule2) => {
-      const checkCountryFrom = rule => {
-        return rule.country || rule.zoneCountry;
-      };
-      if (checkCountryFrom(rule1) && !checkCountryFrom(rule2)) {
-        return -1;
-      }
-      if (!checkCountryFrom(rule1) && checkCountryFrom(rule2)) {
-        return 1;
-      }
-      return 0;
-    };
 
     return rules
       .filter(
@@ -170,6 +146,7 @@
           checkSummary(rule) &&
           checkIsCashVAT(rule)
       )
+      .flatMap(rule => checkandUpdateLocation(rule))
       .sort(
         (rule1, rule2) =>
           sortByDate(rule1, rule2) ||
@@ -177,30 +154,10 @@
           sortByLineno(rule1, rule2) ||
           sortByCascade(rule1, rule2) ||
           sortByTaxBase(rule1, rule2)
-      )
-      .flatMap(rule => joinRuleAndZone(rule))
-      .filter(rule => checkCountry(rule) && checkRegion(rule))
-      .sort(
-        (rule1, rule2) =>
-          sortByRegionFrom(rule1, rule2) || sortByCountryFrom(rule1, rule2)
       );
   };
 
   OB.Taxes.filterRulesByTicketLine = (ticket, line, rules) => {
-    const checkCountry = rule => {
-      return (
-        equals(rule.destinationCountry, line.country || ticket.country) ||
-        equals(rule.zoneDestinationCountry, line.country || ticket.country) ||
-        (!rule.destinationCountry && !rule.zoneDestinationCountry)
-      );
-    };
-    const checkRegion = rule => {
-      return (
-        equals(rule.destinationRegion, line.region || ticket.region) ||
-        equals(rule.zoneDestinationRegion, line.region || ticket.region) ||
-        (!rule.destinationRegion && !rule.zoneDestinationRegion)
-      );
-    };
     const checkTaxCategory = rule => {
       const isTaxExempt = line.taxExempt || ticket.businessPartner.taxExempt;
       return (
@@ -218,108 +175,87 @@
         (line.originalOrderDate ? new Date(line.originalOrderDate) : new Date())
       );
     };
-    const sortByRegionTo = (rule1, rule2) => {
-      const checkRegionTo = rule => {
-        return (
-          equals(rule.destinationRegion, line.region || ticket.region) ||
-          equals(rule.zoneDestinationRegion, line.region || ticket.region)
-        );
+    const checkandUpdateLocation = rule => {
+      const matchLocation = (
+        ruleOrZone,
+        ruleOrZoneLocationProperty,
+        lineLocationProperty
+      ) => {
+        // Rules apply if they match for each location property or if location property is empty
+        const ruleLocation = ruleOrZone[ruleOrZoneLocationProperty];
+        const lineLocation =
+          line[lineLocationProperty] || ticket[lineLocationProperty];
+        return !ruleLocation || equals(ruleLocation, lineLocation);
       };
-      if (checkRegionTo(rule1) && !checkRegionTo(rule2)) {
-        return -1;
+
+      // If rule matches line country and region we keep the rule as it is
+      if (
+        matchLocation(rule, 'destinationCountry', 'country') &&
+        matchLocation(rule, 'destinationRegion', 'region')
+      ) {
+        return { ...rule, taxZones: undefined };
       }
-      if (!checkRegionTo(rule1) && checkRegionTo(rule2)) {
-        return 1;
+
+      // If rule includes a zone that matches line country and region we update rule with zone information
+      const taxZone = (rule.taxZones || []).find(
+        zone =>
+          matchLocation(zone, 'zoneDestinationCountry', 'country') &&
+          matchLocation(zone, 'zoneDestinationRegion', 'region')
+      );
+      if (taxZone) {
+        return {
+          ...rule,
+          country: taxZone.zoneCountry,
+          destinationCountry: taxZone.zoneDestinationCountry,
+          region: taxZone.zoneRegion,
+          destinationRegion: taxZone.zoneDestinationRegion,
+          taxZones: undefined
+        };
       }
-      return 0;
+
+      // If no match we remove the rule
+      return [];
     };
-    const sortByCountryTo = (rule1, rule2) => {
-      const checkCountryTo = rule => {
-        return (
-          equals(rule.destinationCountry, line.country || ticket.country) ||
-          equals(rule.zoneDestinationCountry, line.country || ticket.country)
-        );
+    const sortByLocation = (rule1, rule2) => {
+      const sortLocation = ruleLocationProperty => {
+        if (rule1[ruleLocationProperty] && !rule2[ruleLocationProperty]) {
+          return -1;
+        }
+        if (!rule1[ruleLocationProperty] && rule2[ruleLocationProperty]) {
+          return 1;
+        }
+        return 0;
       };
-      if (checkCountryTo(rule1) && !checkCountryTo(rule2)) {
-        return -1;
-      }
-      if (!checkCountryTo(rule1) && checkCountryTo(rule2)) {
-        return 1;
-      }
-      return 0;
-    };
-    const updateRuleLocation = rule => {
-      const updatedRule = { ...rule };
-      updatedRule.country = equals(rule.country, line.country || ticket.country)
-        ? rule.country
-        : rule.zoneCountry;
-      updatedRule.region = equals(rule.region, line.region || ticket.region)
-        ? rule.region
-        : rule.zoneRegion;
-      updatedRule.destinationCountry = equals(
-        rule.destinationCountry,
-        line.country || ticket.country
-      )
-        ? rule.destinationCountry
-        : rule.zoneDestinationCountry;
-      updatedRule.destinationRegion = equals(
-        rule.destinationRegion,
-        line.region || ticket.region
-      )
-        ? rule.destinationRegion
-        : rule.zoneDestinationRegion;
-      return updatedRule;
-    };
-    const checkLocationAndDate = (rule, rulesFilteredByLine) => {
+
+      // Rules with defined country and region have more priority than rules with empty country and region
       return (
-        equals(
-          rule.destinationCountry,
-          rulesFilteredByLine[0].destinationCountry
-        ) &&
-        equals(
-          rule.destinationRegion,
-          rulesFilteredByLine[0].destinationRegion
-        ) &&
-        equals(rule.country, rulesFilteredByLine[0].country) &&
-        equals(rule.region, rulesFilteredByLine[0].region) &&
-        equals(rule.validFromDate, rulesFilteredByLine[0].validFromDate)
+        sortLocation('destinationRegion') ||
+        sortLocation('region') ||
+        sortLocation('destinationCountry') ||
+        sortLocation('country')
       );
     };
-    const groupRuleAndZone = (rulesAndZones, rule) => {
-      const updatedRule = { ...rule };
-      delete updatedRule.taxRateId;
-      delete updatedRule.zoneCountry;
-      delete updatedRule.zoneRegion;
-      delete updatedRule.zoneDestinationCountry;
-      delete updatedRule.zoneDestinationRegion;
-      delete updatedRule.taxZones;
-
-      return rulesAndZones.find(
-        ruleAndZone => ruleAndZone.id === updatedRule.id
-      )
-        ? rulesAndZones
-        : [...rulesAndZones, updatedRule];
+    const checkDateAndLocationWithFirstMatchingRule = (
+      rule,
+      rulesFilteredByLine
+    ) => {
+      // We return every rule with the same match for date property and for each location property
+      const firstMatchingRule = rulesFilteredByLine[0];
+      return (
+        equals(rule.validFromDate, firstMatchingRule.validFromDate) &&
+        equals(rule.country, firstMatchingRule.country) &&
+        equals(rule.destinationCountry, firstMatchingRule.destinationCountry) &&
+        equals(rule.region, firstMatchingRule.region) &&
+        equals(rule.destinationRegion, firstMatchingRule.destinationRegion)
+      );
     };
 
     return rules
-      .filter(
-        rule =>
-          checkCountry(rule) &&
-          checkRegion(rule) &&
-          checkTaxCategory(rule) &&
-          checkValidFromDate(rule)
-      )
-      .sort(
-        (rule1, rule2) =>
-          sortByRegionTo(rule1, rule2) || sortByCountryTo(rule1, rule2)
-      )
-      .map(rule => updateRuleLocation(rule))
-      .reduce(
-        (rulesAndZones, rule) => groupRuleAndZone(rulesAndZones, rule),
-        []
-      )
+      .filter(rule => checkTaxCategory(rule) && checkValidFromDate(rule))
+      .flatMap(rule => checkandUpdateLocation(rule))
+      .sort((rule1, rule2) => sortByLocation(rule1, rule2))
       .filter((rule, index, rulesFilteredByLine) =>
-        checkLocationAndDate(rule, rulesFilteredByLine)
+        checkDateAndLocationWithFirstMatchingRule(rule, rulesFilteredByLine)
       );
   };
 })();
