feat(dm): migrated all the dm topics
All checks were successful
Build and Push LST Docker Image / docker (push) Successful in 4m26s

This commit is contained in:
2026-06-26 11:05:17 -05:00
parent 012a7e83b2
commit 47b149d1ea
48 changed files with 14156 additions and 44 deletions

View File

@@ -1,10 +1,6 @@
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";
@@ -55,32 +51,22 @@ export const lorealForecast = async (data: any, user: any) => {
const ebmForecastData: any = [];
const missingSku: any = [];
const avSQLQuery = sqlQuerySelector(`datamart.activeArticles`) as SqlQuery;
const { data: a, error: ae } = await tryCatch(
runDatamartQuery({ name: "activeArticles", options: {} }),
);
if (!avSQLQuery.success) {
if (ae) {
return returnFunc({
success: false,
level: "error",
module: "logistics",
subModule: "forecast",
message: `Error getting Article info`,
data: [avSQLQuery.message],
data: [ae.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);

View File

@@ -96,9 +96,18 @@ export const standardForecast = async (data: any, user: any) => {
});
}
return {
success: foreCastData[0].success,
message: foreCastData[0].message,
data: foreCastData,
};
if (foreCastData.length === 0) {
return {
success: false,
message:
"There was an error processing the forecast did you upload the correct file?",
data: [],
};
} else {
return {
success: foreCastData?.[0].success,
message: foreCastData?.[0].message,
data: foreCastData ?? [],
};
}
};

View File

@@ -88,7 +88,7 @@ r.post("/", requireAuth, upload.single("file"), async (req, res) => {
? "The forecast was accepted by Alplaprod 2.0 please check to make sure everything processed properly."
: (result.message as string),
data: result.data ?? ([] as any),
status: result.success ? 200 : 500,
status: result.success ? 200 : 400,
});
});

View File

