Project:
View Issue Details[ Jump to Notes ] | [ Issue History ] [ Print ] | |||||||||||
ID | ||||||||||||
0048120 | ||||||||||||
Type | Category | Severity | Reproducibility | Date Submitted | Last Update | |||||||
feature request | [Retail Modules] Web POS | minor | always | 2021-11-22 12:51 | 2021-11-23 08:54 | |||||||
Reporter | jetxarri | View Status | public | |||||||||
Assigned To | Retail | |||||||||||
Priority | normal | Resolution | open | Fixed in Version | ||||||||
Status | new | Fix in branch | Fixed in SCM revision | |||||||||
Projection | none | ETA | none | Target Version | RR20Q3.4 | |||||||
OS | Any | Database | Any | Java version | ||||||||
OS Version | Database version | Ant version | ||||||||||
Product Version | SCM revision | |||||||||||
Merge Request Status | ||||||||||||
Review Assigned To | ||||||||||||
OBNetwork customer | No | |||||||||||
Support ticket | ||||||||||||
Regression level | ||||||||||||
Regression date | ||||||||||||
Regression introduced in release | ||||||||||||
Regression introduced by commit | ||||||||||||
Triggers an Emergency Pack | No | |||||||||||
Summary | 0048120: Improve Discount endpoint performance creating a cache of the rules | |||||||||||
Description | Improve Discount endpoint performance creating a cache of the rules. In every request it is necessary to create rule object with all the properties like (products, productcategories, characteristics ....) so it will be interesting to cache all the rules and do not need to recalculate data. | |||||||||||
Steps To Reproduce | -Do a request to endpoint discount.discount -The time of each request with big amount of discounts should improve and it does not improve as much as expected | |||||||||||
Proposed Solution | Attached a patch. Limitations: To update the cache is taking only the updated of the header of the discount. Also it creates a preference with value of the cache Maximum time. Currently to 0 to maintain standard behaviour | |||||||||||
Tags | NOR | |||||||||||
Attached Files | ![]() From 0c6586a9b7dbc74fec9e1a90d6e8479fa4efc1fb Mon Sep 17 00:00:00 2001 From: Javier Etxarri <javier.echarri@openbravo.com> Date: Mon, 22 Nov 2021 13:00:54 +0100 Subject: [PATCH] Fixes issue-48120: Improve Discount endpoint performance creating a cache of the rules --- src-db/database/sourcedata/AD_PREFERENCE.xml | 14 + src-db/database/sourcedata/AD_REF_LIST.xml | 14 + .../discounts/engine/DiscountJSExecutor.java | 70 +++ .../engine/DiscountJSExecutor.java.orig | 548 ++++++++++++++++++ .../engine/DiscountJSExecutor.java.rej | 17 + 5 files changed, 663 insertions(+) create mode 100644 src-db/database/sourcedata/AD_PREFERENCE.xml create mode 100644 src-db/database/sourcedata/AD_REF_LIST.xml create mode 100644 src/org/openbravo/discounts/engine/DiscountJSExecutor.java.orig create mode 100644 src/org/openbravo/discounts/engine/DiscountJSExecutor.java.rej diff --git a/src-db/database/sourcedata/AD_PREFERENCE.xml b/src-db/database/sourcedata/AD_PREFERENCE.xml new file mode 100644 index 0000000..c3731b8 --- /dev/null +++ b/src-db/database/sourcedata/AD_PREFERENCE.xml @@ -0,0 +1,14 @@ +<?xml version='1.0' encoding='UTF-8'?> +<data> +<!--5A73DD8743434202B2116B98D5EC13B3--><AD_PREFERENCE> +<!--5A73DD8743434202B2116B98D5EC13B3--> <AD_PREFERENCE_ID><![CDATA[5A73DD8743434202B2116B98D5EC13B3]]></AD_PREFERENCE_ID> +<!--5A73DD8743434202B2116B98D5EC13B3--> <AD_CLIENT_ID><![CDATA[0]]></AD_CLIENT_ID> +<!--5A73DD8743434202B2116B98D5EC13B3--> <AD_ORG_ID><![CDATA[0]]></AD_ORG_ID> +<!--5A73DD8743434202B2116B98D5EC13B3--> <ISACTIVE><![CDATA[Y]]></ISACTIVE> +<!--5A73DD8743434202B2116B98D5EC13B3--> <VALUE><![CDATA[0]]></VALUE> +<!--5A73DD8743434202B2116B98D5EC13B3--> <PROPERTY><![CDATA[OBDISBE_RulesCacheTime]]></PROPERTY> +<!--5A73DD8743434202B2116B98D5EC13B3--> <ISPROPERTYLIST><![CDATA[Y]]></ISPROPERTYLIST> +<!--5A73DD8743434202B2116B98D5EC13B3--> <AD_MODULE_ID><![CDATA[B86B05B713C746D89B40EB5E09046E52]]></AD_MODULE_ID> +<!--5A73DD8743434202B2116B98D5EC13B3--></AD_PREFERENCE> + +</data> diff --git a/src-db/database/sourcedata/AD_REF_LIST.xml b/src-db/database/sourcedata/AD_REF_LIST.xml new file mode 100644 index 0000000..6b5b016 --- /dev/null +++ b/src-db/database/sourcedata/AD_REF_LIST.xml @@ -0,0 +1,14 @@ +<?xml version='1.0' encoding='UTF-8'?> +<data> +<!--75209391828342CF869DAAA65EACC1BE--><AD_REF_LIST> +<!--75209391828342CF869DAAA65EACC1BE--> <AD_REF_LIST_ID><![CDATA[75209391828342CF869DAAA65EACC1BE]]></AD_REF_LIST_ID> +<!--75209391828342CF869DAAA65EACC1BE--> <AD_CLIENT_ID><![CDATA[0]]></AD_CLIENT_ID> +<!--75209391828342CF869DAAA65EACC1BE--> <AD_ORG_ID><![CDATA[0]]></AD_ORG_ID> +<!--75209391828342CF869DAAA65EACC1BE--> <ISACTIVE><![CDATA[Y]]></ISACTIVE> +<!--75209391828342CF869DAAA65EACC1BE--> <VALUE><![CDATA[OBDISBE_RulesCacheTime]]></VALUE> +<!--75209391828342CF869DAAA65EACC1BE--> <NAME><![CDATA[Discount Rules Cache Max Duration]]></NAME> +<!--75209391828342CF869DAAA65EACC1BE--> <AD_REFERENCE_ID><![CDATA[A26BA480E2014707B47257024C3CBFF7]]></AD_REFERENCE_ID> +<!--75209391828342CF869DAAA65EACC1BE--> <AD_MODULE_ID><![CDATA[B86B05B713C746D89B40EB5E09046E52]]></AD_MODULE_ID> +<!--75209391828342CF869DAAA65EACC1BE--></AD_REF_LIST> + +</data> diff --git a/src/org/openbravo/discounts/engine/DiscountJSExecutor.java b/src/org/openbravo/discounts/engine/DiscountJSExecutor.java index bf5655a..bd0d50d 100644 --- a/src/org/openbravo/discounts/engine/DiscountJSExecutor.java +++ b/src/org/openbravo/discounts/engine/DiscountJSExecutor.java @@ -17,12 +17,14 @@ import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -47,6 +49,8 @@ import org.openbravo.dal.core.OBContext; import org.openbravo.dal.service.OBDal; import org.openbravo.discounts.DiscountsComponent; import org.openbravo.discounts.api.DiscountsTicket; +import org.openbravo.erpCommon.businessUtility.Preferences; +import org.openbravo.erpCommon.utility.PropertyException; import org.openbravo.model.pricing.priceadjustment.BusinessPartner; import org.openbravo.model.pricing.priceadjustment.BusinessPartnerGroup; import org.openbravo.model.pricing.priceadjustment.Characteristic; @@ -68,6 +72,13 @@ import org.openbravo.service.json.DataToJsonConverter; public abstract class DiscountJSExecutor implements DiscountExecutor { private static final Logger log = LogManager.getLogger(); + private static HashMap<String, JSONObject> rulesCache = new HashMap<String, JSONObject>(); + private static Date rulesCacheDate = new Date(); + + final static SimpleDateFormat dtFormat = createOrderLoaderDateTimeFormat(); + + private static final String OBDISBE_RULESCACHETIME = "OBDISBE_RulesCacheTime"; + @Inject private ApplicationDictionaryCachedStructures adcs; @@ -146,6 +157,12 @@ public abstract class DiscountJSExecutor implements DiscountExecutor { init(); } long t = System.currentTimeMillis(); + Optional<HashMap<String, JSONObject>> newCache = resetCacheIfItIsNecessary( + getDiscountRulesCacheMaxDurationValue(), rulesCacheDate); + if (!newCache.isEmpty()) { + rulesCache = newCache.get(); + } + final List<JSONObject> rules = getDiscountRules(ticket); final JSONObject bpSets = getBPSets(ticket); log.debug("Calculated {} discount rules for ticket {} in {} ms", rules.size(), ticket, @@ -153,6 +170,33 @@ public abstract class DiscountJSExecutor implements DiscountExecutor { return invokeJSDiscountCalculation(ticket, rules, bpSets); } + private Optional<HashMap<String, JSONObject>> resetCacheIfItIsNecessary(long maxCacheDuration, + Date currentCacheInitialized) { + Date currentDate = new Date(); + + if (currentDate.getTime() - currentCacheInitialized.getTime() > maxCacheDuration * 60 * 60 + * 1000) { + return Optional.of(new HashMap<String, JSONObject>()); + } + + return Optional.empty(); + } + + private static long getDiscountRulesCacheMaxDurationValue() { + String maxCacheTime = "0"; + try { + maxCacheTime = Preferences.getPreferenceValue(OBDISBE_RULESCACHETIME, true, + OBContext.getOBContext().getCurrentClient(), + OBContext.getOBContext().getCurrentOrganization(), OBContext.getOBContext().getUser(), + OBContext.getOBContext().getRole(), null); + } catch (PropertyException pe) { + // Pref not defined + log.debug("Not possible to recover preference Discount Rules Cache Max Duration"); + } + // return 0L; + return Long.parseLong(maxCacheTime.trim(), 10); + } + /** * Returns the discount rules that can be applied for a given ticket. * @@ -285,6 +329,24 @@ public abstract class DiscountJSExecutor implements DiscountExecutor { .get(PriceAdjustment.class, discountRule.getString("id")); JSONObject jsonObject = null; + if (rulesCache.containsKey(discountRule.getString("id"))) { + Date updated = pa.getUpdated(); + JSONObject rule = rulesCache.get(discountRule.getString("id")); + + Date cachedRuleUpdated; + try { + cachedRuleUpdated = dtFormat.parse(rule.getString("updated")); + } catch (Exception e) { + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.YEAR, -1); + cachedRuleUpdated = cal.getTime(); + } + + if (updated.getTime() / 1000 <= cachedRuleUpdated.getTime() / 1000) { + return rule; + } + } + JSONArray cbpartners = new JSONArray(); for (BusinessPartner bp : pa.getPricingAdjustmentBusinessPartnerList()) { jsonObject = getBusinessPartnerInfo(bp); @@ -355,6 +417,8 @@ public abstract class DiscountJSExecutor implements DiscountExecutor { .stream() .forEach(hook -> hook.addDataToDiscount(pa, discountRule)); + rulesCache.put(discountRule.getString("id"), discountRule); + } catch (JSONException e) { log.error("Couldn't calculate discount rules", e); } @@ -527,6 +591,12 @@ public abstract class DiscountJSExecutor implements DiscountExecutor { String value(); } + private static SimpleDateFormat createOrderLoaderDateTimeFormat() { + final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX"); + dateFormat.setLenient(true); + return dateFormat; + } + /** * A class used to select a DiscountJSExecutor instance. */ diff --git a/src/org/openbravo/discounts/engine/DiscountJSExecutor.java.orig b/src/org/openbravo/discounts/engine/DiscountJSExecutor.java.orig new file mode 100644 index 0000000..bf5655a --- /dev/null +++ b/src/org/openbravo/discounts/engine/DiscountJSExecutor.java.orig @@ -0,0 +1,548 @@ +/* + ************************************************************************************ + * Copyright (C) 2019-2021 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. + ************************************************************************************ + */ +package org.openbravo.discounts.engine; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.annotation.PostConstruct; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Inject; +import javax.servlet.ServletContext; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.codehaus.jettison.json.JSONArray; +import org.codehaus.jettison.json.JSONException; +import org.codehaus.jettison.json.JSONObject; +import org.hibernate.query.Query; +import org.openbravo.api.ticket.TicketResult; +import org.openbravo.base.provider.OBProvider; +import org.openbravo.client.application.window.ApplicationDictionaryCachedStructures; +import org.openbravo.client.kernel.KernelConstants; +import org.openbravo.dal.core.DalContextListener; +import org.openbravo.dal.core.OBContext; +import org.openbravo.dal.service.OBDal; +import org.openbravo.discounts.DiscountsComponent; +import org.openbravo.discounts.api.DiscountsTicket; +import org.openbravo.model.pricing.priceadjustment.BusinessPartner; +import org.openbravo.model.pricing.priceadjustment.BusinessPartnerGroup; +import org.openbravo.model.pricing.priceadjustment.Characteristic; +import org.openbravo.model.pricing.priceadjustment.DiscountBusinessPartnerSet; +import org.openbravo.model.pricing.priceadjustment.PriceAdjustment; +import org.openbravo.model.pricing.priceadjustment.PriceList; +import org.openbravo.model.pricing.priceadjustment.Product; +import org.openbravo.model.pricing.priceadjustment.ProductCategory; +import org.openbravo.retail.discounts.DiscountDataHookSelector; +import org.openbravo.service.json.DataToJsonConverter; + +/** + * A DiscountExecutor that implements the discount calculation logic using the Javascript language. + * + * Classes extending this one can decide how to invoke the discount calculation logic and the + * Javascript engine to be used to execute the code. + */ +@ApplicationScoped +public abstract class DiscountJSExecutor implements DiscountExecutor { + private static final Logger log = LogManager.getLogger(); + + @Inject + private ApplicationDictionaryCachedStructures adcs; + + @Inject + private DiscountsComponent discountsComponent; + + @Inject + private DiscountDataHookSelector dataHookSelector; + + @PostConstruct + public void init() { + doInit(); + } + + /** + * Initializes this Javascript DiscountExecutor. + */ + public abstract void doInit(); + + /** + * Invokes the discounts calculation. + * + * @param ticket + * The ticket whose discounts should be calculated. + * @param rules + * The list of applicable discount rules. + * + * @return a TicketResult with the calculated discounts for the given ticket. + */ + public abstract TicketResult invokeJSDiscountCalculation(DiscountsTicket ticket, + List<JSONObject> rules); + + /** + * Invokes the discounts calculation. + * + * @param ticket + * The ticket whose discounts should be calculated. + * @param rules + * The list of applicable discount rules. + * @param bpSets + * The list of applicable bpSets. + * + * @return a TicketResult with the calculated discounts for the given ticket. + */ + public abstract TicketResult invokeJSDiscountCalculation(DiscountsTicket ticket, + List<JSONObject> rules, JSONObject bpSets); + + /** + * @return the Path of the file that contains the Javascript code with the discounts calculation + * logic. + */ + public Path getDiscountsJavaScript() { + Map<String, Object> p = new HashMap<>(1); + ServletContext requestCtx = DalContextListener.getServletContext(); + p.put(KernelConstants.SERVLET_CONTEXT, requestCtx); + discountsComponent.setParameters(p); + String jsFileName = discountsComponent.getStaticResourceFileName() + ".js"; + String jsPath = requestCtx.getRealPath("web/js/gen/" + jsFileName); + log.debug("JS contents generated in {}", jsPath); + return Paths.get(jsPath); + } + + /** + * Calculates the discounts for the provided ticket, retrieving the applicable discount rules from + * database. + * + * @param ticket + * The ticket whose discounts should be calculated. + * + * @return a TicketResult with the calculated discounts for the given ticket. + */ + @Override + public TicketResult calculateDiscounts(DiscountsTicket ticket) { + if (adcs.isInDevelopment()) { + log.info("Re-initiliazing jsExecutor because of in development instance"); + init(); + } + long t = System.currentTimeMillis(); + final List<JSONObject> rules = getDiscountRules(ticket); + final JSONObject bpSets = getBPSets(ticket); + log.debug("Calculated {} discount rules for ticket {} in {} ms", rules.size(), ticket, + System.currentTimeMillis() - t); + return invokeJSDiscountCalculation(ticket, rules, bpSets); + } + + /** + * Returns the discount rules that can be applied for a given ticket. + * + * @param ticket + * The ticket whose applicable discounts will be retrieved. + * + * @return a list of JSONObjects representing the discount rules that can be applied for a given + * ticket. + */ + private List<JSONObject> getDiscountRules(DiscountsTicket ticket) { + String orgId = ticket.getOrganization().getId(); + Set<String> ticketProducts = getProducts(ticket); + Set<String> ticketProductCategories = getProductCategories(ticketProducts); + String bpId = ticket.getBusinessPartner().getId(); + String bpCatId = getBusinessPartnerCategory(bpId); + List<String> manualPromotions = getManualPromotions(); + Date orderDate = getOrderDateWithoutTime(ticket.getOrderDate()); + + // @formatter:off + String whereClause = "as pa where " + + " ((pa.includedOrganizations = 'N' and exists (select 1 from PricingAdjustmentOrganization po where po.priceAdjustment = pa and po.active = 'Y' and po.organization.id = :orgId))" + + " or (pa.includedOrganizations = 'Y' and not exists (select 1 from PricingAdjustmentOrganization po where po.priceAdjustment = pa and po.active = 'Y' and po.organization.id = :orgId)))" + + + " and ((pa.includedProducts = 'N' and exists (select 1 from PricingAdjustmentProduct pap where pap.priceAdjustment = pa and pap.active = 'Y' and pap.product.id in :products))" + + " or (pa.includedProducts = 'Y' and not exists (select 1 from PricingAdjustmentProduct pap where pap.priceAdjustment = pa and pap.active = 'Y' and pap.product.id in :products)))" + + + " and ((pa.includedProductCategories = 'N' and exists (select 1 from PricingAdjustmentProductCategory papc where papc.priceAdjustment = pa and papc.active = 'Y' and papc.productCategory.id in :prodCategories))" + + " or (pa.includedProductCategories = 'Y' and not exists (select 1 from PricingAdjustmentProductCategory papc where papc.priceAdjustment = pa and papc.active = 'Y' and papc.productCategory.id in :prodCategories)))" + + + " and ((pa.includedBusinessPartners = 'N' and exists (select 1 from PricingAdjustmentBusinessPartner pabp where pabp.priceAdjustment = pa and pabp.active = 'Y' and pabp.businessPartner.id = :bpId))" + + " or (pa.includedBusinessPartners = 'Y' and not exists (select 1 from PricingAdjustmentBusinessPartner pabp where pabp.priceAdjustment = pa and pabp.active = 'Y' and pabp.businessPartner.id = :bpId)))" + + + " and ((pa.includedBPCategories ='N' and exists (select 1 from PricingAdjustmentBusinessPartnerGroup pabc where pabc.priceAdjustment = pa and pabc.active = 'Y' and pabc.businessPartnerCategory.id = :bpCatId))" + + " or (pa.includedBPCategories ='Y' and not exists (select 1 from PricingAdjustmentBusinessPartnerGroup pabc where pabc.priceAdjustment = pa and pabc.active = 'Y' and pabc.businessPartnerCategory.id = :bpCatId)))" + + + " and pa.active = 'Y'" + + " and pa.organization.id in :orgIds " + + " and pa.discountType.id not in (:manualPromotions)" + + " and pa.startingDate <= :startingDate" + + " and (pa.endingDate is null" + + " or pa.endingDate >= :endingDate)" + + " order by pa.priority nulls first, pa.id"; + // @formatter:on + + List<PriceAdjustment> discountCandidates = OBDal.getInstance() + .createQuery(PriceAdjustment.class, whereClause) + .setNamedParameter("orgId", orgId) + .setNamedParameter("orgIds", getOrgTree(orgId)) + .setNamedParameter("products", ticketProducts) + .setNamedParameter("prodCategories", ticketProductCategories) + .setNamedParameter("bpId", bpId) + .setNamedParameter("bpCatId", bpCatId) + .setNamedParameter("manualPromotions", manualPromotions) + .setNamedParameter("startingDate", orderDate) + .setNamedParameter("endingDate", orderDate) + .list(); + + return transformDiscountRules(discountCandidates); + } + + /** + * Returns the application bpSets. + * + * @param ticket + * The ticket whose applicable discounts will be retrieved. + * + * @return a JSONObject representing the list of bpSets. + */ + private JSONObject getBPSets(DiscountsTicket ticket) { + JSONObject jsonBPSet = new JSONObject(); + try { + final String orgId = ticket.getOrganization().getId(); + final Date orderDate = getOrderDateWithoutTime(ticket.getOrderDate()); + final Set<String> naturalTreeOrgList = OBContext.getOBContext() + .getOrganizationStructureProvider() + .getNaturalTree(orgId); + + final String hqlQuery = "select c.id as id, c.bpSet.id as bpSet, c.businessPartner.id as businessPartner, " + + "c.startingDate as startingDate, c.endingDate as endingDate from BusinessPartnerSetLine c " + + "where c.bpSet.client.id = :clientId and c.bpSet.organization.id in (:orgList) and c.active = true and c.bpSet.active = true and c.businessPartner.active = true " + + "and (c.startingDate is null or c.startingDate <= :orderDate) and (c.endingDate is null or c.endingDate >= :orderDate) order by c.bpSet.id"; + final Query<Object[]> bpSetQuery = OBDal.getInstance() + .getSession() + .createQuery(hqlQuery, Object[].class); + bpSetQuery.setParameter("clientId", OBContext.getOBContext().getCurrentClient().getId()); + bpSetQuery.setParameter("orgList", naturalTreeOrgList); + bpSetQuery.setParameter("orderDate", orderDate); + final List<Object[]> bpSetList = bpSetQuery.list(); + if (bpSetList.size() > 0) { + for (Object[] bpSet : bpSetList) { + final String bpSetId = (String) bpSet[1]; + JSONArray bpSetJSONArray = null; + if (jsonBPSet.has(bpSetId)) { + bpSetJSONArray = jsonBPSet.getJSONArray(bpSetId); + } else { + bpSetJSONArray = new JSONArray(); + jsonBPSet.put(bpSetId, bpSetJSONArray); + } + JSONObject bpSetJSON = new JSONObject(); + bpSetJSON.put("id", (String) bpSet[0]); + bpSetJSON.put("bpSet", (String) bpSet[1]); + bpSetJSON.put("businessPartner", (String) bpSet[2]); + bpSetJSON.put("startingDate", (Date) bpSet[3]); + bpSetJSON.put("endingDate", (Date) bpSet[4]); + bpSetJSONArray.put(bpSetJSON); + } + } + } catch (JSONException e) { + log.error("Couldn't calculate discount rules", e); + } + return jsonBPSet; + } + + private List<JSONObject> transformDiscountRules(List<PriceAdjustment> rules) { + DataToJsonConverter toJsonConverter = OBProvider.getInstance().get(DataToJsonConverter.class); + return toJsonConverter.toJsonObjects(rules) + .stream() + .map(this::transformDiscountRule) + .collect(Collectors.toList()); + } + + /** + * Maps some properties and adds required information to the rule so that it can be processed by + * the JS discount engine. + */ + private JSONObject transformDiscountRule(JSONObject discountRule) { + try { + // Add required information + PriceAdjustment pa = OBDal.getInstance() + .get(PriceAdjustment.class, discountRule.getString("id")); + JSONObject jsonObject = null; + + JSONArray cbpartners = new JSONArray(); + for (BusinessPartner bp : pa.getPricingAdjustmentBusinessPartnerList()) { + jsonObject = getBusinessPartnerInfo(bp); + if (jsonObject != null) { + cbpartners.put(jsonObject); + } + } + discountRule.put("cbpartners", cbpartners); + + JSONArray cbpartnerGroups = new JSONArray(); + for (BusinessPartnerGroup bpg : pa.getPricingAdjustmentBusinessPartnerGroupList()) { + jsonObject = getBPGroupInfo(bpg); + if (jsonObject != null) { + cbpartnerGroups.put(jsonObject); + } + } + discountRule.put("cbpartnerGroups", cbpartnerGroups); + + JSONArray cbpartnerSets = new JSONArray(); + for (DiscountBusinessPartnerSet bps : pa.getPricingAdjustmentBusinessPartnerSetList()) { + jsonObject = getBPSetInfo(bps); + if (jsonObject != null) { + cbpartnerSets.put(jsonObject); + } + } + discountRule.put("cbpartnerSets", cbpartnerSets); + + JSONArray products = new JSONArray(); + for (Product p : pa.getPricingAdjustmentProductList()) { + jsonObject = getProductInfo(p); + if (jsonObject != null) { + products.put(jsonObject); + } + } + discountRule.put("products", products); + + JSONArray productCategories = new JSONArray(); + for (ProductCategory pc : pa.getPricingAdjustmentProductCategoryList()) { + jsonObject = getProductCategoryInfo(pc); + if (jsonObject != null) { + JSONObject category = new JSONObject(); + category.put("productCategory", jsonObject); + productCategories.put(category); + } + } + discountRule.put("productCategories", productCategories); + + JSONArray characteristics = new JSONArray(); + for (Characteristic c : pa.getPricingAdjustmentCharacteristicList()) { + jsonObject = getCharacteristicInfo(c); + if (jsonObject != null) { + characteristics.put(jsonObject); + } + } + discountRule.put("productCharacteristics", characteristics); + + JSONArray priceLists = new JSONArray(); + for (PriceList pl : pa.getPricingAdjustmentPriceListList()) { + jsonObject = getPriceListInfo(pl); + if (jsonObject != null) { + priceLists.put(jsonObject); + } + } + discountRule.put("pricelists", priceLists); + + // Add information from external discount definitions + dataHookSelector.getDiscountDataHooks(pa.getDiscountType().getId()) + .stream() + .forEach(hook -> hook.addDataToDiscount(pa, discountRule)); + + } catch (JSONException e) { + log.error("Couldn't calculate discount rules", e); + } + return discountRule; + } + + private JSONObject getBusinessPartnerInfo(BusinessPartner businessPartner) throws JSONException { + if (!businessPartner.isActive() || !businessPartner.getBusinessPartner().isActive()) { + return null; + } + JSONObject info = new JSONObject(); + info.put("id", businessPartner.getId()); + info.put("priceAdjustment", businessPartner.getPriceAdjustment().getId()); + info.put("_identifier", businessPartner.getIdentifier()); + JSONObject bpInfo = new JSONObject(); + bpInfo.put("id", businessPartner.getBusinessPartner().getId()); + info.put("businessPartner", bpInfo); + return info; + } + + private JSONObject getBPGroupInfo(BusinessPartnerGroup businessPartnerGroup) + throws JSONException { + if (!businessPartnerGroup.isActive() + || !businessPartnerGroup.getBusinessPartnerCategory().isActive()) { + return null; + } + JSONObject info = new JSONObject(); + info.put("id", businessPartnerGroup.getId()); + info.put("priceAdjustment", businessPartnerGroup.getPriceAdjustment().getId()); + info.put("_identifier", businessPartnerGroup.getIdentifier()); + JSONObject bpCategoryInfo = new JSONObject(); + bpCategoryInfo.put("id", businessPartnerGroup.getBusinessPartnerCategory().getId()); + info.put("businessPartnerCategory", bpCategoryInfo); + return info; + } + + private JSONObject getBPSetInfo(DiscountBusinessPartnerSet businessPartnerSet) + throws JSONException { + if (!businessPartnerSet.isActive() || !businessPartnerSet.getBpSet().isActive()) { + return null; + } + JSONObject info = new JSONObject(); + info.put("id", businessPartnerSet.getId()); + info.put("_identifier", businessPartnerSet.getIdentifier()); + info.put("bpSet", businessPartnerSet.getBpSet().getId()); + info.put("priceAdjustment", businessPartnerSet.getPromotionDiscount().getId()); + return info; + } + + private JSONObject getProductInfo(Product discountProduct) throws JSONException { + if (!discountProduct.isActive() || !discountProduct.getProduct().isActive()) { + return null; + } + JSONObject prodInfo = new JSONObject(); + JSONObject product = new JSONObject(); + product.put("id", discountProduct.getProduct().getId()); + product.put("_identifier", discountProduct.getProduct().getSearchKey()); + prodInfo.put("product", product); + + // Discount rule additional properties + prodInfo.put("obdiscIsGift", discountProduct.isObdiscIsGift()); + prodInfo.put("obdiscQty", discountProduct.getObdiscQty()); + prodInfo.put("obdiscGifqty", discountProduct.getObdiscGifqty()); + + return prodInfo; + } + + private JSONObject getProductCategoryInfo(ProductCategory discountProductCategory) + throws JSONException { + if (!discountProductCategory.isActive() + || !discountProductCategory.getProductCategory().isActive()) { + return null; + } + JSONObject prodInfo = new JSONObject(); + prodInfo.put("id", discountProductCategory.getProductCategory().getId()); + prodInfo.put("_identifier", discountProductCategory.getProductCategory().getSearchKey()); + return prodInfo; + } + + private JSONObject getCharacteristicInfo(Characteristic characteristic) throws JSONException { + if (!characteristic.isActive() || !characteristic.getCharacteristic().isActive()) { + return null; + } + JSONObject info = new JSONObject(); + info.put("id", characteristic.getId()); + info.put("_identifier", characteristic.getCharacteristic().getIdentifier()); + info.put("characteristic", characteristic.getCharacteristic().getId()); + info.put("chValue", characteristic.getChValue().getId()); + info.put("isincludecharacteristics", characteristic.isIncludecharacteristics()); + return info; + } + + private JSONObject getPriceListInfo(PriceList priceList) throws JSONException { + if (!priceList.isActive() || !priceList.getPriceList().isActive()) { + return null; + } + JSONObject priceListInfo = new JSONObject(); + priceListInfo.put("id", priceList.getId()); + priceListInfo.put("_identifier", priceList.getIdentifier()); + priceListInfo.put("priceAdjustment", priceList.getPriceAdjustment().getId()); + priceListInfo.put("m_pricelist_id", priceList.getPriceList().getId()); + return priceListInfo; + } + + private Set<String> getProducts(DiscountsTicket ticket) { + return ticket.getLines() + .stream() + .map(line -> line.getProduct().getId()) + .collect(Collectors.toSet()); + } + + private String getBusinessPartnerCategory(String businessPartnerId) { + String hql = "SELECT bp.businessPartnerCategory.id FROM BusinessPartner bp WHERE bp.id = :bpId"; + return OBDal.getInstance() + .getSession() + .createQuery(hql, String.class) + .setParameter("bpId", businessPartnerId) + .uniqueResult(); + } + + private Set<String> getProductCategories(Set<String> productIds) { + if (productIds.isEmpty()) { + return Collections.emptySet(); + } + String hql = "SELECT DISTINCT(p.productCategory.id) FROM Product p WHERE p.id IN (:productIds)"; + List<String> productCategories = OBDal.getInstance() + .getSession() + .createQuery(hql, String.class) + .setParameterList("productIds", new ArrayList<>(productIds)) + .list(); + return new HashSet<>(productCategories); + } + + private Date getOrderDateWithoutTime(final String orderDate) { + try { + final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + return dateFormat.parse(orderDate); + } catch (Exception e) { + log.error("Couldn't get order date", e); + } + return null; + } + + private List<String> getOrgTree(String organization) { + List<String> result = new ArrayList<String>(); + if (organization != null) { + result = OBContext.getOBContext() + .getOrganizationStructureProvider() + .getParentList(organization, true); + } + return result; + } + + private List<String> getManualPromotions() { + return new ArrayList<String>( + Arrays.asList("D1D193305A6443B09B299259493B272A", "F3B0FB45297844549D9E6B5F03B23A82", + "7B49D8CC4E084A75B7CB4D85A6A3A578", "20E4EC27397344309A2185097392D964", + "8338556C0FBF45249512DB343FEFD280", "ADF7E7F91B3A49869FB3F89B8C5A325E", + "096984DC2B944C85A9162C66C37EE7A3", "971642418DD24DE5BD860D63EF57D5F6", + "00535FB65D9941AE9575546FBAF11B95", "4776954A80E747C5BAA565AD464759BF")); + } + + /** + * Defines the qualifier used to register a DiscountJSExecutor instance. + */ + @javax.inject.Qualifier + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE }) + public @interface JSEngine { + String value(); + } + + /** + * A class used to select a DiscountJSExecutor instance. + */ + @SuppressWarnings("all") + public static class Selector extends AnnotationLiteral<JSEngine> implements JSEngine { + private static final long serialVersionUID = 1L; + + private final String value; + + public Selector(String value) { + this.value = value; + } + + @Override + public String value() { + return value; + } + } +} diff --git a/src/org/openbravo/discounts/engine/DiscountJSExecutor.java.rej b/src/org/openbravo/discounts/engine/DiscountJSExecutor.java.rej new file mode 100644 index 0000000..00fd325 --- /dev/null +++ b/src/org/openbravo/discounts/engine/DiscountJSExecutor.java.rej @@ -0,0 +1,17 @@ +*************** +*** 55,60 **** + import org.openbravo.discounts.api.DiscountsTicket; + import org.openbravo.discounts.engine.hook.DiscountRulesHook; + import org.openbravo.discounts.engine.hook.WhereClauseHook; + import org.openbravo.model.pricing.priceadjustment.BusinessPartner; + import org.openbravo.model.pricing.priceadjustment.BusinessPartnerGroup; + import org.openbravo.model.pricing.priceadjustment.Characteristic; +--- 57,64 ---- + import org.openbravo.discounts.api.DiscountsTicket; + import org.openbravo.discounts.engine.hook.DiscountRulesHook; + import org.openbravo.discounts.engine.hook.WhereClauseHook; ++ import org.openbravo.erpCommon.businessUtility.Preferences; ++ import org.openbravo.erpCommon.utility.PropertyException; + import org.openbravo.model.pricing.priceadjustment.BusinessPartner; + import org.openbravo.model.pricing.priceadjustment.BusinessPartnerGroup; + import org.openbravo.model.pricing.priceadjustment.Characteristic; -- 2.31.0 | |||||||||||
![]() |
|
![]() |
|||
Date Modified | Username | Field | Change |
2021-11-22 12:51 | jetxarri | New Issue | |
2021-11-22 12:51 | jetxarri | Assigned To | => Retail |
2021-11-22 12:51 | jetxarri | File Added: performance.diff | |
2021-11-22 12:51 | jetxarri | OBNetwork customer | => No |
2021-11-22 12:51 | jetxarri | Triggers an Emergency Pack | => No |
2021-11-22 12:56 | jetxarri | File Deleted: performance.diff | |
2021-11-22 13:02 | jetxarri | File Added: performance.diff | |
2021-11-23 08:54 | rafaroda | Tag Attached: NOR |
Copyright © 2000 - 2009 MantisBT Group |