This is an automated email from the ASF dual-hosted git repository.

diveshdut pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 74ca1cfb24 OFBIZ-13451: Apply supplier lead time to purchased MRP 
requirements
74ca1cfb24 is described below

commit 74ca1cfb246a219c8b215c06a48fc3bedb49f1ac
Author: Divesh Dutta <[email protected]>
AuthorDate: Mon Jul 6 12:28:07 2026 +0530

    OFBIZ-13451: Apply supplier lead time to purchased MRP requirements
    
    ## Summary
    
    This change improves MRP scheduling for purchased items by back-scheduling 
`requirementStartDate` using supplier lead time when that data is maintained.
    
    Previously, purchased requirements were effectively created with 
`requirementStartDate = requiredByDate`, so MRP did not indicate when 
procurement needed to begin in order to meet the required date.
    
    ## What changed
    
    - Added supplier lead time resolution for purchased-item MRP planning in 
`MrpServices`
    - Fetched only active `SupplierProduct` records effective for the 
requirement date
    - Ignored supplier rows that do not define `standardLeadTimeDays`
    - Preferred an active primary supplier (`supplierPrefOrderId = 
10_MAIN_SUPPL`) when lead time is available
    - If no active primary supplier with lead time exists, fell back to the 
active supplier with the earliest `availableFromDate`
    - Used `supplierPrefOrderId` and then `partyId` as deterministic 
tie-breakers
    - Used the resolved `SupplierProduct.standardLeadTimeDays` to back-schedule 
the purchased requirement start date from `requiredByDate`
    - Preserved current behavior when no applicable active supplier lead time 
is found
    - Kept `ProductFacility.daysToShip` out of the procurement fallback logic
    - Left the existing built-vs-bought decision logic unchanged
    
    ## Behavior
    
    For purchased items:
    
    - If an active supplier with defined `standardLeadTimeDays` is found, 
`requirementStartDate` is calculated as `requiredByDate - leadTimeDays`
    - If no applicable supplier lead time is found, `requirementStartDate` 
remains equal to `requiredByDate`
    
    Manufactured-item routing behavior is unchanged.
---
 .../ofbiz/manufacturing/mrp/MrpServices.java       | 49 +++++++++++++++++++++-
 .../ofbiz/manufacturing/mrp/ProposedOrder.java     | 14 +++++--
 2 files changed, 59 insertions(+), 4 deletions(-)

diff --git 
a/applications/manufacturing/src/main/java/org/apache/ofbiz/manufacturing/mrp/MrpServices.java
 
b/applications/manufacturing/src/main/java/org/apache/ofbiz/manufacturing/mrp/MrpServices.java
index 53234cd58f..50d4d6d276 100644
--- 
a/applications/manufacturing/src/main/java/org/apache/ofbiz/manufacturing/mrp/MrpServices.java
+++ 
b/applications/manufacturing/src/main/java/org/apache/ofbiz/manufacturing/mrp/MrpServices.java
@@ -58,6 +58,7 @@ public class MrpServices {
 
     private static final String MODULE = MrpServices.class.getName();
     private static final String RESOURCE = "ManufacturingUiLabels";
+    private static final String MAIN_SUPPLIER_PREF_ORDER_ID = "10_MAIN_SUPPL";
 
     public static Map<String, Object> initMrpEvents(DispatchContext ctx, 
Map<String, ? extends Object> context) {
         Delegator delegator = ctx.getDelegator();
@@ -542,6 +543,41 @@ public class MrpServices {
         return ((BigDecimal) resultMap.get("quantityOnHandTotal"));
     }
 
+    private static int getSupplierProductLeadTimeDays(Delegator delegator, 
String productId, Timestamp effectiveDate)
+            throws GenericEntityException {
+        EntityQuery supplierProductQuery = EntityQuery.use(delegator)
+                .from("SupplierProduct")
+                .where("productId", productId)
+                .orderBy("availableFromDate", "supplierPrefOrderId", 
"partyId");
+        if (effectiveDate != null) {
+            supplierProductQuery.filterByDate(effectiveDate, 
"availableFromDate", "availableThruDate");
+        } else {
+            supplierProductQuery.filterByDate();
+        }
+
+        List<GenericValue> supplierProducts = supplierProductQuery.queryList();
+        if (UtilValidate.isEmpty(supplierProducts)) {
+            return 0;
+        }
+        GenericValue fallbackSupplierProduct = null;
+        for (GenericValue supplierProduct : supplierProducts) {
+            BigDecimal standardLeadTimeDays = 
supplierProduct.getBigDecimal("standardLeadTimeDays");
+            if (UtilValidate.isEmpty(standardLeadTimeDays)) {
+                continue;
+            }
+            if 
(MAIN_SUPPLIER_PREF_ORDER_ID.equals(supplierProduct.getString("supplierPrefOrderId")))
 {
+                return standardLeadTimeDays.intValue();
+            }
+            if (fallbackSupplierProduct == null) {
+                fallbackSupplierProduct = supplierProduct;
+            }
+        }
+
+        return fallbackSupplierProduct != null
+                ? 
fallbackSupplierProduct.getBigDecimal("standardLeadTimeDays").intValue()
+                : 0;
+    }
+
     public static void logMrpError(String mrpId, String productId, String 
errorMessage, Delegator delegator) {
         logMrpError(mrpId, productId, UtilDateTime.nowTimestamp(), 
errorMessage, delegator);
     }
@@ -844,8 +880,19 @@ public class MrpServices {
                         }
                         // 
#####################################################
 
+                        int startDateOffsetDays = daysToShip;
+                        if (!isBuilt) {
+                            try {
+                                startDateOffsetDays = 
getSupplierProductLeadTimeDays(delegator, product.getString("productId"), 
eventDate);
+                            } catch (GenericEntityException e) {
+                                return 
ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, 
"ManufacturingMrpCannotFindProductForEvent",
+                                        locale));
+                            }
+                        }
+
                         // calculate the ProposedOrder requirementStartDate 