@@ -0,0 +1,191 @@
import { isAfter } from "date-fns";
import { format } from "date-fns-tz";
import XLSX from "xlsx";
import { runDatamartQuery } from "../datamart/datamart.controller.js";
import { excelDateStuff } from "../utils/excelToDate.utils.js";
import { returnFunc } from "../utils/returnHelper.utils.js";
import { tryCatch } from "../utils/trycatch.utils.js";
import { abbottForecast } from "./logistics.dm.forecast.map.abbott.js";
import { postData } from "./logistics.dm.postData.js";
// customeris/articles stuff will be in basis once we move to iowa
const customerID = 8;
const invoiceID = 9;
const articles = "118,120";
export const abbottOrders = async (data: any, user: any) => {
/**
* Standard orders meaning that we get the standard file exported and fill it out and uplaod to lst.
*/
// articleInfo
const { data: article, error: ae } = await tryCatch(
runDatamartQuery({ name: "bulkOrderArticleInfo", options: { articles } }),
);
const a: any = article?.data;
if (ae) {
return returnFunc({
success: false,
level: "error",
module: "logistics",
subModule: "orders",
message: `Error getting Article info`,
data: [ae.message],
notify: true,
});
}
const { data: o, error: oe } = await tryCatch(
runDatamartQuery({ name: "orderState", options: {} }),
);
if (oe) {
return returnFunc({
success: false,
level: "error",
module: "logistics",
subModule: "orders",
message: `Error getting Article info`,
data: [oe.message],
notify: true,
});
}
const openOrders: any = o?.data;
const buffer = Buffer.from(data.buffer);
const workbook = XLSX.read(buffer, { type: "buffer" });
const sheetName = workbook.SheetNames[0] as string;
const sheet = workbook.Sheets[sheetName] as any;
abbottForecast(sheet, user);
// Define custom headers
const customHeaders = ["date", "time", "newton8oz", "newton10oz"];
const orderData = XLSX.utils.sheet_to_json(sheet, {
range: 5, // Start at row 5 (index 4)
header: customHeaders,
defval: "", // Default value for empty cells
});
// the base of the import
const predefinedObject = {
receivingPlantId: process.env.PROD_PLANT_TOKEN ?? "test1",
documentName: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
"en-US",
)}`,
sender: user.username || "lst-system",
externalRefNo: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
"en-US",
)}`,
orders: [],
};
//const oOrders: any = openOrders;
//console.log(orderData);
function trimAll(str: string) {
return str.replace(/\s+/g, "");
}
let correctedOrders: any = orderData
.filter(
(o: any) =>
(o.newton8oz && o.newton8oz.trim() !== "") ||
(o.newton10oz && o.newton10oz.trim() !== ""),
)
.map((o: any) => ({
date: excelDateStuff(o.date, o.time),
po:
trimAll(o.newton8oz) !== ""
? trimAll(o.newton8oz)
: o.newton10oz.replace(/[\s\u00A0]+/g, ""),
customerArticleNumber:
o.newton8oz !== ""
? a.filter((a: any) => a.av === 118)[0].CustomerArticleNumber
: a.filter((a: any) => a.av === 120)[0].CustomerArticleNumber,
qty:
o.newton8oz !== ""
? a.filter((a: any) => a.av === 118)[0].totalTruckLoad
: a.filter((a: any) => a.av === 120)[0].totalTruckLoad,
}));
//console.log(correctedOrders);
// now we want to make sure we only correct orders that or after now
correctedOrders = correctedOrders.filter((o: any) => {
//const parsedDate = parse(o.date, "M/d/yyyy, h:mm:ss a", new Date());
return isAfter(new Date(o.date), new Date().toISOString());
});
//console.log(correctedOrders);
// last map to remove orders that have already been started
// correctedOrders = correctedOrders.filter((oo: any) =>
// oOrders.some((o: any) => o.CustomerOrderNumber === oo.po)
// );
const postedOrders: any = [];
const filterOrders: any = correctedOrders;
//console.log(filterOrders);
filterOrders.forEach((oo: any) => {
const isMatch = openOrders.some(
(o: any) => String(o.po).trim() === String(oo.po).trim(),
);
//console.log(isMatch, oo.po);
if (!isMatch) {
//console.log(`ok to update: ${oo.po}`);
// oo = {
// ...oo,
// CustomerOrderNumber: oo.CustomerOrderNumber.replace(" ", ""),
// };
postedOrders.push(oo);
} else {
//console.log(`Not valid order to update: ${oo.po}`);
//console.log(oo)
}
});
// Map Excel data to predefinedObject format
const orders = filterOrders.map((o: any) => {
//console.log(o.po, " ", o.date, format(o.date, "M/d/yyyy HH:mm"));
return {
customerId: customerID,
invoiceAddressId: invoiceID,
customerOrderNo: o.po,
orderDate: new Date(Date.now()).toLocaleString("en-US"),
positions: [
{
deliveryAddressId: 8,
customerArticleNo: o.customerArticleNumber,
quantity: o.qty,
deliveryDate: format(o.date, "M/d/yyyy HH:mm"), // addHours(format(o.date, "M/d/yyyy HH:mm"), 1), //addHours(addDays(o.date, 1), 1), // adding this in so we can over come the constant 1 day behind thing as a work around
customerLineItemNo: 1, // this is how it is currently sent over from abbott
customerReleaseNo: 1, // same as above
},
],
};
});
//console.log(orders);
// combine it all together.
const updatedPredefinedObject = {
...predefinedObject,
orders: [...predefinedObject.orders, ...orders],
};
//console.log(updatedPredefinedObject);
// post the orders to the server
const posting: any = await postData(
{
type: "orders",
endpoint: "/public/v1.0/DemandManagement/ORDERS",
data: updatedPredefinedObject as any,
},
user,
);
return {
success: posting?.success,
message: posting?.message,
data: posting,
};
};

View File

