feat(lstv2 move): moved lstv2 into this app to keep them combined and easier to maintain

This commit is contained in:
2025-09-19 22:22:05 -05:00
parent caf2315191
commit e4477402ad
847 changed files with 165801 additions and 0 deletions

View File

@@ -0,0 +1,286 @@
import { db } from "../../../../../../../database/dbclient.js";
import { settings } from "../../../../../../../database/schema/settings.js";
import { tryCatch } from "../../../../../../globalUtils/tryCatch.js";
import XLSX from "xlsx";
import { excelDateStuff } from "../../../../utils/excelDateStuff.js";
import { postForecast } from "../postForecast.js";
import { query } from "../../../../../sqlServer/prodSqlServer.js";
import { activeArticle } from "../../../../../sqlServer/querys/dataMart/article.js";
import { addDays } from "date-fns";
import { sendEmail } from "../../../../../notifications/controller/sendMail.js";
import { createLog } from "../../../../../logger/logger.js";
let customerID = 4;
export const lorealForecast = async (data: any, user: any) => {
/**
* Post a standard forecast based on the standard template.
*/
const { data: s, error: e } = await tryCatch(db.select().from(settings));
if (e) {
return {
sucess: false,
message: `Error getting settings`,
data: e,
};
}
const plantToken = s.filter((s) => s.name === "plantToken");
const arrayBuffer = await data.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
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 { data: a, error: ae } = await tryCatch(
query(activeArticle, "Loreal calling active av")
);
if (ae) {
return {
success: false,
message: "Error getting active av",
data: [],
};
}
const article: any = a?.data;
// 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;
}, {})
);
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,
},
};
const { data: sentEmail, error: sendEmailError } = await tryCatch(
sendEmail(emailSetup)
);
if (sendEmailError) {
createLog(
"error",
"blocking",
"notify",
"Failed to send email, will try again on next interval"
);
return {
success: false,
message: "Failed to send email, will try again on next interval",
};
}
// if the customerarticle number is not matching just ignore it
const predefinedObject = {
receivingPlantId: plantToken[0].value,
documentName: `ForecastFromLST-${new Date(Date.now()).toLocaleString(
"en-US"
)}`,
sender: user.username || "lst-system",
customerId: customerID,
positions: [],
};
let 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);
const posting: any = await postForecast(updatedPredefinedObject, user);
return {
success: posting.success,
message: posting.message,
data: posting.data === "" ? ebmForecastData : posting.data,
};
};

View File

@@ -0,0 +1,164 @@
import { db } from "../../../../../../../database/dbclient.js";
import { settings } from "../../../../../../../database/schema/settings.js";
import { tryCatch } from "../../../../../../globalUtils/tryCatch.js";
import XLSX from "xlsx";
import { excelDateStuff } from "../../../../utils/excelDateStuff.js";
import { postForecast } from "../postForecast.js";
import { query } from "../../../../../sqlServer/prodSqlServer.js";
import { activeArticle } from "../../../../../sqlServer/querys/dataMart/article.js";
export const pNgForecast = async (data: any, user: any) => {
/**
* Post a standard forecast based on the standard template.
*/
const { data: s, error: e } = await tryCatch(db.select().from(settings));
if (e) {
return {
sucess: false,
message: `Error getting settings`,
data: e,
};
}
const pNg = s.filter((n: any) => n.name === "pNgAddress");
const { data: a, error: ae } = await tryCatch(
query(activeArticle, "p&g active av")
);
if (ae) {
return {
success: false,
message: "Error getting active av",
data: [],
};
}
const article: any = a?.data;
const plantToken = s.filter((s) => s.name === "plantToken");
const arrayBuffer = await data.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
const workbook = XLSX.read(buffer, { type: "buffer" });
const sheetName = workbook.SheetNames[0];
//const sheet: any = workbook.Sheets[sheetName];
const sheet: any = workbook.Sheets["SchedAgreementUIConfigSpreadshe"];
const range = XLSX.utils.decode_range(sheet["!ref"]);
const headers = [];
for (let C = range.s.c; C <= range.e.c; ++C) {
const cellAddress = XLSX.utils.encode_cell({ r: 0, c: C }); // row 0 = Excel row 1
const cell = sheet[cellAddress];
headers.push(cell ? cell.v : undefined);
}
//console.log(headers);
const forecastData: any = XLSX.utils.sheet_to_json(sheet, {
defval: "",
header: headers,
range: 1,
});
const groupedByCustomer: any = forecastData.reduce(
(acc: any, item: any) => {
const id = item.CustomerID;
if (!acc[id]) {
acc[id] = [];
}
acc[id].push(item);
return acc;
},
{}
);
const foreCastData: any = [];
for (const [customerID, forecast] of Object.entries(groupedByCustomer)) {
//console.log(`Running for Customer ID: ${customerID}`);
const newForecast: any = forecast;
const predefinedObject = {
receivingPlantId: plantToken[0].value,
documentName: `ForecastFromLST-${new Date(
Date.now()
).toLocaleString("en-US")}`,
sender: user.username || "lst-system",
customerId: pNg[0].value,
positions: [],
};
// map everything out for each order
const nForecast = newForecast.map((o: any) => {
// const invoice = i.filter(
// (i: any) => i.deliveryAddress === parseInt(customerID)
// );
// if (!invoice) {
// return;
// }
return {
customerArticleNo: parseInt(o["Customer Item No."]),
requirementDate: excelDateStuff(parseInt(o["Request Date"])),
quantity: o["Remaining Qty to be Shipped"],
};
});
// check to make sure the av belongs in this plant.
const onlyNumbers = nForecast.filter((n: any) => n.quantity > 0);
const filteredForecast: any = [];
for (let i = 0; i < nForecast.length; i++) {
//console.log(nForecast[i].customerArticleNo);
const activeAV = article.filter(
(c: any) =>
c?.CustomerArticleNumber ===
nForecast[i]?.customerArticleNo.toString() &&
// validate it works via the default address
c?.IdAdresse === parseInt(pNg[0].value)
);
if (activeAV.length > 0) {
filteredForecast.push(onlyNumbers[i]);
}
}
if (filteredForecast.length === 0) {
console.log("Nothing to post");
return {
success: true,
message: "No forecast to be posted",
data: foreCastData,
};
}
// do that fun combining thing
let updatedPredefinedObject = {
...predefinedObject,
positions: [...predefinedObject.positions, ...filteredForecast],
};
//console.log(updatedPredefinedObject);
// post the orders to the server
const posting: any = await postForecast(updatedPredefinedObject, user);
foreCastData.push({
customer: customerID,
//totalOrders: orders?.length(),
success: posting.success,
message: posting.message,
data: posting.data,
});
}
return {
success: true,
message: "Forecast Posted",
data: foreCastData,
};
};

