public static boolean isProductInCategory(
      Delegator delegator, String productId, String productCategoryId)
      throws GenericEntityException {
    if (productCategoryId == null) return false;
    if (UtilValidate.isEmpty(productId)) return false;

    List<GenericValue> productCategoryMembers =
        EntityUtil.filterByDate(
            delegator.findByAndCache(
                "ProductCategoryMember",
                UtilMisc.toMap("productCategoryId", productCategoryId, "productId", productId)),
            true);
    if (UtilValidate.isEmpty(productCategoryMembers)) {
      // before giving up see if this is a variant product, and if so look up the virtual product
      // and check it...
      GenericValue product =
          delegator.findByPrimaryKeyCache("Product", UtilMisc.toMap("productId", productId));
      List<GenericValue> productAssocs = ProductWorker.getVariantVirtualAssocs(product);
      // this does take into account that a product could be a variant of multiple products, but
      // this shouldn't ever really happen...
      if (productAssocs != null) {
        for (GenericValue productAssoc : productAssocs) {
          if (isProductInCategory(
              delegator, productAssoc.getString("productId"), productCategoryId)) {
            return true;
          }
        }
      }

      return false;
    } else {
      return true;
    }
  }
Esempio n. 2
0
  /**
   * Generic service to find party by id. By default return the party find by partyId but you can
   * pass searchPartyFirst at false if you want search in partyIdentification before or pass
   * searchAllId at true to find apartyuct with this id (party.partyId and
   * partyIdentification.idValue)
   *
   * @param delegator
   * @param idToFind
   * @param partyIdentificationTypeId
   * @param searchPartyFirst
   * @param searchAllId
   * @return
   * @throws GenericEntityException
   */
  public static List<GenericValue> findPartiesById(
      Delegator delegator,
      String idToFind,
      String partyIdentificationTypeId,
      boolean searchPartyFirst,
      boolean searchAllId)
      throws GenericEntityException {

    if (Debug.verboseOn())
      Debug.logVerbose(
          "Analyze partyIdentification: entered id = "
              + idToFind
              + ", partyIdentificationTypeId = "
              + partyIdentificationTypeId,
          module);

    GenericValue party = null;
    List<GenericValue> partiesFound = null;

    // 1) look if the idToFind given is a real partyId
    if (searchPartyFirst) {
      party = delegator.findByPrimaryKeyCache("Party", UtilMisc.toMap("partyId", idToFind));
    }

    if (searchAllId || (searchPartyFirst && UtilValidate.isEmpty(party))) {
      // 2) Retrieve party in PartyIdentification
      Map<String, String> conditions = UtilMisc.toMap("idValue", idToFind);
      if (UtilValidate.isNotEmpty(partyIdentificationTypeId)) {
        conditions.put("partyIdentificationTypeId", partyIdentificationTypeId);
      }
      partiesFound =
          delegator.findByAndCache(
              "PartyIdentificationAndParty", conditions, UtilMisc.toList("partyId"));
    }

    if (!searchPartyFirst) {
      party = delegator.findByPrimaryKeyCache("Party", UtilMisc.toMap("partyId", idToFind));
    }

    if (UtilValidate.isNotEmpty(party)) {
      if (UtilValidate.isNotEmpty(partiesFound)) partiesFound.add(party);
      else partiesFound = UtilMisc.toList(party);
    }
    if (Debug.verboseOn())
      Debug.logVerbose(
          "Analyze partyIdentification: found party.partyId = "
              + party
              + ", and list : "
              + partiesFound,
          module);
    return partiesFound;
  }
  public static List<GenericValue> getRelatedCategoriesRet(
      Delegator delegator,
      String attributeName,
      String parentId,
      boolean limitView,
      boolean excludeEmpty,
      boolean recursive) {
    List<GenericValue> categories = FastList.newInstance();

    if (Debug.verboseOn())
      Debug.logVerbose("[CategoryWorker.getRelatedCategories] ParentID: " + parentId, module);

    List<GenericValue> rollups = null;

    try {
      rollups =
          delegator.findByAndCache(
              "ProductCategoryRollup",
              UtilMisc.toMap("parentProductCategoryId", parentId),
              UtilMisc.toList("sequenceNum"));
      if (limitView) {
        rollups = EntityUtil.filterByDate(rollups, true);
      }
    } catch (GenericEntityException e) {
      Debug.logWarning(e.getMessage(), module);
    }
    if (rollups != null) {
      // Debug.logInfo("Rollup size: " + rollups.size(), module);
      for (GenericValue parent : rollups) {
        // Debug.logInfo("Adding child of: " + parent.getString("parentProductCategoryId"), module);
        GenericValue cv = null;

        try {
          cv = parent.getRelatedOneCache("CurrentProductCategory");
        } catch (GenericEntityException e) {
          Debug.logWarning(e.getMessage(), module);
        }
        if (cv != null) {
          if (excludeEmpty) {
            if (!isCategoryEmpty(cv)) {
              // Debug.logInfo("Child : " + cv.getString("productCategoryId") + " is not empty.",
              // module);
              categories.add(cv);
              if (recursive) {
                categories.addAll(
                    getRelatedCategoriesRet(
                        delegator,
                        attributeName,
                        cv.getString("productCategoryId"),
                        limitView,
                        excludeEmpty,
                        recursive));
              }
            }
          } else {
            categories.add(cv);
            if (recursive) {
              categories.addAll(
                  getRelatedCategoriesRet(
                      delegator,
                      attributeName,
                      cv.getString("productCategoryId"),
                      limitView,
                      excludeEmpty,
                      recursive));
            }
          }
        }
      }
    }
    return categories;
  }