@@ -0,0 +1,197 @@
import * as XLSX from "xlsx";
import { runDatamartQuery } from "../datamart/datamart.controller.js";
import { returnFunc } from "../utils/returnHelper.utils.js";
import { tryCatch } from "../utils/trycatch.utils.js";
import { postData } from "./logistics.dm.postData.js";
export const energizerOrders = async (data: any, user: any) => {
/**
* Standard orders meaning that we get the standard file exported and fill it out and uplaod to lst.
*/
/*
get the order state.
*/
const { data: o, error: oe } = await tryCatch(
runDatamartQuery({ name: "orderState", options: {} }),
);
if (oe) {
return returnFunc({
success: false,
level: "error",
module: "logistics",
subModule: "orders",
message: `Error getting Article info`,
data: [oe.message],
notify: true,
});
}
const openOrders: any = o?.data;
/*
get default invoice address
*/
const { data: invoice, error: ie } = await tryCatch(
runDatamartQuery({ name: "invoiceAddress", options: {} }),
);
if (ie) {
return returnFunc({
success: false,
level: "error",
module: "logistics",
subModule: "orders",
message: `Error getting Article info`,
data: [ie.message],
notify: true,
});
}
const i: any = invoice?.data;
const buffer = Buffer.from(data.buffer);
const workbook = XLSX.read(buffer, { type: "buffer" });
const sheetName = workbook.SheetNames[0] as string;
const sheet = workbook.Sheets[sheetName] as any;
// define custom headers
const headers = [
"ITEM",
"PO",
"ReleaseNo",
"QTY",
"DELDATE",
"COMMENTS",
"What changed",
"CUSTOMERID",
"Remark",
];
const orderData = XLSX.utils.sheet_to_json(sheet, {
defval: "",
header: headers,
range: 1,
});
// the base of the import
const predefinedObject = {
receivingPlantId: process.env.PROD_PLANT_TOKEN ?? "test1",
documentName: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
"en-US",
)}`,
sender: user.username || "lst-system",
externalRefNo: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
"en-US",
)}`,
orders: [],
};
let newOrders: any = orderData;
// for orders that are in od or managed by od we want to make sure we send out an email on this so we dont over right data that could be already planned with a carrier.
const odOrders: any = [];
const okToUpdateOrders: any = [];
for (const order of openOrders) {
if (order.AdditionalInformation1?.includes("od")) {
odOrders.push(order);
} else {
okToUpdateOrders.push(order);
}
}
if (odOrders.length > 0) {
console.log("send email for od touched orders", odOrders);
}
if (okToUpdateOrders.length === 0) {
return returnFunc({
success: false,
level: "error",
module: "logistics",
subModule: "orders",
message: `All orders have been posted to od and releases will not be updated`,
data: [],
notify: false,
});
}
// filter out the orders that have already been started just to reduce the risk of errors.
newOrders.filter((oo: any) =>
okToUpdateOrders.some(
(o: any) => o.CustomerOrderNumber === oo.CustomerOrderNumber,
),
);
// filter out the blanks
newOrders = newOrders.filter((z: any) => z.ITEM !== "");
// let postedOrders: any = [];
// for (const [customerID, orders] of Object.entries(orderData)) {
// // console.log(`Running for Customer ID: ${customerID}`);
// const newOrders: any = orderData;
// // filter out the orders that have already been started just to reduce the risk of errors.
// newOrders.filter((oo: any) =>
// openOrders.some(
// (o: any) => o.CustomerOrderNumber === oo.CustomerOrderNumber
// )
// );
// // map everything out for each order
const nOrder = newOrders.map((o: any) => {
const invoice = i.filter(
(i: any) => i.deliveryAddress === parseInt(o.CUSTOMERID),
);
if (!invoice) {
return null;
}
return {
customerId: parseInt(o.CUSTOMERID),
invoiceAddressId: invoice[0].invoiceAddress, // matched to the default invoice address
customerOrderNo: o.PO,
orderDate: new Date(Date.now()).toLocaleString("en-US"),
positions: [
{
deliveryAddressId: parseInt(o.CUSTOMERID),
customerArticleNo: o.ITEM,
quantity: parseInt(o.QTY),
deliveryDate: o.DELDATE, //excelDateStuff(o.DELDATE),
customerLineItemNo: o.ReleaseNo, // this is how it is currently sent over from abbott
customerReleaseNo: o.ReleaseNo, // same as above
remark: o.COMMENTS === "" ? null : o.COMMENTS,
},
],
};
});
// // do that fun combining thing
const updatedPredefinedObject = {
...predefinedObject,
orders: [...predefinedObject.orders, ...nOrder],
};
// //console.log(updatedPredefinedObject);
// // post the orders to the server
const posting: any = await postData(
{
type: "orders",
endpoint: "/public/v1.0/DemandManagement/ORDERS",
data: updatedPredefinedObject as any,
},
user,
);
return {
customer: nOrder[0].CUSTOMERID,
//totalOrders: orders?.length(),
success: posting.success,
message: posting.message,
data: posting.data,
};
};

