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);
}