Esempio n. 4
0
  private static void handlePartyTaxExempt(
      GenericValue adjValue,
      Set<String> billToPartyIdSet,
      String taxAuthGeoId,
      String taxAuthPartyId,
      BigDecimal taxAmount,
      Timestamp nowTimestamp,
      Delegator delegator)
      throws GenericEntityException {
    Debug.logInfo("Checking for tax exemption : " + taxAuthGeoId + " / " + taxAuthPartyId, module);
    List<EntityCondition> ptiConditionList =
        UtilMisc.<EntityCondition>toList(
            EntityCondition.makeCondition("partyId", EntityOperator.IN, billToPartyIdSet),
            EntityCondition.makeCondition("taxAuthGeoId", EntityOperator.EQUALS, taxAuthGeoId),
            EntityCondition.makeCondition("taxAuthPartyId", EntityOperator.EQUALS, taxAuthPartyId));
    ptiConditionList.add(
        EntityCondition.makeCondition("fromDate", EntityOperator.LESS_THAN_EQUAL_TO, nowTimestamp));
    ptiConditionList.add(
        EntityCondition.makeCondition(
            EntityCondition.makeCondition("thruDate", EntityOperator.EQUALS, null),
            EntityOperator.OR,
            EntityCondition.makeCondition("thruDate", EntityOperator.GREATER_THAN, nowTimestamp)));
    EntityCondition ptiCondition =
        EntityCondition.makeCondition(ptiConditionList, EntityOperator.AND);
    // sort by -fromDate to get the newest (largest) first, just in case there is more than one, we
    // only want the most recent valid one, should only be one per jurisdiction...
    List<GenericValue> partyTaxInfos =
        delegator.findList(
            "PartyTaxAuthInfo", ptiCondition, null, UtilMisc.toList("-fromDate"), null, false);

    boolean foundExemption = false;
    if (partyTaxInfos.size() > 0) {
      GenericValue partyTaxInfo = partyTaxInfos.get(0);
      adjValue.set("customerReferenceId", partyTaxInfo.get("partyTaxId"));
      if ("Y".equals(partyTaxInfo.getString("isExempt"))) {
        adjValue.set("amount", BigDecimal.ZERO);
        adjValue.set("exemptAmount", taxAmount);
        foundExemption = true;
      }
    }

    // if no exceptions were found for the current; try the parent
    if (!foundExemption) {
      // try the "parent" TaxAuthority
      List<GenericValue> taxAuthorityAssocList =
          delegator.findByAndCache(
              "TaxAuthorityAssoc",
              UtilMisc.toMap(
                  "toTaxAuthGeoId",
                  taxAuthGeoId,
                  "toTaxAuthPartyId",
                  taxAuthPartyId,
                  "taxAuthorityAssocTypeId",
                  "EXEMPT_INHER"),
              UtilMisc.toList("-fromDate"));
      taxAuthorityAssocList = EntityUtil.filterByDate(taxAuthorityAssocList, true);
      GenericValue taxAuthorityAssoc = EntityUtil.getFirst(taxAuthorityAssocList);
      // Debug.logInfo("Parent assoc to " + taxAuthGeoId + " : " + taxAuthorityAssoc, module);
      if (taxAuthorityAssoc != null) {
        handlePartyTaxExempt(
            adjValue,
            billToPartyIdSet,
            taxAuthorityAssoc.getString("taxAuthGeoId"),
            taxAuthorityAssoc.getString("taxAuthPartyId"),
            taxAmount,
            nowTimestamp,
            delegator);
      }
    }
  }