View File

@@ -0,0 +1,202 @@
import XLSX from "xlsx";
import { runDatamartQuery } from "../datamart/datamart.controller.js";
import { excelDateStuff } from "../utils/excelToDate.utils.js";
import { returnFunc } from "../utils/returnHelper.utils.js";
import { tryCatch } from "../utils/trycatch.utils.js";
import { postData } from "./logistics.dm.postData.js";
export const macroImportOrders = async (data: any, user: any) => {
/**
* Standard orders meaning that we get the standard file exported and fill it out and uplaod to lst.
*/
const plantToken = process.env.PROD_PLANT_TOKEN;
/*
get the order state.
*/
const { data: o, error: oe } = await tryCatch(
runDatamartQuery({ name: "orderState", options: {} }),
);
if (oe) {
return returnFunc({
success: false,
level: "error",
module: "logistics",
subModule: "orders",
message: `Error getting Article info`,
data: [oe.message],
notify: true,
});
}
const openOrders: any = o?.data;
/*
get default invoice address
*/
const { data: invoice, error: ie } = await tryCatch(
runDatamartQuery({ name: "invoiceAddress", options: {} }),
);
if (ie) {
return returnFunc({
success: false,
level: "error",
module: "logistics",
subModule: "orders",
message: `Error getting Article info`,
data: [ie.message],
notify: true,
});
}
const i: any = invoice?.data;
const buffer = Buffer.from(data.buffer);
const workbook = XLSX.read(buffer, { type: "buffer" });
const sheetName = workbook.SheetNames[0] as string;
const sheet = workbook.Sheets[sheetName] as any;
// define custom headers
const headers = [
"CustomerArticleNumber",
"CustomerOrderNumber",
"CustomerLineNumber",
"CustomerRealeaseNumber",
"Quantity",
"DeliveryDate",
"CustomerID",
"Remark",
];
const orderData = XLSX.utils.sheet_to_json(sheet, {
defval: "",
header: headers,
range: 5,
});
// the base of the import
const predefinedObject = {
receivingPlantId: plantToken ?? "test1",
documentName: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
"en-US",
)}`,
sender: user.username || "lst-system",
externalRefNo: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
"en-US",
)}`,
orders: [],
};
const removeBlanks = orderData.filter(
(n: any) => n.CustomerArticleNumber !== "",
);
const groupedByCustomer: any = removeBlanks.reduce((acc: any, item: any) => {
const id = item.CustomerID;
if (!acc[id]) {
acc[id] = [];
}
acc[id].push(item);
return acc;
}, {});
const postedOrders: any = [];
for (const [customerID, orders] of Object.entries(groupedByCustomer)) {
// console.log(`Running for Customer ID: ${customerID}`);
const filterOrders: any = orders;
const newOrders: any = [];
//newOrders.filter((oo) => openOrders.some((o) => String(o.CustomerOrderNumber) === String(oo.CustomerOrderNumber)));
//console.log(newOrders)
filterOrders.forEach((oo: any) => {
const isMatch = openOrders.some(
(o: any) =>
// check the header
String(o.CustomerOrderNumber).trim() ===
String(oo.CustomerOrderNumber).trim() &&
// and check the customer release is not in here.
String(o.CustomerReleaseNumber).trim() ===
String(oo.CustomerReleaseNumber).trim(),
);
if (!isMatch) {
//console.log(`ok to update: ${oo.CustomerOrderNumber}`);
newOrders.push(oo);
} else {
//console.log(`Not valid order to update: ${oo.CustomerOrderNumber}`);
//console.log(oo)
}
});
// filter out the orders that have already been started just to reduce the risk of errors.
newOrders.filter((oo: any) =>
openOrders.some(
(o: any) => o.CustomerOrderNumber === oo.CustomerOrderNumber,
),
);
// map everything out for each order
const nOrder = newOrders.map((o: any) => {
const invoice = i.find(
(inv: any) => inv.deliveryAddress === parseInt(customerID),
);
if (!invoice) {
return null;
}
return {
customerId: parseInt(customerID),
invoiceAddressId: invoice.invoiceAddress, // matched to the default invoice address
customerOrderNo: o.CustomerOrderNumber,
orderDate: new Date(Date.now()).toLocaleString("en-US"),
positions: [
{
deliveryAddressId: parseInt(customerID),
customerArticleNo: o.CustomerArticleNumber,
quantity: parseInt(o.Quantity),
deliveryDate: excelDateStuff(o.DeliveryDate),
customerLineItemNo: o.CustomerLineNumber, // this is how it is currently sent over from abbott
customerReleaseNo: o.CustomerRealeaseNumber, // same as above
remark: o.remark === "" ? null : o.remark,
},
],
};
});
// do that fun combining thing
const updatedPredefinedObject = {
...predefinedObject,
orders: [...predefinedObject.orders, ...nOrder],
};
//console.log(updatedPredefinedObject);
// post the orders to the server
const posting: any = await postData(
{
type: "orders",
endpoint: "/public/v1.0/DemandManagement/ORDERS",
data: updatedPredefinedObject as any,
},
user,
);
postedOrders.push({
customer: customerID,
//totalOrders: orders?.length(),
success: posting.success,
message: posting.message,
data: posting.data,
});
}
return {
success: true,
message:
"Standard Template was just processed successfully, please check AlplaProd 2.0 to confirm no errors. ",
data: postedOrders,
};
};