View File

@@ -0,0 +1,114 @@
import { db } from "../../../../../../../database/dbclient.js";
import { settings } from "../../../../../../../database/schema/settings.js";
import { tryCatch } from "../../../../../../globalUtils/tryCatch.js";
import XLSX from "xlsx";
import { excelDateStuff } from "../../../../utils/excelDateStuff.js";
import { postForecast } from "../postForecast.js";
export const standardForecast = async (data: any, user: any) => {
/**
* Post a standard forecast based on the standard template.
*/
const { data: s, error: e } = await tryCatch(db.select().from(settings));
if (e) {
return {
sucess: false,
message: `Error getting settings`,
data: e,
};
}
const plantToken = s.filter((s) => s.name === "plantToken");
const arrayBuffer = await data.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
const workbook = XLSX.read(buffer, { type: "buffer" });
const sheetName = workbook.SheetNames[0];
const sheet = workbook.Sheets[sheetName];
const headers = [
"CustomerArticleNumber",
"Quantity",
"RequirementDate",
"CustomerID",
];
const forecastData: any = XLSX.utils.sheet_to_json(sheet, {
defval: "",
header: headers,
range: 1,
});
const groupedByCustomer: any = forecastData.reduce(
(acc: any, item: any) => {
const id = item.CustomerID;
if (!acc[id]) {
acc[id] = [];
}
acc[id].push(item);
return acc;
},
{}
);
const foreCastData: any = [];
for (const [customerID, forecast] of Object.entries(groupedByCustomer)) {
//console.log(`Running for Customer ID: ${customerID}`);
const newForecast: any = forecast;
const predefinedObject = {
receivingPlantId: plantToken[0].value,
documentName: `ForecastFromLST-${new Date(
Date.now()
).toLocaleString("en-US")}`,
sender: user.username || "lst-system",
customerId: customerID,
positions: [],
};
// map everything out for each order
const nForecast = newForecast.map((o: any) => {
// const invoice = i.filter(
// (i: any) => i.deliveryAddress === parseInt(customerID)
// );
// if (!invoice) {
// return;
// }
return {
customerArticleNo: o.CustomerArticleNumber,
requirementDate: excelDateStuff(parseInt(o.RequirementDate)),
quantity: o.Quantity,
};
});
// do that fun combining thing
let updatedPredefinedObject = {
...predefinedObject,
positions: [...predefinedObject.positions, ...nForecast],
};
//console.log(updatedPredefinedObject);
// post the orders to the server
const posting: any = await postForecast(updatedPredefinedObject, user);
foreCastData.push({
customer: customerID,
//totalOrders: orders?.length(),
success: posting.success,
message: posting.message,
data: posting.data,
});
}
return {
success: true,
message: "Forecast Posted",
data: foreCastData,
};
};