Esempio n. 5
0
  private static List<GenericValue> getTaxAdjustments(
      Delegator delegator,
      GenericValue product,
      GenericValue productStore,
      String payToPartyId,
      String billToPartyId,
      Set<GenericValue> taxAuthoritySet,
      BigDecimal itemPrice,
      BigDecimal itemQuantity,
      BigDecimal itemAmount,
      BigDecimal shippingAmount,
      BigDecimal orderPromotionsAmount) {
    Timestamp nowTimestamp = UtilDateTime.nowTimestamp();
    List<GenericValue> adjustments = FastList.newInstance();

    if (payToPartyId == null) {
      if (productStore != null) {
        payToPartyId = productStore.getString("payToPartyId");
      }
    }

    // store expr
    EntityCondition storeCond = null;
    if (productStore != null) {
      storeCond =
          EntityCondition.makeCondition(
              EntityCondition.makeCondition(
                  "productStoreId", EntityOperator.EQUALS, productStore.get("productStoreId")),
              EntityOperator.OR,
              EntityCondition.makeCondition("productStoreId", EntityOperator.EQUALS, null));
    } else {
      storeCond = EntityCondition.makeCondition("productStoreId", EntityOperator.EQUALS, null);
    }

    // build the TaxAuthority expressions (taxAuthGeoId, taxAuthPartyId)
    List<EntityCondition> taxAuthCondOrList = FastList.newInstance();
    // start with the _NA_ TaxAuthority...
    taxAuthCondOrList.add(
        EntityCondition.makeCondition(
            EntityCondition.makeCondition("taxAuthPartyId", EntityOperator.EQUALS, "_NA_"),
            EntityOperator.AND,
            EntityCondition.makeCondition("taxAuthGeoId", EntityOperator.EQUALS, "_NA_")));

    for (GenericValue taxAuthority : taxAuthoritySet) {
      EntityCondition taxAuthCond =
          EntityCondition.makeCondition(
              EntityCondition.makeCondition(
                  "taxAuthPartyId",
                  EntityOperator.EQUALS,
                  taxAuthority.getString("taxAuthPartyId")),
              EntityOperator.AND,
              EntityCondition.makeCondition(
                  "taxAuthGeoId", EntityOperator.EQUALS, taxAuthority.getString("taxAuthGeoId")));
      taxAuthCondOrList.add(taxAuthCond);
    }
    EntityCondition taxAuthoritiesCond =
        EntityCondition.makeCondition(taxAuthCondOrList, EntityOperator.OR);

    try {
      EntityCondition productCategoryCond = null;
      if (product != null) {
        // find the tax categories associated with the product and filter by those, with an IN
        // clause or some such
        // if this product is variant, find the virtual product id and consider also the categories
        // of the virtual
        // question: get all categories, or just a special type? for now let's do all categories...
        String virtualProductId = null;
        if ("Y".equals(product.getString("isVariant"))) {
          virtualProductId = ProductWorker.getVariantVirtualId(product);
        }
        Set<String> productCategoryIdSet = FastSet.newInstance();
        EntityCondition productIdCond = null;
        if (virtualProductId != null) {
          productIdCond =
              EntityCondition.makeCondition(
                  EntityCondition.makeCondition(
                      "productId", EntityOperator.EQUALS, product.getString("productId")),
                  EntityOperator.OR,
                  EntityCondition.makeCondition(
                      "productId", EntityOperator.EQUALS, virtualProductId));

        } else {
          productIdCond =
              EntityCondition.makeCondition(
                  "productId", EntityOperator.EQUALS, product.getString("productId"));
        }
        List<GenericValue> pcmList =
            delegator.findList(
                "ProductCategoryMember",
                productIdCond,
                UtilMisc.toSet("productCategoryId", "fromDate", "thruDate"),
                null,
                null,
                true);
        pcmList = EntityUtil.filterByDate(pcmList, true);
        for (GenericValue pcm : pcmList) {
          productCategoryIdSet.add(pcm.getString("productCategoryId"));
        }

        if (productCategoryIdSet.size() == 0) {
          productCategoryCond =
              EntityCondition.makeCondition("productCategoryId", EntityOperator.EQUALS, null);
        } else {
          productCategoryCond =
              EntityCondition.makeCondition(
                  EntityCondition.makeCondition("productCategoryId", EntityOperator.EQUALS, null),
                  EntityOperator.OR,
                  EntityCondition.makeCondition(
                      "productCategoryId", EntityOperator.IN, productCategoryIdSet));
        }
      } else {
        productCategoryCond =
            EntityCondition.makeCondition("productCategoryId", EntityOperator.EQUALS, null);
      }

      // FIXME handles shipping and promo tax. Simple solution, see
      // https://issues.apache.org/jira/browse/OFBIZ-4160 for a better one
      if (product == null && shippingAmount != null) {
        EntityCondition taxShippingCond =
            EntityCondition.makeCondition(
                EntityCondition.makeCondition("taxShipping", EntityOperator.EQUALS, null),
                EntityOperator.OR,
                EntityCondition.makeCondition("taxShipping", EntityOperator.EQUALS, "Y"));

        if (productCategoryCond != null) {
          productCategoryCond =
              EntityCondition.makeCondition(
                  productCategoryCond, EntityOperator.OR, taxShippingCond);
        }
      }

      if (product == null && orderPromotionsAmount != null) {
        EntityCondition taxOrderPromotionsCond =
            EntityCondition.makeCondition(
                EntityCondition.makeCondition("taxPromotions", EntityOperator.EQUALS, null),
                EntityOperator.OR,
                EntityCondition.makeCondition("taxPromotions", EntityOperator.EQUALS, "Y"));

        if (productCategoryCond != null) {
          productCategoryCond =
              EntityCondition.makeCondition(
                  productCategoryCond, EntityOperator.OR, taxOrderPromotionsCond);
        }
      }

      // build the main condition clause
      List<EntityCondition> mainExprs =
          UtilMisc.toList(storeCond, taxAuthoritiesCond, productCategoryCond);
      mainExprs.add(
          EntityCondition.makeCondition(
              EntityCondition.makeCondition("minItemPrice", EntityOperator.EQUALS, null),
              EntityOperator.OR,
              EntityCondition.makeCondition(
                  "minItemPrice", EntityOperator.LESS_THAN_EQUAL_TO, itemPrice)));
      mainExprs.add(
          EntityCondition.makeCondition(
              EntityCondition.makeCondition("minPurchase", EntityOperator.EQUALS, null),
              EntityOperator.OR,
              EntityCondition.makeCondition(
                  "minPurchase", EntityOperator.LESS_THAN_EQUAL_TO, itemAmount)));
      EntityCondition mainCondition = EntityCondition.makeCondition(mainExprs, EntityOperator.AND);

      // create the orderby clause
      List<String> orderList = UtilMisc.<String>toList("minItemPrice", "minPurchase", "fromDate");

      // finally ready... do the rate query
      List<GenericValue> lookupList =
          delegator.findList(
              "TaxAuthorityRateProduct", mainCondition, null, orderList, null, false);
      List<GenericValue> filteredList = EntityUtil.filterByDate(lookupList, true);

      if (filteredList.size() == 0) {
        Debug.logWarning(
            "In TaxAuthority Product Rate no records were found for condition:"
                + mainCondition.toString(),
            module);
        return adjustments;
      }

      // find the right entry(s) based on purchase amount
      for (GenericValue taxAuthorityRateProduct : filteredList) {
        BigDecimal taxRate =
            taxAuthorityRateProduct.get("taxPercentage") != null
                ? taxAuthorityRateProduct.getBigDecimal("taxPercentage")
                : ZERO_BASE;
        BigDecimal taxable = ZERO_BASE;

        if (product != null
            && (product.get("taxable") == null
                || (product.get("taxable") != null
                    && product.getBoolean("taxable").booleanValue()))) {
          taxable = taxable.add(itemAmount);
        }
        if (shippingAmount != null
            && taxAuthorityRateProduct != null
            && (taxAuthorityRateProduct.get("taxShipping") == null
                || (taxAuthorityRateProduct.get("taxShipping") != null
                    && taxAuthorityRateProduct.getBoolean("taxShipping").booleanValue()))) {
          taxable = taxable.add(shippingAmount);
        }
        if (orderPromotionsAmount != null
            && taxAuthorityRateProduct != null
            && (taxAuthorityRateProduct.get("taxPromotions") == null
                || (taxAuthorityRateProduct.get("taxPromotions") != null
                    && taxAuthorityRateProduct.getBoolean("taxPromotions").booleanValue()))) {
          taxable = taxable.add(orderPromotionsAmount);
        }

        if (taxable.compareTo(BigDecimal.ZERO) == 0) {
          // this should make it less confusing if the taxable flag on the product is not Y/true,
          // and there is no shipping and such
          continue;
        }

        // taxRate is in percentage, so needs to be divided by 100
        BigDecimal taxAmount =
            (taxable.multiply(taxRate))
                .divide(PERCENT_SCALE, salestaxCalcDecimals, salestaxRounding);

        String taxAuthGeoId = taxAuthorityRateProduct.getString("taxAuthGeoId");
        String taxAuthPartyId = taxAuthorityRateProduct.getString("taxAuthPartyId");

        // get glAccountId from TaxAuthorityGlAccount entity using the payToPartyId as the
        // organizationPartyId
        GenericValue taxAuthorityGlAccount =
            delegator.findByPrimaryKey(
                "TaxAuthorityGlAccount",
                UtilMisc.toMap(
                    "taxAuthPartyId",
                    taxAuthPartyId,
                    "taxAuthGeoId",
                    taxAuthGeoId,
                    "organizationPartyId",
                    payToPartyId));
        String taxAuthGlAccountId = null;
        if (taxAuthorityGlAccount != null) {
          taxAuthGlAccountId = taxAuthorityGlAccount.getString("glAccountId");
        } else {
          // TODO: what to do if no TaxAuthorityGlAccount found? Use some default, or is that done
          // elsewhere later on?
        }

        GenericValue productPrice = null;
        if (product != null && taxAuthPartyId != null && taxAuthGeoId != null) {
          // find a ProductPrice for the productId and taxAuth* valxues, and see if it has a
          // priceWithTax value
          Map<String, String> priceFindMap =
              UtilMisc.toMap(
                  "productId",
                  product.getString("productId"),
                  "taxAuthPartyId",
                  taxAuthPartyId,
                  "taxAuthGeoId",
                  taxAuthGeoId,
                  "productPricePurposeId",
                  "PURCHASE");
          List<GenericValue> productPriceList =
              delegator.findByAnd("ProductPrice", priceFindMap, UtilMisc.toList("-fromDate"));
          productPriceList = EntityUtil.filterByDate(productPriceList, true);
          productPrice =
              (productPriceList != null && productPriceList.size() > 0)
                  ? productPriceList.get(0)
                  : null;
          // Debug.logInfo("=================== productId=" + product.getString("productId"),
          // module);
          // Debug.logInfo("=================== productPrice=" + productPrice, module);

        }

        GenericValue taxAdjValue = delegator.makeValue("OrderAdjustment");

        if (productPrice != null && "Y".equals(productPrice.getString("taxInPrice"))) {
          // tax is in the price already, so we want the adjustment to be a VAT_TAX adjustment to be
          // subtracted instead of a SALES_TAX adjustment to be added
          taxAdjValue.set("orderAdjustmentTypeId", "VAT_TAX");

          // the amount will be different because we want to figure out how much of the price was
          // tax, and not how much tax needs to be added
          // the formula is: taxAmount = priceWithTax - (priceWithTax/(1+taxPercentage/100))
          BigDecimal taxAmountIncluded =
              itemAmount.subtract(
                  itemAmount.divide(
                      BigDecimal.ONE.add(
                          taxRate.divide(PERCENT_SCALE, 4, BigDecimal.ROUND_HALF_UP)),
                      3,
                      BigDecimal.ROUND_HALF_UP));
          taxAdjValue.set("amountAlreadyIncluded", taxAmountIncluded);
          taxAdjValue.set("amount", BigDecimal.ZERO);
        } else {
          taxAdjValue.set("orderAdjustmentTypeId", "SALES_TAX");
          taxAdjValue.set("amount", taxAmount);
        }

        taxAdjValue.set("sourcePercentage", taxRate);
        taxAdjValue.set(
            "taxAuthorityRateSeqId", taxAuthorityRateProduct.getString("taxAuthorityRateSeqId"));
        // the primary Geo should be the main jurisdiction that the tax is for, and the secondary
        // would just be to define a parent or wrapping jurisdiction of the primary
        taxAdjValue.set("primaryGeoId", taxAuthGeoId);
        taxAdjValue.set("comments", taxAuthorityRateProduct.getString("description"));
        if (taxAuthPartyId != null) taxAdjValue.set("taxAuthPartyId", taxAuthPartyId);
        if (taxAuthGlAccountId != null) taxAdjValue.set("overrideGlAccountId", taxAuthGlAccountId);
        if (taxAuthGeoId != null) taxAdjValue.set("taxAuthGeoId", taxAuthGeoId);

        // check to see if this party has a tax ID for this, and if the party is tax exempt in the
        // primary (most-local) jurisdiction
        if (UtilValidate.isNotEmpty(billToPartyId) && UtilValidate.isNotEmpty(taxAuthGeoId)) {
          // see if partyId is a member of any groups, if so honor their tax exemptions
          // look for PartyRelationship with partyRelationshipTypeId=GROUP_ROLLUP, the partyIdTo is
          // the group member, so the partyIdFrom is the groupPartyId
          Set<String> billToPartyIdSet = FastSet.newInstance();
          billToPartyIdSet.add(billToPartyId);
          List<GenericValue> partyRelationshipList =
              EntityUtil.filterByDate(
                  delegator.findByAndCache(
                      "PartyRelationship",
                      UtilMisc.toMap(
                          "partyIdTo", billToPartyId, "partyRelationshipTypeId", "GROUP_ROLLUP")),
                  true);
          for (GenericValue partyRelationship : partyRelationshipList) {
            billToPartyIdSet.add(partyRelationship.getString("partyIdFrom"));
          }
          handlePartyTaxExempt(
              taxAdjValue,
              billToPartyIdSet,
              taxAuthGeoId,
              taxAuthPartyId,
              taxAmount,
              nowTimestamp,
              delegator);
        } else {
          Debug.logInfo(
              "NOTE: A tax calculation was done without a billToPartyId or taxAuthGeoId, so no tax exemptions or tax IDs considered; billToPartyId=["
                  + billToPartyId
                  + "] taxAuthGeoId=["
                  + taxAuthGeoId
                  + "]",
              module);
        }

        adjustments.add(taxAdjValue);

        if (productPrice != null
            && itemQuantity != null
            && productPrice.getBigDecimal("priceWithTax") != null
            && !"Y".equals(productPrice.getString("taxInPrice"))) {
          BigDecimal priceWithTax = productPrice.getBigDecimal("priceWithTax");
          BigDecimal price = productPrice.getBigDecimal("price");
          BigDecimal baseSubtotal = price.multiply(itemQuantity);
          BigDecimal baseTaxAmount =
              (baseSubtotal.multiply(taxRate))
                  .divide(PERCENT_SCALE, salestaxCalcDecimals, salestaxRounding);
          // Debug.logInfo("=================== priceWithTax=" + priceWithTax, module);
          // Debug.logInfo("=================== enteredTotalPriceWithTax=" +
          // enteredTotalPriceWithTax, module);
          // Debug.logInfo("=================== calcedTotalPriceWithTax=" + calcedTotalPriceWithTax,
          // module);

          // tax is not already in price so we want to add it in, but this is a VAT situation so
          // adjust to make it as accurate as possible

          // for VAT taxes if the calculated total item price plus calculated taxes is different
          // from what would be
          // expected based on the original entered price with taxes (if the price was entered this
          // way), then create
          // an adjustment that corrects for the difference, and this correction will be effectively
          // subtracted from the
          // price and not from the tax (the tax is meant to be calculated based on Tax Authority
          // rules and so should
          // not be shorted)

          // TODO (don't think this is needed, but just to keep it in mind): get this to work with
          // multiple VAT tax authorities instead of just one (right now will get incorrect totals
          // if there are multiple taxes included in the price)
          // TODO add constraint to ProductPrice lookup by any productStoreGroupId associated with
          // the current productStore

          BigDecimal enteredTotalPriceWithTax = priceWithTax.multiply(itemQuantity);
          BigDecimal calcedTotalPriceWithTax = (baseSubtotal).add(baseTaxAmount);
          if (!enteredTotalPriceWithTax.equals(calcedTotalPriceWithTax)) {
            // if the calced amount is higher than the entered amount we want the value to be
            // negative
            //     to get it down to match the entered amount
            // so, subtract the calced amount from the entered amount (ie: correction = entered -
            // calced)
            BigDecimal correctionAmount =
                enteredTotalPriceWithTax.subtract(calcedTotalPriceWithTax);
            // Debug.logInfo("=================== correctionAmount=" + correctionAmount, module);

            GenericValue correctionAdjValue = delegator.makeValue("OrderAdjustment");
            correctionAdjValue.set(
                "taxAuthorityRateSeqId",
                taxAuthorityRateProduct.getString("taxAuthorityRateSeqId"));
            correctionAdjValue.set("amount", correctionAmount);
            // don't set this, causes a doubling of the tax rate because calling code adds up all
            // tax rates: correctionAdjValue.set("sourcePercentage", taxRate);
            correctionAdjValue.set("orderAdjustmentTypeId", "VAT_PRICE_CORRECT");
            // the primary Geo should be the main jurisdiction that the tax is for, and the
            // secondary would just be to define a parent or wrapping jurisdiction of the primary
            correctionAdjValue.set("primaryGeoId", taxAuthGeoId);
            correctionAdjValue.set("comments", taxAuthorityRateProduct.getString("description"));
            if (taxAuthPartyId != null) correctionAdjValue.set("taxAuthPartyId", taxAuthPartyId);
            if (taxAuthGlAccountId != null)
              correctionAdjValue.set("overrideGlAccountId", taxAuthGlAccountId);
            if (taxAuthGeoId != null) correctionAdjValue.set("taxAuthGeoId", taxAuthGeoId);
            adjustments.add(correctionAdjValue);
          }
        }
      }
    } catch (GenericEntityException e) {
      Debug.logError(e, "Problems looking up tax rates", module);
      return FastList.newInstance();
    }

    return adjustments;
  }