View File

@@ -0,0 +1,258 @@
import * as XLSX from "xlsx";
import { runDatamartQuery } from "../datamart/datamart.controller.js";
import { db } from "../db/db.controller.js";
import { excelDateStuff } from "../utils/excelToDate.utils.js";
import { returnFunc } from "../utils/returnHelper.utils.js";
import { tryCatch } from "../utils/trycatch.utils.js";
import { postData } from "./logistics.dm.postData.js";
export const standardOrders = async (data: any, user: any) => {
/**
* Post a standard forecast based on the standard template.
*/
const plantToken = process.env.PROD_PLANT_TOKEN;
/*
get the order state.
*/
const { data: o, error: oe } = await tryCatch(
runDatamartQuery({ name: "orderState", options: {} }),
);
if (oe) {
return returnFunc({
success: false,
level: "error",
module: "logistics",
subModule: "orders",
message: `Error getting Article info`,
data: [oe.message],
notify: true,
});
}
const openOrders: any = o?.data;
/*
get default invoice address
*/
const { data: invoice, error: ie } = await tryCatch(
runDatamartQuery({ name: "invoiceAddress", options: {} }),
);
if (ie) {
return returnFunc({
success: false,
level: "error",
module: "logistics",
subModule: "orders",
message: `Error getting Article info`,
data: [ie.message],
notify: true,
});
}
const i: any = invoice?.data;
//const arrayBuffer = await data.arrayBuffer();
const buffer = Buffer.from(data.buffer);
const workbook = XLSX.read(buffer, { type: "buffer" });
const sheetName = workbook.SheetNames[0] as string;
const sheet = workbook.Sheets[sheetName] as any;
// define custom headers
const headers = [
"CustomerArticleNumber",
"CustomerOrderNumber",
"CustomerLineNumber",
"CustomerRealeaseNumber",
"Quantity",
"DeliveryDate",
"CustomerID",
"Remark",
];
const orderData = XLSX.utils.sheet_to_json(sheet, {
defval: "",
header: headers,
range: 1,
});
// the base of the import
const predefinedObject = {
receivingPlantId: plantToken ?? "test1",
documentName: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
"en-US",
)}`,
sender: user.username || "lst-system",
externalRefNo: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
"en-US",
)}`,
orders: [],
};
// for orders that are in od or managed by od we want to make sure we send out an email on this so we dont over right data that could be already planned with a carrier.
const odOrders: any = [];
const okToUpdateOrders: any = [];
for (const order of openOrders) {
if (order.AdditionalInformation1?.includes("od")) {
odOrders.push(order);
} else {
okToUpdateOrders.push(order);
}
}
if (odOrders.length > 0) {
console.log("send email for od touched orders", odOrders);
}
if (okToUpdateOrders.length === 0) {
return returnFunc({
success: false,
level: "error",
module: "logistics",
subModule: "orders",
message: `All orders have been posted to od and releases will not be updated`,
data: [],
notify: false,
});
}
const groupedByCustomer: any = orderData.reduce((acc: any, item: any) => {
const id = item.CustomerID;
if (!acc[id]) {
acc[id] = [];
}
acc[id].push(item);
return acc;
}, {});
const postedOrders: any = [];
for (const [customerID, orders] of Object.entries(groupedByCustomer)) {
// console.log(`Running for Customer ID: ${customerID}`);
const filterOrders: any = orders;
const newOrders: any = [];
//newOrders.filter((oo) => openOrders.some((o) => String(o.CustomerOrderNumber) === String(oo.CustomerOrderNumber)));
//console.log(newOrders)
filterOrders.forEach((oo: any) => {
const isMatch = okToUpdateOrders.some(
(o: any) =>
// check the header
String(o.CustomerOrderNumber).trim() ===
String(oo.CustomerOrderNumber).trim() &&
// and check the customer release is not in here.
String(o.CustomerRealeaseNumber).trim() ===
String(oo.CustomerRealeaseNumber).trim(),
);
if (!isMatch) {
//console.log(`ok to update: ${oo.CustomerOrderNumber}`);
newOrders.push(oo);
} else {
//console.log(`Not valid order to update: ${oo.CustomerOrderNumber}`);
//console.log(oo)
}
});
// filter out the orders that have already been started just to reduce the risk of errors.
newOrders.filter((oo: any) =>
openOrders.some(
(o: any) => o.CustomerOrderNumber === oo.CustomerOrderNumber,
),
);
// get the default time to put in here if an order dose not include the date
const { data: s, error } = await tryCatch(
db.query.settings.findFirst({
where: (setting, { eq }) => eq(setting.name, "defaultOrderTime"),
}),
);
if (error) {
return returnFunc({
success: false,
level: "error",
module: "utils",
subModule: "excelToDate",
message: "Failed to get the default order time setting.",
data: [],
notify: false,
});
}
const hour = parseInt(s?.value ?? "8", 10);
const defaultOrderTime =
Number.isNaN(hour) || hour < 1 || hour > 23 ? 800 : hour * 100;
// map everything out for each order
const nOrder = newOrders.map((o: any) => {
const invoice = i.find(
(inv: any) => inv.deliveryAddress === parseInt(customerID),
);
if (!invoice) {
return returnFunc({
success: false,
level: "error",
module: "logistics",
subModule: "orders",
message: `No invoice address found for ${customerID}`,
data: [],
notify: true,
});
}
return {
customerId: parseInt(customerID),
invoiceAddressId: invoice.invoiceAddress, // matched to the default invoice address
customerOrderNo: o.CustomerOrderNumber,
orderDate: new Date(Date.now()).toLocaleString("en-US"),
positions: [
{
deliveryAddressId: parseInt(customerID),
customerArticleNo: o.CustomerArticleNumber,
quantity: parseInt(o.Quantity),
deliveryDate: excelDateStuff(o.DeliveryDate, defaultOrderTime),
customerLineItemNo: o.CustomerLineNumber, // this is how it is currently sent over from abbott
customerReleaseNo: o.CustomerRealeaseNumber, // same as above
remark: o.Remark === "" ? null : o.Remark,
},
],
};
});
// do that fun combining thing
const updatedPredefinedObject = {
...predefinedObject,
orders: [...predefinedObject.orders, ...nOrder],
};
// post the orders to the server
const posting: any = await postData(
{
type: "orders",
endpoint: "/public/v1.0/DemandManagement/ORDERS",
data: updatedPredefinedObject as any,
},
user,
);
postedOrders.push({
customer: customerID,
//totalOrders: orders?.length(),
success: posting.success,
message: posting.message,
data: posting.data,
});
}
return {
success: postedOrders[0].success,
message: postedOrders[0].message,
data: postedOrders,
};
};

