private Collection<I_PP_MRP> getPP_MRPs_ToUse() { final Map<Integer, I_PP_MRP> mrps = new LinkedHashMap<>(); // // Get MRP records that were explicitelly set for (final I_PP_MRP mrp : _mrps) { mrps.put(mrp.getPP_MRP_ID(), mrp); } // If we have MRP records explicitelly assigned, there is no point checking the MRP Context // because developer specified them and the ones from MRP context could be not relevant if (!mrps.isEmpty()) { return mrps.values(); } // // Get MRP records from context final IMRPContext mrpContext = getMRPContext(); if (mrpContext != null) { final I_PP_MRP mrp = mrpContext.getPP_MRP(); if (mrp != null) { mrps.put(mrp.getPP_MRP_ID(), mrp); } } return mrps.values(); }
private I_PP_Product_Planning getProductPlanning_ToUse() { final IMRPContext mrpContext = getMRPContext(); if (mrpContext != null) { return mrpContext.getProductPlanning(); } return null; }
private final Properties getCtx() { final IMRPContext mrpContext = getMRPContext(); if (mrpContext != null) { return mrpContext.getCtx(); } return Env.getCtx(); }
private I_M_Product getM_Product_ToUse() { if (_product != null) { return _product; } final IMRPContext mrpContext = getMRPContext(); if (mrpContext != null) { return mrpContext.getM_Product(); } return null; }
private I_AD_Org getAD_Org_ToUse() { if (_org != null) { return _org; } final IMRPContext mrpContext = getMRPContext(); if (mrpContext != null) { return mrpContext.getAD_Org(); } return null; }
private I_M_Warehouse getM_Warehouse_ToUse() { if (_warehouse != null) { return _warehouse; } final IMRPContext mrpContext = getMRPContext(); if (mrpContext != null) { return mrpContext.getM_Warehouse(); } return null; }
private I_S_Resource getPlant_ToUse() { if (_plant != null) { return _plant; } final IMRPContext mrpContext = getMRPContext(); if (mrpContext != null) { return mrpContext.getPlant(); } return null; }
private int getAD_Client_ID_ToUse() { if (_adClientId != null) { return _adClientId; } else if (_mrpContext != null) { return _mrpContext.getAD_Client_ID(); } return -1; }
private int getM_Product_ID_ToUse() { if (_productId != null) { return _productId; } else if (_mrpContext != null) { return _mrpContext.getM_Product_ID(); } return -1; }
@Override public boolean applies(final IMRPContext mrpContext, final IMutable<String> notAppliesReason) { if (!mrpContext.isRequireDRP()) { notAppliesReason.setValue("DRP not enabled"); return false; } // Check distribution network final I_PP_Product_Planning productPlanning = mrpContext.getProductPlanning(); if (productPlanning.getDD_NetworkDistribution_ID() <= 0) { notAppliesReason.setValue( "No distribution network configured in product data planning: " + productPlanning); return false; } // If nothing else is preventing as, consider that we can do DRP return true; }
private int getAD_Org_ID_ToUse() { if (_adOrgId != null) { return _adOrgId; } else if (_mrpContext != null) { final I_AD_Org org = _mrpContext.getAD_Org(); if (org != null) { return org.getAD_Org_ID(); } } return -1; }
private int getM_Warehouse_ID_ToUse() { if (_warehouseId != null) { return _warehouseId; } else if (_mrpContext != null) { final I_M_Warehouse warehouse = _mrpContext.getM_Warehouse(); if (warehouse != null) { return warehouse.getM_Warehouse_ID(); } } return -1; }
@Override public void cleanup(final IMRPContext mrpContext, final IMRPExecutor executor) { // If DRP module is not activated, then skip the cleanup if (!mrpContext.isRequireDRP()) { return; } final IQueryBL queryBL = Services.get(IQueryBL.class); // // Delete generated distribution orders // (i.e. Distribution Order with Draft Status) final ICompositeQueryFilter<I_DD_Order> filters = queryBL.createCompositeQueryFilter(I_DD_Order.class); filters.addEqualsFilter(I_DD_Order.COLUMNNAME_DocStatus, X_DD_Order.DOCSTATUS_Drafted); // // Only those which were generated by MRP filters.addEqualsFilter(I_DD_Order.COLUMN_MRP_Generated, true); // Only those which are allowed to be deleted by MRP cleanup filters.addEqualsFilter(I_DD_Order.COLUMN_MRP_AllowCleanup, true); // // Only for our AD_Client_ID filters.addEqualsFilter(I_DD_Order.COLUMNNAME_AD_Client_ID, mrpContext.getAD_Client_ID()); // // Only for our AD_Org_ID filters.addEqualsFilter(I_DD_Order.COLUMNNAME_AD_Org_ID, mrpContext.getAD_Org().getAD_Org_ID()); // // Only those DD Orders which are from our Plant or does not have a plant at all filters.addInArrayFilter( I_DD_Order.COLUMNNAME_PP_Plant_ID, null, mrpContext.getPlant().getS_Resource_ID()); // // Only those which have a line with Destination Warehouse same as our warehouse final int targetWarehouseId = mrpContext.getM_Warehouse().getM_Warehouse_ID(); filters.addFilter( Services.get(IDDOrderDAO.class) .getDDOrdersForTargetWarehouseQueryFilter(targetWarehouseId)); // // If we are running in an constrained MRP Context, filter only those documents if (mrpContext.getEnforced_PP_MRP_Demand_ID() > 0) { final IQuery<I_PP_MRP> mrpQuery = createMRPQueryBuilderForCleanup(mrpContext, executor) .createQueryBuilder() .addEqualsFilter(I_PP_MRP.COLUMN_TypeMRP, X_PP_MRP.TYPEMRP_Supply) .create(); filters.addInSubQueryFilter( I_DD_Order.COLUMN_DD_Order_ID, I_PP_MRP.COLUMN_DD_Order_ID, mrpQuery); } deletePO(mrpContext, executor, I_DD_Order.class, filters); }
private int calculateDurationDays( final IMRPContext mrpContext, final I_DD_NetworkDistributionLine networkLine) { final I_PP_Product_Planning productPlanningData = mrpContext.getProductPlanning(); // // Leadtime final int leadtimeDays = productPlanningData.getDeliveryTime_Promised().intValueExact(); Check.assume(leadtimeDays >= 0, LiberoException.class, "leadtimeDays >= 0"); // // Transfer time int transferTime = networkLine.getTransfertTime().intValueExact(); if (transferTime <= 0) { transferTime = productPlanningData.getTransfertTime().intValueExact(); } Check.assume(transferTime >= 0, LiberoException.class, "transferTime >= 0"); final int durationTotalDays = leadtimeDays + transferTime; return durationTotalDays; }
private Set<Integer> getPP_Plant_IDs_ToUse() { int ppPlantId = -1; if (_ppPlantId != null) { ppPlantId = _ppPlantId; } else if (_mrpContext != null) { final I_S_Resource plant = _mrpContext.getPlant(); if (plant != null) { ppPlantId = plant.getS_Resource_ID(); } } if (ppPlantId <= 0) { return Collections.emptySet(); } final Set<Integer> plantIds = new HashSet<>(); plantIds.add(ppPlantId); if (_acceptNoPlant) { plantIds.add(null); } return plantIds; }
private I_DD_OrderLine createDD_OrderLine( final IMRPContext mrpContext, final I_DD_Order order, final I_DD_NetworkDistributionLine networkLine, final I_M_Locator locatorFrom, final I_M_Locator locatorTo, final BigDecimal qtyToMove, final Timestamp supplyDateFinishSchedule, final IMRPCreateSupplyRequest request) { final I_M_Product product = mrpContext.getM_Product(); final int bpartnerId = request.getMRPDemandBPartnerId(); final int orderLineSOId = request.getMRPDemandOrderLineSOId(); // // Create DD Order Line final I_DD_OrderLine ddOrderline = InterfaceWrapperHelper.newInstance(I_DD_OrderLine.class, order); ddOrderline.setAD_Org_ID(order.getAD_Org_ID()); ddOrderline.setDD_Order(order); ddOrderline.setC_BPartner_ID(bpartnerId); ddOrderline.setC_OrderLineSO_ID(orderLineSOId); // // Locator From/To ddOrderline.setM_Locator(locatorFrom); ddOrderline.setM_LocatorTo(locatorTo); // // Product, UOM, Qty // NOTE: we assume qtyToMove is in "mrpContext.getC_UOM()" which shall be the Product's stocking // UOM ddOrderline.setM_Product(product); ddOrderline.setC_UOM(mrpContext.getC_UOM()); ddOrderline.setQtyEntered(qtyToMove); ddOrderline.setQtyOrdered(qtyToMove); ddOrderline.setTargetQty(qtyToMove); // // Dates ddOrderline.setDateOrdered(order.getDateOrdered()); ddOrderline.setDatePromised(order.getDatePromised()); // // Other flags ddOrderline.setIsInvoiced(false); ddOrderline.setDD_AllowPush(networkLine.isDD_AllowPush()); ddOrderline.setIsKeepTargetPlant(networkLine.isKeepTargetPlant()); // // Save DD Order Line InterfaceWrapperHelper.save(ddOrderline); // // Create DD_OrderLine_Alternatives // NOTE: demand MRP line is available only in lot-for-lot order policy // TODO: i think we shall get all MRP demands, retrieve their alternatives, aggregate them and // create DD_OrderLine_Alternatives final I_PP_MRP parentMRPDemand = request.getMRPDemandRecordOrNull(); if (parentMRPDemand != null) { final List<I_PP_MRP_Alternative> mrpAlternatives = mrpDAO.retrieveMRPAlternativesQuery(parentMRPDemand).create().list(); for (final I_PP_MRP_Alternative mrpAlternative : mrpAlternatives) { createDD_OrderLine_Alternative(mrpContext, ddOrderline, mrpAlternative); } } // // Set Correct Planning Dates final int durationDays = calculateDurationDays(mrpContext, networkLine); final Timestamp supplyDateStartSchedule = TimeUtil.addDays(supplyDateFinishSchedule, 0 - durationDays); final List<I_PP_MRP> mrpList = mrpDAO.retrieveMRPRecords(ddOrderline); for (final I_PP_MRP mrp : mrpList) { mrp.setDateStartSchedule(supplyDateStartSchedule); mrp.setDateFinishSchedule(supplyDateFinishSchedule); InterfaceWrapperHelper.save(mrp); } return ddOrderline; }
/** Check Scheduled Dates for given MRP Supply record and creates MRP notes if needed */ private void checkMRPSupplyDates(final IMRPDemandToSupplyAllocation mrpDemandToSupplyAlloc) { // // Extract from parameters only what we need final IMRPContext mrpContext = getMRPContext(); final Date mrpRunDate = mrpContext.getDate(); // date when MRP is executed; this is our reference date final I_PP_MRP mrpDemandRecord = mrpDemandToSupplyAlloc .getMRPDemand(); // NOTE: MRP Demand is needed only as a reference, no other // informations are/shall be used from there final I_PP_MRP mrpSupplyRecord = mrpDemandToSupplyAlloc.getMRPSupply(); final BigDecimal mrpSupplyQty = mrpDemandToSupplyAlloc.getQtyAllocated(); // In case is QOH allocation, consider it valid if (mrpBL.isQtyOnHandAnyReservation(mrpSupplyRecord)) { return; } // // final boolean mrpSupplyIsFirm = mrpBL.isReleased(mrpSupplyRecord); final boolean mrpSupplyQtyNotZero = mrpSupplyQty.signum() != 0; final Date mrpSupplyDateFinishSchedule = mrpSupplyRecord .getDateFinishSchedule(); // Date when the supply it's scheduled by MRP to be available // in our warehouse final Date mrpSupplyDatePromised = mrpSupplyRecord .getDatePromised(); // Date when supply is promised by "vendor" to be available in our // warehouse // // MRP-030 De-Expedite Action Notice // Indicates that a schedule supply order is due before it is needed and should be delayed, // or demand rescheduled to an earlier date. // aka: Push Out if (mrpSupplyIsFirm && mrpSupplyQtyNotZero && mrpSupplyDateFinishSchedule != null && mrpSupplyDateFinishSchedule.compareTo(demandDateStartSchedule) < 0 // supply is scheduled to arrive before it's needed ) { newSupplyMRPNote(mrpSupplyRecord, "MRP-030") .addParameter("DateSupply", mrpSupplyDateFinishSchedule) .addParameter("DateDemand", demandDateStartSchedule) .addParameter("PP_MRP_Demand_ID", mrpDemandRecord) .collect(); } // // MRP-040 Expedite Action Notice // Indicates that a scheduled supply order is due after is needed and should be rescheduled to // an earlier date or demand rescheduled to a later date. // aka: Pull In if (mrpSupplyIsFirm && mrpSupplyQtyNotZero && mrpSupplyDateFinishSchedule != null && mrpSupplyDateFinishSchedule.compareTo(demandDateStartSchedule) > 0 // supply is scheduled to arrive after it's needed (that could be a serious problem) ) { newSupplyMRPNote(mrpSupplyRecord, "MRP-040") .addParameter("DateSupply", mrpSupplyDateFinishSchedule) .addParameter("DateDemand", demandDateStartSchedule) .addParameter("PP_MRP_Demand_ID", mrpDemandRecord) .collect(); } // // MRP-060 Release Due For Action Notice in time // Indicate that a supply order should be released. if it is a draft order, it must also be // approved. // if(date release > today && date release + after floating) if (!mrpSupplyIsFirm && mrpSupplyQtyNotZero && mrpSupplyDatePromised.compareTo(mrpRunDate) >= 0) { newSupplyMRPNote(mrpSupplyRecord, MRPExecutor.MRP_ERROR_060_SupplyDueButNotReleased) .addParameter("SupplyDatePromised", mrpSupplyDatePromised) .addParameter("MRPDateRun", mrpRunDate) .collect(); } // // MRP-070 Release Past Due For Action Notice overdue // Indicates that a supply order was not released when it was due, and should be either released // or expedited now, or the demand rescheduled for a later date. // if (date release < today && date erelese + before floating) if (!mrpSupplyIsFirm && mrpSupplyQtyNotZero && mrpSupplyDatePromised.compareTo(mrpRunDate) < 0) { newSupplyMRPNote(mrpSupplyRecord, MRPExecutor.MRP_ERROR_070_SupplyPastDueButNotReleased) .addParameter("SupplyDatePromised", mrpSupplyDatePromised) .addParameter("MRPDateRun", mrpRunDate) .collect(); } // // MRP-110 Past Due Action Notice // Indicates that a schedule supply order receipt is past due. // i.e. it's a firm supply which was scheduled to be released in the past, but so far it's not // received yet. if (mrpSupplyIsFirm && mrpSupplyDatePromised.compareTo(mrpRunDate) < 0) { newSupplyMRPNote(mrpSupplyRecord, MRPExecutor.MRP_ERROR_110_SupplyPastDueButReleased) .addParameter("SupplyDatePromised", mrpSupplyDatePromised) .addParameter("MRPDateRun", mrpRunDate) .collect(); } }
@Override public void createSupply(final IMRPCreateSupplyRequest request) { final IMRPContext mrpContext = request.getMRPContext(); final IMRPExecutor executor = request.getMRPExecutor(); final Properties ctx = mrpContext.getCtx(); final I_PP_Product_Planning productPlanningData = mrpContext.getProductPlanning(); final I_AD_Org org = mrpContext.getAD_Org(); final I_S_Resource plant = mrpContext.getPlant(); final Timestamp supplyDateFinishSchedule = TimeUtil.asTimestamp(request.getDemandDate()); // QtyToSupply: qty for which we need to produce the supply final BigDecimal qtyToSupply = request.getQtyToSupply(); // TODO vpj-cd I need to create logic for DRP-040 Shipment Due Action Notice // Indicates that a shipment for a Order Distribution is due. // Action should be taken at the source warehouse to ensure that the order is received on time. // TODO vpj-cd I need to create logic for DRP-050 Shipment Pas Due Action Notice // Indicates that a shipment for a Order Distribution is past due. You should either delay the // orders created the requirement for the product // or expedite them when the product does arrive. if (productPlanningData.getDD_NetworkDistribution_ID() <= 0) { // Indicates that the Product Planning Data for this product does not specify a valid network // distribution. executor.newMRPNote(mrpContext, ERR_DRP_060_NoSourceOfSupply).collect(); // return; } final I_DD_NetworkDistribution network = productPlanningData.getDD_NetworkDistribution(); final List<I_DD_NetworkDistributionLine> networkLines = Services.get(IDistributionNetworkDAO.class) .retrieveNetworkLinesByTargetWarehouse( network, productPlanningData.getM_Warehouse_ID()); if (networkLines.isEmpty()) { // No network lines were found for our target warehouse final I_M_Warehouse warehouseTo = productPlanningData.getM_Warehouse(); executor .newMRPNote(mrpContext, ERR_DRP_060_NoSourceOfSupply) .setComment("@NotFound@ @DD_NetworkDistributionLine_ID@") .addParameter( I_DD_NetworkDistribution.COLUMNNAME_DD_NetworkDistribution_ID, network == null ? "?" : network.getName()) .addParameter("M_Warehouse_Dest_ID", warehouseTo == null ? "?" : warehouseTo.getName()) .collect(); // return; } int M_Shipper_ID = -1; I_DD_Order order = null; BigDecimal qtyToSupplyRemaining = qtyToSupply; for (final I_DD_NetworkDistributionLine networkLine : networkLines) { // // Check: if we created DD Orders for all qty that needed to be supplied, stop here if (qtyToSupplyRemaining.signum() <= 0) { break; } // get supply source warehouse and locator final I_M_Warehouse warehouseFrom = networkLine.getM_WarehouseSource(); final I_M_Locator locatorFrom = Services.get(IWarehouseBL.class).getDefaultLocator(warehouseFrom); // get supply target warehouse and locator final I_M_Warehouse warehouseTo = networkLine.getM_Warehouse(); final I_M_Locator locatorTo = Services.get(IWarehouseBL.class).getDefaultLocator(warehouseTo); if (locatorFrom == null || locatorTo == null) { executor .newMRPNote(mrpContext, "DRP-001") // FIXME: DRP-001 error code does not exist .addParameter( I_DD_NetworkDistribution.COLUMNNAME_DD_NetworkDistribution_ID, network == null ? "?" : network.getName()) .addParameter( I_DD_NetworkDistributionLine.COLUMNNAME_M_WarehouseSource_ID, warehouseFrom.getName()) .addParameter( I_DD_NetworkDistributionLine.COLUMNNAME_M_Warehouse_ID, warehouseTo.getName()) .setComment("No locators found for source or target warehouse") .collect(); // continue; } // // Get the warehouse in transit final I_M_Warehouse warehouseInTrasit = retrieveInTransitWarehouse(ctx, warehouseFrom.getAD_Org_ID()); if (warehouseInTrasit == null) { // DRP-010: Do not exist Transit Warehouse to this Organization executor .newMRPNote(mrpContext, ERR_DRP_010_InTransitWarehouseNotFound) .addParameter(I_AD_Org.COLUMNNAME_AD_Org_ID, org.getName()) .collect(); // continue; } // // DRP-030: Do not exist Shipper for Create Distribution Order if (networkLine.getM_Shipper_ID() <= 0) { executor .newMRPNote(mrpContext, "DRP-030") .addParameter( I_DD_NetworkDistribution.COLUMNNAME_DD_NetworkDistribution_ID, network == null ? "?" : network.getName()) .addParameter( I_DD_NetworkDistributionLine.COLUMNNAME_DD_NetworkDistributionLine_ID, networkLine) .collect(); // continue; } if (M_Shipper_ID != networkLine.getM_Shipper_ID()) { // Org Must be linked to BPartner final I_AD_Org locatorToOrg = locatorTo.getAD_Org(); final IBPartnerOrgBL bpartnerOrgBL = Services.get(IBPartnerOrgBL.class); final I_C_BPartner orgBPartner = bpartnerOrgBL.retrieveLinkedBPartner(locatorToOrg); if (orgBPartner == null) { // DRP-020: Target Org has no BP linked to it executor .newMRPNote(mrpContext, "DRP-020") .addParameter(I_AD_Org.COLUMNNAME_AD_Org_ID, locatorToOrg.getName()) .collect(); // continue; } final I_C_BPartner_Location orgBPLocation = bpartnerOrgBL.retrieveOrgBPLocation( mrpContext.getCtx(), locatorToOrg.getAD_Org_ID(), ITrx.TRXNAME_None); // // Try found some DD_Order with Shipper , Business Partner and Doc Status = Draft // Consolidate the demand in a single order for each Shipper , Business Partner , // DemandDateStartSchedule order = getDDOrderFromCache( org, plant, warehouseInTrasit, networkLine.getM_Shipper_ID(), orgBPartner.getC_BPartner_ID(), supplyDateFinishSchedule); if (order == null) { order = InterfaceWrapperHelper.newInstance(I_DD_Order.class, mrpContext); order.setMRP_Generated(true); order.setMRP_AllowCleanup(true); order.setAD_Org_ID(warehouseTo.getAD_Org_ID()); order.setPP_Plant(plant); order.setC_BPartner(orgBPartner); order.setC_BPartner_Location(orgBPLocation); // order.setAD_User_ID(productPlanningData.getPlanner_ID()); // FIXME: improve // performances/cache and retrive Primary BP's User order.setSalesRep_ID(productPlanningData.getPlanner_ID()); final int docTypeDO_ID = getC_DocType_ID(mrpContext, X_C_DocType.DOCBASETYPE_DistributionOrder); order.setC_DocType_ID(docTypeDO_ID); order.setM_Warehouse(warehouseInTrasit); order.setDocStatus(X_DD_Order.DOCSTATUS_Drafted); order.setDocAction(X_DD_Order.DOCACTION_Complete); order.setDateOrdered(mrpContext.getDateAsTimestamp()); order.setDatePromised(supplyDateFinishSchedule); order.setM_Shipper_ID(networkLine.getM_Shipper_ID()); order.setIsInDispute(false); order.setIsInTransit(false); InterfaceWrapperHelper.save(order); executor.addGeneratedSupplyDocument(order); addToCache(order); } M_Shipper_ID = networkLine.getM_Shipper_ID(); } // // Crate DD order line final BigDecimal qtyToMove = calculateQtyToMove(qtyToSupplyRemaining, networkLine.getPercent()); createDD_OrderLine( mrpContext, order, networkLine, locatorFrom, locatorTo, qtyToMove, supplyDateFinishSchedule, request); qtyToSupplyRemaining = qtyToSupplyRemaining.subtract(qtyToMove); } // // Check: remaining qtyToSupply shall be ZERO if (qtyToSupplyRemaining.signum() != 0) { // TODO: introduce DRP-XXX notice throw new LiberoException( "Cannot create DD Order for required Qty To Supply." + "\nQtyToSupply: " + qtyToSupply + "\nQtyToSupply (remaining): " + qtyToSupplyRemaining + "\n@DD_NetworkDistribution_ID@: " + network + "\n@DD_NetworkDistributionLine_ID@: " + networkLines + "\nMRPContext: " + mrpContext); } }