private void performDailyLearning() { updateStatusCode(); if (statusCd != STATUS_CD.LEARNING || disableLearning == true) { return; } if (onRange && (rcBopInv > 0 || hasSale == true || rcInvIn > 0)) { initLearningMetrics(); // Daily learning only applies to RC_AVG_DEMAND and begins immediately when the PL comes on // range double lastWeekLift = getDemandUplift(SystemDao.getReviewCycleStartDate()); double defaultWeight = SystemDao.getDefaultWeight(); Demand beginOfRunCycleDemand = getDemand(SystemDao.getReviewCycleStartDate()); double beginOfRunCycleRcAvgDemand = 0; if (beginOfRunCycleDemand != null) { beginOfRunCycleRcAvgDemand = beginOfRunCycleDemand.getRcAvgDemand(); } if (onRange && !disableLearning) { rcAvgDemand = getWeightedWeight1(learningWeekCounter, defaultWeight) * rcDemand / lastWeekLift + (1 - getWeightedWeight1(learningWeekCounter, defaultWeight)) * beginOfRunCycleRcAvgDemand; } } }
private void processInventory() { LocalDate crc = SystemDao.getCrc(); if (isStockedOut) { Demand d = getDemand(SystemDao.getCrc()); if (crc.getDayOfWeek() == DateTimeConstants.SUNDAY) { d = getDemand(SystemDao.getReviewCycleStartDate()); } int targetInv = (int) d.getRcAvgDemand() - inventory; setInventory(targetInv > 0 ? targetInv : (int) d.getRcAvgDemand()); isStockedOut = false; } if (crc.getDayOfWeek() == DateTimeConstants.SUNDAY) { rcBopInv = inventory; } // daily inventory updates updateEpEopInv(); epEopInv = inventory; rcInvOut += epInvOut; rcInvIn += epInvIn; ProductInventory p = getInventory(SystemDao.getReviewCycleStartDate()); double beginOfPeriodEpAvgInv = 0; if (p != null) { beginOfPeriodEpAvgInv = p.getEpAvgInv(); } double weight_5 = SystemDao.getWeight_5(); epAvgInv = weight_5 * ((epEopInv >= 0) ? epEopInv : 0) + (((1 - weight_5) * beginOfPeriodEpAvgInv)); epAvgInv = Math.max(0, epAvgInv); }
private void initLearningMetrics() { if (learningMetricsInitialized) { return; } double defaultWeight = SystemDao.getDefaultWeight(); double lastWeekLift = getDemandUplift(SystemDao.getReviewCycleStartDate()); Sales beginOfRunCycleSales = getSales(SystemDao.getReviewCycleStartDate()); double beginOfRunCycleRcAvgSales = 0; if (beginOfRunCycleSales != null) { beginOfRunCycleRcAvgSales = beginOfRunCycleSales.getRcAvgSales(); } rcAvgSales = getWeightedWeight1(learningWeekCounter, defaultWeight) * epSales / lastWeekLift + (1 - getWeightedWeight1(learningWeekCounter, defaultWeight)) * beginOfRunCycleRcAvgSales; // Tim's documentation states: // Its important sales metrics are initialized to AVG_WEEKLY_SALES based on the initialization // logic. // This estimates initial values based on different combinations of the PLs product and location // hierarchies dependent on the specific situation for that PL rcAvgSalesActual = rcAvgSales; rcAvgDemand = rcAvgSales; rcOldAvgDemand = rcAvgSales; rcAvgDemandActual = rcAvgSales; epAvgInv = rcAvgSales; rcWass2 = Math.pow(rcAvgSales, 2); learningMetricsInitialized = true; }
private Double getWeight1(long age) { // Weight that will be used when status_cd = ACTIVE if (age <= 0) { return SystemDao.getDefaultWeight(); } return Math.max(0.2, 1.0 / age); }
public void setFirstSalesDate(LocalDate firstSalesDate) { if (firstSalesDate == null && epSalesActual > 0) { this.firstSalesDate = SystemDao.getCrc(); } else { this.firstSalesDate = firstSalesDate; } }
// Hook: WeeklyLearningSummationHook.java public void performWeeklyMetricsProcessing(String locationId) { System.out.println( "Starting the Weekly Metrics Processing for product/location: " + locationId); if (SystemDao.getCrc().getDayOfWeek() == DateTimeConstants.SUNDAY) { processWeeklyMetrics(); } }
public void setFirstReceiptDate(LocalDate firstReceiptDate) { if (this.firstReceiptDate == null && epEopInv > 0 || rcInvIn > 0) { this.firstReceiptDate = SystemDao.getCrc(); } else if (this.firstReceiptDate == null && firstSalesDate != null) { this.firstReceiptDate = firstSalesDate; } this.firstReceiptDate = firstReceiptDate; }
// Hook: MSSalesOutlierCheckHook.java public void performOutlierProcessing(String locId) { // only apply constraint if we are not in a seasonal period System.out.println( "Starting the Outlier/Unreported Sales Calculation for product/location: " + locId); int specialPuchaseOrderWassMult = SystemDao.getSpecialPurchaseOrderWassMultiplier(); int specialPuchaseOrderSizeMult = SystemDao.getSpecialPurchaseOrderSizeMultipler(); double rcWass2 = Math.pow(rcAvgSales, 2); Double maxValue = Math.max( rcAvgSales + specialPuchaseOrderSizeMult * Math.sqrt(Math.max(0, rcWass2)), specialPuchaseOrderWassMult * innerPackQty); if (epSalesActual > maxValue && eventSeasonalIndicator == false) { epSales = maxValue; } else { epSales = epSalesActual; } }
public void setDisableLearning(Boolean disableLearning) { // Learning can only be turned on on a SUNDAY if (disableLearning) { if (SystemDao.getCrc().plusDays(1).getDayOfWeek() == DateTimeConstants.SUNDAY) this.disableLearning = disableLearning; } else { this.disableLearning = disableLearning; } }
public void updateStatusCode() { switch (this.statusCd) { case NEW: if (this.statusCd == STATUS_CD.NEW && !firstReceiptDate.isAfter(SystemDao.getCrc()) && !storeOpenDate.isAfter(SystemDao.getCrc())) { this.statusCd = STATUS_CD.LEARNING; } break; case LEARNING: // provided there have been no non-demand events or events that lasted most of the week and // the PL has been on // range the entire time, the PL is moved to status_cd = ACTIVE after the 3rd full week. if (!hasBeenOffRange && learningWeekCounter > 3) { statusCd = STATUS_CD.ACTIVE; } break; case INACTIVE: // item only goes inactive during integration, when PL drops from the file } }
private void processWeeklyMetrics() { // On the end of the review cycle (Sat night) the rcAvgDemand does not undergo weekly learning // For this exercise we do not have time granulity so weekly processing is done Sunday for the // prior week LocalDate crc = SystemDao.getCrc(); LocalDate prevCRCStartDate = SystemDao.getReviewCycleStartDate(); // getPreviousCRCStartDate(); Sales salesData = getSales(crc); Sales beginOfPeriodSalesData = getSales(prevCRCStartDate); double beginOfPeriodRcAvgSales = 0; double beginOfPeriodRcActualAvgSales = 0; if (beginOfPeriodSalesData != null) { beginOfPeriodRcAvgSales = beginOfPeriodSalesData.getRcAvgSales(); beginOfPeriodRcActualAvgSales = beginOfPeriodSalesData.getRcAvgSalesActual(); } Demand demandData = getDemand(crc); Demand beginOfPeriodDemandData = getDemand(prevCRCStartDate); double beginOfPeriodRcAvgDemand = 0; double beginOfPeriodRcAvgActualDemand = 0; if (beginOfPeriodDemandData != null) { beginOfPeriodRcAvgDemand = beginOfPeriodDemandData.getRcAvgDemand(); beginOfPeriodRcAvgActualDemand = beginOfPeriodDemandData.getRcAvgDemandActual(); } double defaultWeight = SystemDao.getDefaultWeight(); double lastWeekLift = getDemandUplift(prevCRCStartDate); double weight = 1.0; if (statusCd == STATUS_CD.LEARNING) { weight = getWeightedWeight1(learningWeekCounter, defaultWeight); } else if (statusCd == STATUS_CD.ACTIVE) { weight = getWeight1(learningWeekCounter); } rcAvgSalesActual = (weight * (salesData.getRcSalesActual() / lastWeekLift)) + ((1 - weight) * beginOfPeriodRcActualAvgSales); rcAvgSales = (weight * (salesData.getRcSales() / lastWeekLift)) + ((1 - weight) * beginOfPeriodRcAvgSales); rcAvgDemand = (weight * (demandData.getRcDemand() / lastWeekLift)) + ((1 - weight) * beginOfPeriodRcAvgDemand); rcAvgDemandActual = (weight * (demandData.getRcDemandActual() / lastWeekLift)) + ((1 - weight) * beginOfPeriodRcAvgActualDemand); // error checking if (rcAvgDemand == 0 && statusCd != STATUS_CD.INACTIVE) { System.out.println("Error: 0 demand when product status is not inactive"); } if (rcAvgDemand >= 4 * rcAvgSales) { rcAvgSales = 4 * rcAvgSalesActual; System.out.println("Error: RC Actual Sales greater then 4 times RC Average Sales"); } if (rcAvgDemand >= 3 * rcAvgSalesActual) { rcAvgSales = 3 * rcAvgSalesActual; System.out.println("Error: RC Actual Sales greater then 3 times RC Actual Average Sales"); } storeWeeklyMetrics(crc); this.learningWeekCounter++; // this is the end of the review cycle reset hasBeenOffRange if (hasBeenOffRange) { hasBeenOffRange = false; } resetRcAccumulators(); }
/** Outlier filtering has been done prior to processing daily metrics * */ private void processDailyMetrics() { LocalDate crc = SystemDao.getCrc(); double lostSales = getLostSales(); rcSalesActual = rcSalesActual + epSalesActual; // rcSales is outlier filtered sales and has been calculated in outlier processing rcSales = rcSales + epSales; Years yr = Years.years(crc.getYear()); Map<LocalDate, Sales> currSalesMap = salesMap.get(yr); if (currSalesMap == null) { currSalesMap = new TreeMap<LocalDate, Sales>(); } Sales s = currSalesMap.get(crc); if (s == null) { s = new Sales(); } s.setEpSalesActual(epSalesActual); s.setEpSales(epSales); s.setRcSales(rcSales); s.setRcSalesActual(rcSalesActual); s.setLostSales(lostSales); currSalesMap.put(crc, s); salesMap.put(yr, currSalesMap); /** * Daily Demand Calculations **** */ // daily demand epDemand = getEpDemand(); // demand = sales + lostsales // outlier filtered sales used for rcDemand rcDemand = rcSales + lostSales; rcDemandActual += (epSalesActual + lostSales); Map<LocalDate, Demand> currDemandMap = demandMap.get(yr); if (currDemandMap == null) { currDemandMap = new TreeMap<LocalDate, Demand>(); } Demand d = currDemandMap.get(crc); if (d == null) { d = new Demand(); } d.setEpDemand(epDemand); d.setRcDemand(rcDemand); d.setRcDemandActual(rcDemandActual); currDemandMap.put(crc, d); demandMap.put(yr, currDemandMap); /** **** Daily Inventory Calculations ****** */ processInventory(); if (epSales > rcMaxSales) { rcMaxSales = epSales; weekSinceMaxSales = 0; } if (epEopInv > demoStock) { daysSinceWalk = daysSinceWalk + 1; } else { daysSinceWalk = 0; } if (epSalesActual == 0) { daysSinceSale = daysSinceSale + 1; } else { daysSinceSale = 0; } Map<LocalDate, ProductInventory> currInventoryMap = inventoryMap.get(yr); if (currInventoryMap == null) { currInventoryMap = new TreeMap<LocalDate, ProductInventory>(); } ProductInventory inv = currInventoryMap.get(crc); if (inv == null) { inv = new ProductInventory(); } inv.setEpAvgInv(epAvgInv); inv.setEpEopInv(epEopInv); inv.setEpInvOut(epInvOut); inv.setEpInvIn(epInvIn); inv.setRcBopInv(rcBopInv); inv.setRcBopInv(rcBopInv); inv.setRcInvIn(rcInvIn); inv.setRcInvOut(rcInvOut); inv.setInventory(inventory); currInventoryMap.put(crc, inv); inventoryMap.put(yr, currInventoryMap); if (statusCd == STATUS_CD.LEARNING) { performDailyLearning(); } resetEpAccumulators(); }