View File

@@ -0,0 +1,104 @@
import { Router } from "express";
import multer from "multer";
import { requireAuth } from "../middleware/auth.middleware.js";
import { apiReturn } from "../utils/returnHelper.utils.js";
import { abbottOrders } from "./logistics.dm.orders.map.abbott.js";
import { energizerOrders } from "./logistics.dm.orders.map.energizer.js";
import { macroImportOrders } from "./logistics.dm.orders.map.macroImport.js";
import { standardOrders } from "./logistics.dm.orders.map.standard.js";
import { scjOrders } from "./logistics.dm.orders.scj.js";
type ForecastResult = {
success?: boolean;
message?: string;
data?: unknown;
};
const r = Router();
const upload = multer({
storage: multer.memoryStorage(),
});
r.post("/", requireAuth, upload.single("file"), async (req, res) => {
if (!req.file) {
return apiReturn(res, {
success: false,
level: "error",
module: "dm",
subModule: "orders",
message: "A file must be added to be able to run the forecast.",
data: [],
status: 400,
});
}
const { fileType } = req.body;
if (typeof fileType !== "string") {
return apiReturn(res, {
success: false,
level: "error",
module: "dm",
subModule: "orders",
message: "A fileType must be provided.",
data: [],
status: 400,
});
}
//console.log("fileType:", req.body.fileType);
let result: ForecastResult;
switch (fileType) {
case "standard":
result = await standardOrders(req.file, req.user);
break;
case "abbott":
// daytons orders
result = await abbottOrders(req.file, req.user);
break;
case "energizer":
// daytons orders
result = await energizerOrders(req.file, req.user);
break;
case "macro":
result = await macroImportOrders(req.file, req.user);
break;
case "scj":
// this is for west bend orders
result = await scjOrders(req.file, req.user);
break;
default:
return apiReturn(res, {
success: false,
level: "error",
module: "dm",
subModule: "orders",
message: `Invalid fileType: ${fileType}`,
data: [],
status: 400,
});
}
return apiReturn(res, {
success: result.success ?? false,
level: result.success ? "info" : "error",
module: "dm",
subModule: "orders",
message: result.success
? "The orders was accepted by Alplaprod 2.0 please check to make sure everything processed properly."
: (result.message as string),
data: result.data ?? ([] as any),
status: result.success ? 200 : 500,
});
});
export default r;