and update the requirementStartDate object property.
-                        Map<String, Object> routingTaskStartDate = 
proposedOrder.calculateStartDate(daysToShip, routing, delegator, dispatcher,
+                        Map<String, Object> routingTaskStartDate = 
proposedOrder.calculateStartDate(startDateOffsetDays, routing, delegator,
+                                dispatcher,
                                 userLogin);
                         if (isBuilt) {
                             // process the product components
diff --git 
a/applications/manufacturing/src/main/java/org/apache/ofbiz/manufacturing/mrp/ProposedOrder.java
 
b/applications/manufacturing/src/main/java/org/apache/ofbiz/manufacturing/mrp/ProposedOrder.java
index bf1ceab2ca..896841ab72 100644
--- 
a/applications/manufacturing/src/main/java/org/apache/ofbiz/manufacturing/mrp/ProposedOrder.java
+++ 
b/applications/manufacturing/src/main/java/org/apache/ofbiz/manufacturing/mrp/ProposedOrder.java
@@ -28,11 +28,13 @@ import java.util.ListIterator;
 import java.util.Map;
 
 import org.apache.ofbiz.base.util.Debug;
+import org.apache.ofbiz.base.util.UtilDateTime;
 import org.apache.ofbiz.base.util.UtilGenerics;
 import org.apache.ofbiz.base.util.UtilMisc;
 import org.apache.ofbiz.entity.Delegator;
 import org.apache.ofbiz.entity.GenericEntityException;
 import org.apache.ofbiz.entity.GenericValue;
+import org.apache.ofbiz.entity.util.EntityQuery;
 import org.apache.ofbiz.entity.util.EntityUtil;
 import org.apache.ofbiz.manufacturing.bom.BOMNode;
 import org.apache.ofbiz.manufacturing.bom.BOMTree;
@@ -202,9 +204,15 @@ public class ProposedOrder {
             // the product is purchased
             // TODO: REVIEW this code
             try {
-                GenericValue techDataCalendar = 
product.getDelegator().findOne("TechDataCalendar", UtilMisc.toMap("calendarId",
-                        "SUPPLIER"), true);
-                startDate = TechDataServices.addBackward(techDataCalendar, 
endDate, timeToShip);
+                GenericValue techDataCalendar = 
EntityQuery.use(product.getDelegator())
+                        .from("TechDataCalendar")
+                        .where("calendarId", "SUPPLIER")
+                        .queryOne();
+                if (techDataCalendar != null) {
+                    startDate = TechDataServices.addBackward(techDataCalendar, 
endDate, timeToShip);
+                } else {
+                    startDate = UtilDateTime.addDaysToTimestamp(endDate, 
-daysToShip);
+                }
             } catch (GenericEntityException e) {
                 Debug.logError(e, "Error : reading SUPPLIER TechDataCalendar: 
" + e.getMessage(), MODULE);
             }

Reply via email to