feat(dm): standard forecast added in rest of migration to come
This commit is contained in:
22
backend/db/schema/forecastImports.schema.ts
Normal file
22
backend/db/schema/forecastImports.schema.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { jsonb, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||||
|
import type { z } from "zod";
|
||||||
|
|
||||||
|
export const forecastImport = pgTable("forecast_import", {
|
||||||
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
|
receivingPlantId: text("receiving_plant_id").notNull(),
|
||||||
|
documentName: text("documentName"),
|
||||||
|
sender: text("sender"),
|
||||||
|
customerId: text("customer_id"),
|
||||||
|
rawData: jsonb("raw_data").default([]),
|
||||||
|
add_date: timestamp("add_date", { withTimezone: true }).defaultNow(),
|
||||||
|
add_user: text("add_user").default("lst-system"),
|
||||||
|
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||||
|
upd_user: text("upd_user").default("lst-system"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const forecastImportSchema = createSelectSchema(forecastImport);
|
||||||
|
export const newForecastImportSchema = createInsertSchema(forecastImport);
|
||||||
|
|
||||||
|
export type ForecastImport = z.infer<typeof forecastImportSchema>;
|
||||||
|
export type NeworecastImport = z.infer<typeof newForecastImportSchema>;
|
||||||
95
backend/logistics/logistics.dm.forecast.route.ts
Normal file
95
backend/logistics/logistics.dm.forecast.route.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
import multer from "multer";
|
||||||
|
import { requireAuth } from "../middleware/auth.middleware.js";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
import { standardForecast } from "./logsitcs.dm.forecast.map.standard.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: "forecast",
|
||||||
|
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: "forecast",
|
||||||
|
message: "A fileType must be provided.",
|
||||||
|
data: [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log("fileType:", req.body.fileType);
|
||||||
|
|
||||||
|
let result: ForecastResult;
|
||||||
|
|
||||||
|
switch (fileType) {
|
||||||
|
case "standard":
|
||||||
|
result = await standardForecast(req.file, req.user);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "loreal":
|
||||||
|
`result = await lorealForecast(req.file, req.user);`;
|
||||||
|
result = { success: true, message: "standardForecast", data: [] };
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "pg":
|
||||||
|
`result = await pNgForecast(req.file, req.user);`;
|
||||||
|
result = { success: true, message: "standardForecast", data: [] };
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "energizer":
|
||||||
|
`result = await energizerForecast(req.file, req.user);`;
|
||||||
|
result = { success: true, message: "standardForecast", data: [] };
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "dm",
|
||||||
|
subModule: "forecast",
|
||||||
|
message: `Invalid fileType: ${fileType}`,
|
||||||
|
data: [],
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: result.success ?? false,
|
||||||
|
level: result.success ? "info" : "error",
|
||||||
|
module: "dm",
|
||||||
|
subModule: "forecast",
|
||||||
|
message: result.success
|
||||||
|
? "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,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default r;
|
||||||
49
backend/logistics/logistics.dm.postForecast.ts
Normal file
49
backend/logistics/logistics.dm.postForecast.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { db } from "../db/db.controller.js";
|
||||||
|
import { forecastImport } from "../db/schema/forecastImports.schema.js";
|
||||||
|
import { runProdApi } from "../utils/prodEndpoint.utils.js";
|
||||||
|
import { returnFunc } from "../utils/returnHelper.utils.js";
|
||||||
|
|
||||||
|
export const postForecast = async (data: any, user: any) => {
|
||||||
|
const forecast = await runProdApi(
|
||||||
|
{
|
||||||
|
method: "post",
|
||||||
|
endpoint: "/public/v1.0/DemandManagement/DELFOR",
|
||||||
|
data: [data],
|
||||||
|
},
|
||||||
|
"Forecast post",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!forecast?.success) {
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "dm",
|
||||||
|
subModule: "forecast",
|
||||||
|
message: forecast?.message ?? "Error in posting the forecast data",
|
||||||
|
data: forecast?.data ?? [],
|
||||||
|
notify: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forecast.success) {
|
||||||
|
await db.insert(forecastImport).values({
|
||||||
|
receivingPlantId: data.receivingPlantId ?? "test1",
|
||||||
|
documentName: data.documentName ?? "forecast-data-missing",
|
||||||
|
sender: data.sender ?? "lst-user",
|
||||||
|
customerId: data.customerId ?? "0",
|
||||||
|
rawData: data ?? [],
|
||||||
|
add_user: user.username ?? undefined,
|
||||||
|
upd_user: user.username ?? undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
return returnFunc({
|
||||||
|
success: true,
|
||||||
|
level: "info",
|
||||||
|
module: "dm",
|
||||||
|
subModule: "forecast",
|
||||||
|
message: forecast?.message ?? "",
|
||||||
|
data: data ?? [],
|
||||||
|
notify: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
79
backend/logistics/logistics.dm.template.route.ts
Normal file
79
backend/logistics/logistics.dm.template.route.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { format } from "date-fns";
|
||||||
|
import { Router } from "express";
|
||||||
|
import { requireAuth } from "../middleware/auth.middleware.js";
|
||||||
|
import { excelTemplate, type Template } from "../utils/excelTemplates.utils.js";
|
||||||
|
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||||
|
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
r.get("/", requireAuth, async (req, res) => {
|
||||||
|
const { filename } = req.query;
|
||||||
|
|
||||||
|
const templateNames = ["orders", "forecast"];
|
||||||
|
if (typeof filename !== "string" || !templateNames.includes(filename)) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "dm",
|
||||||
|
subModule: "template",
|
||||||
|
message: "You are required to pass over the template name.",
|
||||||
|
data: [],
|
||||||
|
status: 500,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = `${filename}-Template-${format(
|
||||||
|
new Date(Date.now()),
|
||||||
|
"M-d-yyyy",
|
||||||
|
)}.xlsx`;
|
||||||
|
|
||||||
|
const standardHeaders = [
|
||||||
|
"CustomerArticleNumber",
|
||||||
|
"CustomerOrderNumber",
|
||||||
|
"CustomerLineNumber",
|
||||||
|
"CustomerRealeaseNumber",
|
||||||
|
"Quantity",
|
||||||
|
"DeliveryDate",
|
||||||
|
"CustomerID",
|
||||||
|
"Remark",
|
||||||
|
// "InvoiceID",
|
||||||
|
];
|
||||||
|
|
||||||
|
const forecastHeaders = [
|
||||||
|
"CustomerArticleNumber",
|
||||||
|
"Quantity",
|
||||||
|
"RequirementDate",
|
||||||
|
"CustomerID",
|
||||||
|
];
|
||||||
|
|
||||||
|
const template = {
|
||||||
|
name,
|
||||||
|
headers: filename === "orders" ? standardHeaders : forecastHeaders,
|
||||||
|
} as Template;
|
||||||
|
|
||||||
|
//console.log(template);
|
||||||
|
const { data, error } = await tryCatch(excelTemplate(template));
|
||||||
|
|
||||||
|
if (error || !data) {
|
||||||
|
return apiReturn(res, {
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "dm",
|
||||||
|
subModule: "template",
|
||||||
|
message: "There was an error creating the Excel template.",
|
||||||
|
data: [],
|
||||||
|
status: 500,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.set({
|
||||||
|
"Content-Type":
|
||||||
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
"Content-Disposition": `attachment; filename="${name}"`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).send(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default r;
|
||||||
21
backend/logistics/logistics.routes.ts
Normal file
21
backend/logistics/logistics.routes.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import type { Express } from "express";
|
||||||
|
import { featureCheck } from "../middleware/featureActive.middleware.js";
|
||||||
|
import forecast from "./logistics.dm.forecast.route.js";
|
||||||
|
import createTemplate from "./logistics.dm.template.route.js";
|
||||||
|
|
||||||
|
export const setupLogisticsRoutes = (baseUrl: string, app: Express) => {
|
||||||
|
//stats will be like this as we dont need to change this
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
`${baseUrl}/api/logistics/dm/template`,
|
||||||
|
featureCheck("demandManagement"),
|
||||||
|
createTemplate,
|
||||||
|
);
|
||||||
|
app.use(
|
||||||
|
`${baseUrl}/api/logistics/dm/forecast`,
|
||||||
|
featureCheck("demandManagement"),
|
||||||
|
forecast,
|
||||||
|
);
|
||||||
|
|
||||||
|
// all other system should be under /api/system/*
|
||||||
|
};
|
||||||
97
backend/logistics/logsitcs.dm.forecast.map.standard.ts
Normal file
97
backend/logistics/logsitcs.dm.forecast.map.standard.ts
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import * as XLSX from "xlsx";
|
||||||
|
import { excelDateStuff } from "../utils/excelToDate.utils.js";
|
||||||
|
import { postForecast } from "./logistics.dm.postForecast.js";
|
||||||
|
export const standardForecast = async (data: any, user: any) => {
|
||||||
|
/**
|
||||||
|
* Post a standard forecast based on the standard template.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const plantToken = process.env.PROD_PLANT_TOKEN;
|
||||||
|
|
||||||
|
//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;
|
||||||
|
|
||||||
|
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,
|
||||||
|
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
|
||||||
|
const 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: foreCastData[0].success,
|
||||||
|
message: foreCastData[0].message,
|
||||||
|
data: foreCastData,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -7,6 +7,7 @@ import { setupDatamartRoutes } from "./datamart/datamart.routes.js";
|
|||||||
import { setupDockDoorRoutes } from "./dockdoorScanning/dockdoor.routes.js";
|
import { setupDockDoorRoutes } from "./dockdoorScanning/dockdoor.routes.js";
|
||||||
import { setupEomRoutes } from "./eom/eom.routes.js";
|
import { setupEomRoutes } from "./eom/eom.routes.js";
|
||||||
import { setupGPSqlRoutes } from "./gpSql/gpSql.routes.js";
|
import { setupGPSqlRoutes } from "./gpSql/gpSql.routes.js";
|
||||||
|
import { setupLogisticsRoutes } from "./logistics/logistics.routes.js";
|
||||||
import { setupMobileRoutes } from "./mobile/mobile.routes.js";
|
import { setupMobileRoutes } from "./mobile/mobile.routes.js";
|
||||||
import { setupNotificationRoutes } from "./notification/notification.routes.js";
|
import { setupNotificationRoutes } from "./notification/notification.routes.js";
|
||||||
import { setupOCPRoutes } from "./ocp/ocp.routes.js";
|
import { setupOCPRoutes } from "./ocp/ocp.routes.js";
|
||||||
@@ -33,4 +34,5 @@ export const setupRoutes = (baseUrl: string, app: Express) => {
|
|||||||
setupTCPRoutes(baseUrl, app);
|
setupTCPRoutes(baseUrl, app);
|
||||||
setupDockDoorRoutes(baseUrl, app);
|
setupDockDoorRoutes(baseUrl, app);
|
||||||
setupEomRoutes(baseUrl, app);
|
setupEomRoutes(baseUrl, app);
|
||||||
|
setupLogisticsRoutes(baseUrl, app);
|
||||||
};
|
};
|
||||||
|
|||||||
49
backend/utils/excelTemplates.utils.ts
Normal file
49
backend/utils/excelTemplates.utils.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import * as XLSX from "xlsx";
|
||||||
|
|
||||||
|
export type Template = {
|
||||||
|
name: string;
|
||||||
|
headers: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const excelTemplate = async (data: Template) => {
|
||||||
|
/**
|
||||||
|
* Creates the standard Template for bulk orders in
|
||||||
|
*/
|
||||||
|
|
||||||
|
// const headers = [
|
||||||
|
// [
|
||||||
|
// "CustomerArticleNumber",
|
||||||
|
// "CustomerOrderNumber",
|
||||||
|
// "CustomerLineNumber",
|
||||||
|
// "CustomerRealeaseNumber",
|
||||||
|
// "Quantity",
|
||||||
|
// "DeliveryDate",
|
||||||
|
// "CustomerID",
|
||||||
|
// "Remark",
|
||||||
|
// // "InvoiceID",
|
||||||
|
// ],
|
||||||
|
// ];
|
||||||
|
|
||||||
|
// create a new workbook
|
||||||
|
const wb = XLSX.utils.book_new();
|
||||||
|
const ws = XLSX.utils.aoa_to_sheet([data.headers]);
|
||||||
|
//const ws2 = XLSX.utils.aoa_to_sheet(headers2);
|
||||||
|
|
||||||
|
const columnWidths = data.headers.map((header) => ({
|
||||||
|
width: header.length + 2,
|
||||||
|
}));
|
||||||
|
|
||||||
|
ws["!cols"] = columnWidths;
|
||||||
|
|
||||||
|
// append the worksheet to the workbook
|
||||||
|
XLSX.utils.book_append_sheet(wb, ws, `Sheet1`);
|
||||||
|
//XLSX.utils.book_append_sheet(wb, ws2, `Sheet2`);
|
||||||
|
|
||||||
|
// Creates the file to disk'
|
||||||
|
// XLSX.writeFile(wb, data.name);
|
||||||
|
|
||||||
|
// Write the workbook to a buffer and return it
|
||||||
|
const excelBuffer = XLSX.write(wb, { bookType: "xlsx", type: "buffer" });
|
||||||
|
|
||||||
|
return excelBuffer;
|
||||||
|
};
|
||||||
28
backend/utils/excelToDate.utils.ts
Normal file
28
backend/utils/excelToDate.utils.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { getJsDateFromExcel } from "excel-date-to-js";
|
||||||
|
|
||||||
|
export const excelDateStuff = (serial: number, time?: any) => {
|
||||||
|
if (typeof serial !== "number" || serial <= 0) {
|
||||||
|
return "invalid Date";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default time to 8:00 AM if not provided
|
||||||
|
if (!time) {
|
||||||
|
time = 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get base date from Excel serial (this gives you UTC midnight)
|
||||||
|
const date = getJsDateFromExcel(serial);
|
||||||
|
|
||||||
|
const localOffset = new Date().getTimezoneOffset() / 60;
|
||||||
|
const hours = Math.floor(time / 100);
|
||||||
|
const minutes = time % 100;
|
||||||
|
|
||||||
|
// Set the time in UTC
|
||||||
|
date.setUTCHours(hours + localOffset);
|
||||||
|
date.setUTCMinutes(minutes);
|
||||||
|
date.setUTCSeconds(0);
|
||||||
|
date.setUTCMilliseconds(0);
|
||||||
|
|
||||||
|
//console.log(date.toISOString(), serial, time);
|
||||||
|
return date.toISOString();
|
||||||
|
};
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import https from "node:https";
|
import https from "node:https";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { createLogger } from "../logger/logger.controller.js";
|
||||||
import { returnFunc } from "./returnHelper.utils.js";
|
import { returnFunc } from "./returnHelper.utils.js";
|
||||||
import { tryCatch } from "./trycatch.utils.js";
|
import { tryCatch } from "./trycatch.utils.js";
|
||||||
|
|
||||||
@@ -59,9 +60,14 @@ export const prodEndpointCreation = async (endpoint: string) => {
|
|||||||
* @param timeoutDelay
|
* @param timeoutDelay
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const runProdApi = async (data: Data) => {
|
export const runProdApi = async (data: Data, name?: string) => {
|
||||||
|
const log = createLogger({ module: "utils", subModule: "prodEndpoints" });
|
||||||
const url = await prodEndpointCreation(data.endpoint);
|
const url = await prodEndpointCreation(data.endpoint);
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
{ stack: data },
|
||||||
|
`Info passed over for ${name ? name : "Missing name"}`,
|
||||||
|
);
|
||||||
const { data: d, error } = await tryCatch(
|
const { data: d, error } = await tryCatch(
|
||||||
axios({
|
axios({
|
||||||
method: data.method as string,
|
method: data.method as string,
|
||||||
@@ -94,7 +100,7 @@ export const runProdApi = async (data: Data) => {
|
|||||||
level: "error",
|
level: "error",
|
||||||
module: "utils",
|
module: "utils",
|
||||||
subModule: "prodEndpoint",
|
subModule: "prodEndpoint",
|
||||||
message: "Data from prod endpoint",
|
message: "Error data from prod endpoint",
|
||||||
data: d.data,
|
data: d.data,
|
||||||
notify: false,
|
notify: false,
|
||||||
});
|
});
|
||||||
@@ -104,10 +110,30 @@ export const runProdApi = async (data: Data) => {
|
|||||||
level: "error",
|
level: "error",
|
||||||
module: "utils",
|
module: "utils",
|
||||||
subModule: "prodEndpoint",
|
subModule: "prodEndpoint",
|
||||||
message: "Data from prod endpoint",
|
message: "Error data from prod endpoint",
|
||||||
data: d.data,
|
data: d.data,
|
||||||
notify: false,
|
notify: false,
|
||||||
});
|
});
|
||||||
|
case 500:
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "utils",
|
||||||
|
subModule: "prodEndpoint",
|
||||||
|
message: "Error data from prod endpoint",
|
||||||
|
data: d.data,
|
||||||
|
notify: false,
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "utils",
|
||||||
|
subModule: "prodEndpoint",
|
||||||
|
message: "Unknown error encountered",
|
||||||
|
data: d?.data as any,
|
||||||
|
notify: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ export interface ReturnHelper<T = unknown[]> {
|
|||||||
| "admin"
|
| "admin"
|
||||||
| "mobile"
|
| "mobile"
|
||||||
| "dockdoor"
|
| "dockdoor"
|
||||||
| "eom";
|
| "eom"
|
||||||
|
| "dm";
|
||||||
subModule: string;
|
subModule: string;
|
||||||
|
|
||||||
level: "info" | "error" | "debug" | "fatal" | "warn";
|
level: "info" | "error" | "debug" | "fatal" | "warn";
|
||||||
|
|||||||
13
migrations/0064_magical_lady_mastermind.sql
Normal file
13
migrations/0064_magical_lady_mastermind.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
CREATE TABLE "forecast_import" (
|
||||||
|
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||||
|
"receiving_plant_id" text NOT NULL,
|
||||||
|
"documentName" text,
|
||||||
|
"sender" text,
|
||||||
|
"customer_id" text,
|
||||||
|
"raw_data" jsonb DEFAULT '[]'::jsonb,
|
||||||
|
"add_date" timestamp with time zone DEFAULT now(),
|
||||||
|
"add_user" text DEFAULT 'lst-system',
|
||||||
|
"upd_date" timestamp with time zone DEFAULT now(),
|
||||||
|
"upd_user" text DEFAULT 'lst-system',
|
||||||
|
CONSTRAINT "forecast_import_documentName_unique" UNIQUE("documentName")
|
||||||
|
);
|
||||||
1
migrations/0065_jittery_ares.sql
Normal file
1
migrations/0065_jittery_ares.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "forecast_import" DROP CONSTRAINT "forecast_import_documentName_unique";
|
||||||
2783
migrations/meta/0064_snapshot.json
Normal file
2783
migrations/meta/0064_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
2775
migrations/meta/0065_snapshot.json
Normal file
2775
migrations/meta/0065_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -449,6 +449,20 @@
|
|||||||
"when": 1781045714275,
|
"when": 1781045714275,
|
||||||
"tag": "0063_illegal_mauler",
|
"tag": "0063_illegal_mauler",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 64,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1781425987022,
|
||||||
|
"tag": "0064_magical_lady_mastermind",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 65,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1781426193735,
|
||||||
|
"tag": "0065_jittery_ares",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
102
package-lock.json
generated
102
package-lock.json
generated
@@ -25,6 +25,7 @@
|
|||||||
"drizzle-kit": "^0.31.10",
|
"drizzle-kit": "^0.31.10",
|
||||||
"drizzle-orm": "^0.45.1",
|
"drizzle-orm": "^0.45.1",
|
||||||
"drizzle-zod": "^0.8.3",
|
"drizzle-zod": "^0.8.3",
|
||||||
|
"excel-date-to-js": "^1.1.5",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"ldapts": "^8.1.7",
|
"ldapts": "^8.1.7",
|
||||||
@@ -42,6 +43,7 @@
|
|||||||
"powershell": "^2.3.3",
|
"powershell": "^2.3.3",
|
||||||
"socket.io": "^4.8.3",
|
"socket.io": "^4.8.3",
|
||||||
"socket.io-client": "^4.8.3",
|
"socket.io-client": "^4.8.3",
|
||||||
|
"xlsx": "^0.18.5",
|
||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -3412,6 +3414,15 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/adler-32": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/agent-base": {
|
"node_modules/agent-base": {
|
||||||
"version": "7.1.4",
|
"version": "7.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
|
||||||
@@ -4301,6 +4312,19 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cfb": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"adler-32": "~1.3.0",
|
||||||
|
"crc-32": "~1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chai": {
|
"node_modules/chai": {
|
||||||
"version": "6.2.2",
|
"version": "6.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
|
||||||
@@ -4402,6 +4426,15 @@
|
|||||||
"node": ">=0.8"
|
"node": ">=0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/codepage": {
|
||||||
|
"version": "1.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
|
||||||
|
"integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
@@ -7634,6 +7667,15 @@
|
|||||||
"bare-events": "^2.7.0"
|
"bare-events": "^2.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/excel-date-to-js": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/excel-date-to-js/-/excel-date-to-js-1.1.5.tgz",
|
||||||
|
"integrity": "sha512-grZW0MPye0VGCzLNljI7H22QWgrI8/hkTCvIUczYsQTTSaPQU/UTcz1fBPHNxWKpiv8Zu2I/98z+aAnlp6STNw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/execa": {
|
"node_modules/execa": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
|
||||||
@@ -8176,6 +8218,15 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/frac": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fresh": {
|
"node_modules/fresh": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
|
||||||
@@ -12461,6 +12512,18 @@
|
|||||||
"node": ">= 10.x"
|
"node": ">= 10.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ssf": {
|
||||||
|
"version": "0.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
|
||||||
|
"integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"frac": "~1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/stackback": {
|
"node_modules/stackback": {
|
||||||
"version": "0.0.2",
|
"version": "0.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
|
||||||
@@ -13581,6 +13644,24 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/wmf": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/word": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/word-wrap": {
|
"node_modules/word-wrap": {
|
||||||
"version": "1.2.5",
|
"version": "1.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
||||||
@@ -13674,6 +13755,27 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/xlsx": {
|
||||||
|
"version": "0.18.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
|
||||||
|
"integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"adler-32": "~1.3.0",
|
||||||
|
"cfb": "~1.2.1",
|
||||||
|
"codepage": "~1.15.0",
|
||||||
|
"crc-32": "~1.2.1",
|
||||||
|
"ssf": "~0.11.2",
|
||||||
|
"wmf": "~1.0.1",
|
||||||
|
"word": "~0.3.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"xlsx": "bin/xlsx.njs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/xmlhttprequest-ssl": {
|
"node_modules/xmlhttprequest-ssl": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
|
||||||
|
|||||||
@@ -83,6 +83,7 @@
|
|||||||
"drizzle-kit": "^0.31.10",
|
"drizzle-kit": "^0.31.10",
|
||||||
"drizzle-orm": "^0.45.1",
|
"drizzle-orm": "^0.45.1",
|
||||||
"drizzle-zod": "^0.8.3",
|
"drizzle-zod": "^0.8.3",
|
||||||
|
"excel-date-to-js": "^1.1.5",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"ldapts": "^8.1.7",
|
"ldapts": "^8.1.7",
|
||||||
@@ -100,6 +101,7 @@
|
|||||||
"powershell": "^2.3.3",
|
"powershell": "^2.3.3",
|
||||||
"socket.io": "^4.8.3",
|
"socket.io": "^4.8.3",
|
||||||
"socket.io-client": "^4.8.3",
|
"socket.io-client": "^4.8.3",
|
||||||
|
"xlsx": "^0.18.5",
|
||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
|||||||
Reference in New Issue
Block a user