View File

@@ -0,0 +1,175 @@
import * as XLSX from "xlsx";
import { runDatamartQuery } from "../datamart/datamart.controller.js";
import { excelDateStuff } from "../utils/excelToDate.utils.js";
import { returnFunc } from "../utils/returnHelper.utils.js";
import { tryCatch } from "../utils/trycatch.utils.js";
import { postData } from "./logistics.dm.postData.js";
export const scjOrders = async (data: any, user: any) => {
/**
* Standard orders meaning that we get the standard file exported and fill it out and uplaod to lst.
*/
const customerID = 48;
const plantToken = process.env.PROD_PLANT_TOKEN;
/*
get the order state.
*/
const { data: o, error: oe } = await tryCatch(
runDatamartQuery({ name: "orderState", options: {} }),
);
if (oe) {
return returnFunc({
success: false,
level: "error",
module: "logistics",
subModule: "orders",
message: `Error getting Article info`,
data: [oe.message],
notify: true,
});
}
const openOrders: any = o?.data;
/*
get default invoice address
*/
const { data: invoice, error: ie } = await tryCatch(
runDatamartQuery({ name: "invoiceAddress", options: {} }),
);
if (ie) {
return returnFunc({
success: false,
level: "error",
module: "logistics",
subModule: "orders",
message: `Error getting Article info`,
data: [ie.message],
notify: true,
});
}
const i: any = invoice?.data;
const buffer = Buffer.from(data.buffer);
const workbook = XLSX.read(buffer, { type: "buffer" });
const sheetName = workbook.SheetNames[0] as string;
const sheet = workbook.Sheets[sheetName] as any;
// define custom headers
const headers = [
"ItemNo",
"Description",
"DeliveryDate",
"Quantity",
"PO",
"Releases",
"remarks",
];
const orderData = XLSX.utils.sheet_to_json(sheet, {
defval: "",
header: headers,
range: 1,
});
// the base of the import
const predefinedObject = {
receivingPlantId: plantToken ?? "test1",
documentName: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
"en-US",
)}`,
sender: user.username || "lst-system",
externalRefNo: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
"en-US",
)}`,
orders: [],
};
let newOrders: any = orderData;
// filter out the orders that have already been started just to reduce the risk of errors.
newOrders.filter((oo: any) =>
openOrders.some(
(o: any) => o.CustomerOrderNumber === oo.CustomerOrderNumber,
),
);
// filter out the blanks
newOrders = newOrders.filter((z: any) => z.ItemNo !== "");
const nOrder = newOrders.map((o: any) => {
const invoice = i.filter((i: any) => i.deliveryAddress === customerID);
if (!invoice) {
return null;
}
if (o.Releases === "") {
return null;
}
if (o.PO === "") {
return null;
}
const date = isNaN(o.DeliveryDate)
? new Date(o.DeliveryDate)
: excelDateStuff(o.DeliveryDate);
return {
customerId: customerID,
invoiceAddressId: invoice[0].invoiceAddress, // matched to the default invoice address
customerOrderNo: o.PO,
orderDate: new Date(Date.now()).toLocaleString("en-US"),
positions: [
{
deliveryAddressId: customerID,
customerArticleNo: o.ItemNo,
quantity: parseInt(o.Quantity),
deliveryDate: date, //excelDateStuff(o.DELDATE),
customerLineItemNo: o.PO, // this is how it is currently sent over from abbott
customerReleaseNo: o.Releases, // same as above
remark: o.remarks === "" ? null : o.remarks,
},
],
};
});
//console.log(nOrder.filter((o: any) => o !== undefined));
// // do that fun combining thing
const updatedPredefinedObject = {
...predefinedObject,
orders: [
...predefinedObject.orders,
...nOrder.filter((o: any) => o !== undefined),
],
};
//console.log(updatedPredefinedObject.orders[0]);
// // post the orders to the server
const posting: any = await postData(
{
type: "orders",
endpoint: "/public/v1.0/DemandManagement/ORDERS",
data: updatedPredefinedObject as any,
},
user,
);
return {
customer: customerID,
//totalOrders: orders?.length(),
success: posting.success,
message: posting.message,
data: posting.data,
};
};

