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 { setupEomRoutes } from "./eom/eom.routes.js";
|
||||
import { setupGPSqlRoutes } from "./gpSql/gpSql.routes.js";
|
||||
import { setupLogisticsRoutes } from "./logistics/logistics.routes.js";
|
||||
import { setupMobileRoutes } from "./mobile/mobile.routes.js";
|
||||
import { setupNotificationRoutes } from "./notification/notification.routes.js";
|
||||
import { setupOCPRoutes } from "./ocp/ocp.routes.js";
|
||||
@@ -33,4 +34,5 @@ export const setupRoutes = (baseUrl: string, app: Express) => {
|
||||
setupTCPRoutes(baseUrl, app);
|
||||
setupDockDoorRoutes(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 axios from "axios";
|
||||
import { createLogger } from "../logger/logger.controller.js";
|
||||
import { returnFunc } from "./returnHelper.utils.js";
|
||||
import { tryCatch } from "./trycatch.utils.js";
|
||||
|
||||
@@ -59,9 +60,14 @@ export const prodEndpointCreation = async (endpoint: string) => {
|
||||
* @param timeoutDelay
|
||||
* @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);
|
||||
|
||||
log.debug(
|
||||
{ stack: data },
|
||||
`Info passed over for ${name ? name : "Missing name"}`,
|
||||
);
|
||||
const { data: d, error } = await tryCatch(
|
||||
axios({
|
||||
method: data.method as string,
|
||||
@@ -94,7 +100,7 @@ export const runProdApi = async (data: Data) => {
|
||||
level: "error",
|
||||
module: "utils",
|
||||
subModule: "prodEndpoint",
|
||||
message: "Data from prod endpoint",
|
||||
message: "Error data from prod endpoint",
|
||||
data: d.data,
|
||||
notify: false,
|
||||
});
|
||||
@@ -104,10 +110,30 @@ export const runProdApi = async (data: Data) => {
|
||||
level: "error",
|
||||
module: "utils",
|
||||
subModule: "prodEndpoint",
|
||||
message: "Data from prod endpoint",
|
||||
message: "Error data from prod endpoint",
|
||||
data: d.data,
|
||||
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) {
|
||||
|
||||
@@ -18,7 +18,8 @@ export interface ReturnHelper<T = unknown[]> {
|
||||
| "admin"
|
||||
| "mobile"
|
||||
| "dockdoor"
|
||||
| "eom";
|
||||
| "eom"
|
||||
| "dm";
|
||||
subModule: string;
|
||||
|
||||
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,
|
||||
"tag": "0063_illegal_mauler",
|
||||
"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-orm": "^0.45.1",
|
||||
"drizzle-zod": "^0.8.3",
|
||||
"excel-date-to-js": "^1.1.5",
|
||||
"express": "^5.2.1",
|
||||
"husky": "^9.1.7",
|
||||
"ldapts": "^8.1.7",
|
||||
@@ -42,6 +43,7 @@
|
||||
"powershell": "^2.3.3",
|
||||
"socket.io": "^4.8.3",
|
||||
"socket.io-client": "^4.8.3",
|
||||
"xlsx": "^0.18.5",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -3412,6 +3414,15 @@
|
||||
"dev": true,
|
||||
"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": {
|
||||
"version": "7.1.4",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
|
||||
@@ -4301,6 +4312,19 @@
|
||||
"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": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
|
||||
@@ -4402,6 +4426,15 @@
|
||||
"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": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
@@ -7634,6 +7667,15 @@
|
||||
"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": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
|
||||
@@ -8176,6 +8218,15 @@
|
||||
"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": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
|
||||
@@ -12461,6 +12512,18 @@
|
||||
"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": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
|
||||
@@ -13581,6 +13644,24 @@
|
||||
"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": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
||||
@@ -13674,6 +13755,27 @@
|
||||
"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": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
"drizzle-kit": "^0.31.10",
|
||||
"drizzle-orm": "^0.45.1",
|
||||
"drizzle-zod": "^0.8.3",
|
||||
"excel-date-to-js": "^1.1.5",
|
||||
"express": "^5.2.1",
|
||||
"husky": "^9.1.7",
|
||||
"ldapts": "^8.1.7",
|
||||
@@ -100,6 +101,7 @@
|
||||
"powershell": "^2.3.3",
|
||||
"socket.io": "^4.8.3",
|
||||
"socket.io-client": "^4.8.3",
|
||||
"xlsx": "^0.18.5",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"config": {
|
||||
|
||||
Reference in New Issue
Block a user