import { addDays } from "date-fns"; import XLSX from "xlsx"; import { runDatamartQuery } from "../datamart/datamart.controller.js"; import { type SqlQuery, sqlQuerySelector, } from "../prodSql/prodSqlQuerySelector.utils.js"; import { returnFunc } from "../utils/returnHelper.utils.js"; import { sendEmail } from "../utils/sendEmail.utils.js"; import { tryCatch } from "../utils/trycatch.utils.js"; import { postData } from "./logistics.dm.postData.js"; const customerID = 4; export const lorealForecast = async (data: any, user: any) => { /** * Post a standard forecast based on the standard template. */ const buffer = Buffer.from(data.buffer); const workbook = XLSX.read(buffer, { type: "buffer" }); const sheet: any = workbook.Sheets["Alpla HDPE"]; const range = XLSX.utils.decode_range(sheet["!ref"]); const psheet: any = workbook.Sheets["Alpla PET"]; const prange = XLSX.utils.decode_range(psheet["!ref"]); const headers = []; for (let C = range.s.c; C <= range.e.c; ++C) { const cellAddress = XLSX.utils.encode_cell({ r: 1, c: C }); // row 0 = Excel row 1 const cell = sheet[cellAddress]; headers.push(cell ? cell.v : undefined); } const pheaders = []; for (let C = prange.s.c; C <= prange.e.c; ++C) { const cellAddress = XLSX.utils.encode_cell({ r: 1, c: C }); // row 0 = Excel row 1 const cell = psheet[cellAddress]; pheaders.push(cell ? cell.v : undefined); } const ebmForeCastData: any = XLSX.utils.sheet_to_json(sheet, { defval: "", header: headers, range: 3, }); const petForeCastData: any = XLSX.utils.sheet_to_json(psheet, { defval: "", header: pheaders, range: 3, }); const ebmForecastData: any = []; const missingSku: any = []; const avSQLQuery = sqlQuerySelector(`datamart.activeArticles`) as SqlQuery; if (!avSQLQuery.success) { return returnFunc({ success: false, level: "error", module: "logistics", subModule: "forecast", message: `Error getting Article info`, data: [avSQLQuery.message], notify: true, }); } const { data: a, error: ae } = await tryCatch( runDatamartQuery({ name: "activeArticles", options: {} }), ); if (ae) { return { success: false, message: "Error getting active av", data: [], }; } const article: any = a?.data; console.log(article); // process the ebm forcast for (let i = 0; i < ebmForeCastData.length; i++) { // bottle code const sku = ebmForeCastData[i]["HDPE Bottle Code"]; // ignore the blanks if (sku === "") continue; // ignore zero qty // if (ebmForeCastData[i][`Day ${i}`]) continue; for (let f = 0; f <= 90; f++) { const day = `Day ${f + 1}`; // if (ebmForeCastData[i][day] === 0) continue; const forcast = { customerArticleNo: sku, requirementDate: addDays(new Date(Date.now()), f), //excelDateStuff(parseInt(date)), quantity: ebmForeCastData[i][day] ?? 0, }; if (forcast.quantity === 0) continue; // checking to make sure there is a real av to add to. const activeAV = article.filter( (c: any) => c?.CustomerArticleNumber === forcast.customerArticleNo.toString(), ); if (activeAV.length === 0) { if (typeof forcast.customerArticleNo === "number") { missingSku.push(forcast); } continue; } ebmForecastData.push(forcast); } //console.log(ebmForeCastData.length); } // petForeCastData.forEach((item: any, index: any) => { // //console.log(`Processing item ${index + 1} of ${forecastData.length}`); // // Extract the customer code // const customerCode = item["SOUTH PET BOTTLES"]; // // Process each date in the current object // for (const [date, qty] of Object.entries(item)) { // // Skip metadata fields // if (petMetadataFields.includes(date)) continue; // if (qty === 0) continue; // // Create your transformed record // const record = { // customerArticleNo: customerCode, // requirementDate: excelDateStuff(parseInt(date)), // quantity: qty, // }; // // Do something with this record // petForecastData.push(record); // } // }); // pet forecast for (let i = 0; i < petForeCastData.length; i++) { // bottle code const sku = petForeCastData[i]["South PET Bottle Code"]; // ignore the blanks if (sku === "") continue; // ignore zero qty // if (ebmForeCastData[i][`Day ${i}`]) continue; for (let f = 0; f <= 90; f++) { const day = `Day ${f + 1}`; // if (ebmForeCastData[i][day] === 0) continue; const forcast = { customerArticleNo: sku, requirementDate: addDays(new Date(Date.now()), f), //excelDateStuff(parseInt(date)), quantity: petForeCastData[i][day] ?? 0, }; if (forcast.quantity === 0 || forcast.quantity === "") continue; if (forcast.customerArticleNo < 99999) { //console.log(`Sku a normal av ${forcast.customerArticleNo}`); continue; } // checking to make sure there is a real av to add to. const activeAV = article.filter( (c: any) => c?.CustomerArticleNumber === forcast.customerArticleNo.toString(), ); if (activeAV.length === 0) { if (typeof forcast.customerArticleNo === "number") { missingSku.push(forcast); } continue; } ebmForecastData.push(forcast); } } //console.log(comForecast); // email the for the missing ones const missedGrouped = Object.values( missingSku.reduce((acc: any, item: any) => { const key = item.customerArticleNo; if (!acc[key]) { // first time we see this customer acc[key] = item; } else { // compare dates and keep the earliest if ( new Date(item.requirementDate) < new Date(acc[key].requirementDate) ) { acc[key] = item; } } return acc; }, {}), ); // TODO: change this to a flagged notification so that he users can subscribe or leave it. this removes the hardcody shit. // const emailSetup = { // email: // "Blake.matthes@alpla.com; Stuart.Gladney@alpla.com; Harold.Mccalister@alpla.com; Jenn.Osbourn@alpla.com", // subject: // missedGrouped.length > 0 // ? `Alert! There are ${missedGrouped.length}, missing skus.` // : `Alert! There is a missing SKU.`, // template: "missingLorealSkus", // context: { // items: missedGrouped, // }, // }; // sendEmail(emailSetup); // if the customerarticle number is not matching just ignore it const predefinedObject = { receivingPlantId: process.env.PROD_PLANT_TOKEN ?? "test1", documentName: `ForecastFromLST-${new Date(Date.now()).toLocaleString( "en-US", )}`, sender: user.username || "lst-system", customerId: customerID, positions: [], }; const updatedPredefinedObject = { ...predefinedObject, positions: [ ...predefinedObject.positions, ...ebmForecastData, // ...ebmForecastData.filter( // (q: any) => // q.customerArticleNo != "" && q.customerArticleNo != "Total" // ), // ...petForecastData.filter( // (q: any) => // q.customerArticleNo != "" && q.customerArticleNo != "Total" // ), ], }; // console.log(updatedPredefinedObject); // posting the data to the new backend so we can store the data. const posting: any = await postData( { type: "forecast", endpoint: "/public/v1.0/DemandManagement/DELFOR", data: updatedPredefinedObject as any, }, user, ); return { success: posting.success, message: posting.message, data: posting.data === "" ? ebmForecastData : posting.data, }; };