View File

@@ -1,5 +1,6 @@
import { db } from "../db/db.controller.js";
import { forecastImport } from "../db/schema/forecastImports.schema.js";
import { orderImport } from "../db/schema/ordersImports.schema.js";
import { runProdApi } from "../utils/prodEndpoint.utils.js";
import { returnFunc } from "../utils/returnHelper.utils.js";
@@ -8,6 +9,7 @@ type PostData = {
documentName: string;
sender: string;
customerId: string;
invoiceAddressId?: string;
positions: unknown[];
};
type Data = {
@@ -52,6 +54,19 @@ export const postData = async (data: Data, user: any) => {
});
}
if (data.type === "orders") {
await db.insert(orderImport).values({
receivingPlantId: data.data.receivingPlantId ?? "test1",
documentName: data.data.documentName ?? "order-data-missing",
sender: data.data.sender ?? "lst-user",
customerId: data.data.customerId ?? "0",
invoiceAddressId: data.data.invoiceAddressId ?? "0",
rawData: data ?? [],
add_user: user.username ?? undefined,
upd_user: user.username ?? undefined,
});
}
return returnFunc({
success: true,
level: "info",

View File

@@ -1,6 +1,7 @@
import type { Express } from "express";
import { featureCheck } from "../middleware/featureActive.middleware.js";
import forecast from "./logistics.dm.forecast.route.js";
import orders from "./logistics.dm.orders.route.js";
import createTemplate from "./logistics.dm.template.route.js";
export const setupLogisticsRoutes = (baseUrl: string, app: Express) => {
@@ -17,5 +18,11 @@ export const setupLogisticsRoutes = (baseUrl: string, app: Express) => {
forecast,
);
app.use(
`${baseUrl}/api/logistics/dm/orders`,
featureCheck("demandManagement"),
orders,
);
// all other system should be under /api/system/*
};