From 94e1198f6305751af7662a63e0ac21ac04f805d1 Mon Sep 17 00:00:00 2001 From: Blake Matthes Date: Wed, 15 Oct 2025 14:27:54 -0500 Subject: [PATCH] feat(app): order schdeuler --- app/main.ts | 11 +- .../logistics/controller/schedulerManager.ts | 122 ++++++++++++++++++ app/src/internal/logistics/routes.ts | 16 +++ .../logistics/routes/scheduler/getSchedule.ts | 12 ++ .../routes/scheduler/scheduleRoutes.ts | 32 +++++ .../internal/routerHandler/routeHandler.ts | 2 + app/src/pkg/db/schema/orderScheduler.ts | 51 ++++++++ 7 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 app/src/internal/logistics/controller/schedulerManager.ts create mode 100644 app/src/internal/logistics/routes.ts create mode 100644 app/src/internal/logistics/routes/scheduler/getSchedule.ts create mode 100644 app/src/internal/logistics/routes/scheduler/scheduleRoutes.ts create mode 100644 app/src/pkg/db/schema/orderScheduler.ts diff --git a/app/main.ts b/app/main.ts index b87d832..eda5a9d 100644 --- a/app/main.ts +++ b/app/main.ts @@ -19,10 +19,13 @@ import { sendNotify } from "./src/pkg/utils/notify.js"; import { toNodeHandler } from "better-auth/node"; import { auth } from "./src/pkg/auth/auth.js"; import { apiHitMiddleware } from "./src/pkg/middleware/apiHits.js"; +import { setupIoServer } from "./src/ws/server.js"; +import { schedulerManager } from "./src/internal/logistics/controller/schedulerManager.js"; const main = async () => { const env = validateEnv(process.env); const PORT = Number(env.VITE_PORT) || 4200; + //create the logger const log = createLogger({ module: "system", subModule: "main start" }); @@ -130,16 +133,18 @@ const main = async () => { express.static(join(__dirname, "../frontend/dist")) ); + // server setup + const server = createServer(app); + // register app setupRoutes(app, basePath); // ws stuff - - // ws + server stuff - const server = createServer(app); + setupIoServer(server, basePath); // sub systems printers(); + schedulerManager(); // start the server up server.listen(PORT, "0.0.0.0", () => diff --git a/app/src/internal/logistics/controller/schedulerManager.ts b/app/src/internal/logistics/controller/schedulerManager.ts new file mode 100644 index 0000000..67f3b18 --- /dev/null +++ b/app/src/internal/logistics/controller/schedulerManager.ts @@ -0,0 +1,122 @@ +import { subMinutes } from "date-fns"; +import { format, formatInTimeZone } from "date-fns-tz"; +import { sql } from "drizzle-orm"; +import { db } from "../../../pkg/db/db.js"; +import { + type OrderScheduler, + orderScheduler, +} from "../../../pkg/db/schema/orderScheduler.js"; +import { createLogger } from "../../../pkg/logger/logger.js"; +import { prodQuery } from "../../../pkg/prodSql/prodQuery.js"; +import { scheduler } from "../../../pkg/prodSql/querys/scheduler/scheduler.js"; +import { tryCatch } from "../../../pkg/utils/tryCatch.js"; + +/* +will monitor the incoming goods and the orders and update lst as they change or get updated. +*/ +export const schedulerManager = async () => { + const log = createLogger({ + module: "logistics", + subModule: "scheduleManager", + }); + + log.info({}, "Starting the scheduler manager up."); + + setInterval(async () => { + const targetTimeZone = "America/New_York"; + const now = new Date(); + + // console.log(formatInTimeZone(now, targetTimeZone, "yyyy-M-d HH:mm")); + // console.log(format(now, "yyyy-M-d HH:mm")); + const { data, error } = (await tryCatch( + prodQuery( + scheduler.replace( + "[dateCheck]", + formatInTimeZone( + subMinutes(now, 1), // dealing with the 1 min difference in case we have something missed. + targetTimeZone, + "yyyy-M-d HH:mm", + ), + ), + "scheduler", + ), + )) as any; + // do the updates to the db so we can pull the info up to the frontend + if (error) { + log.error({ error: error }, "there was an error getting the data"); + return; + } + const orderData = data.data || ([] as OrderScheduler); + //console.log(data); + + if (orderData.length === 0) { + log.info({}, "There are no new orders or incoming to be updated"); + return; + } + + for (let i = 0; i < orderData.length; i++) { + const { data, error } = await tryCatch( + db + .insert(orderScheduler) + .values({ + av: orderData[i].av, + description: orderData[i].description, + orderType: orderData[i].type, //orders || incoming + orderNumber: orderData[i].orderNumber, + header: orderData[i].av, + lineItemNumber: orderData[i].lineItemNumber, + customerReleaseNumber: orderData[i].customerReleaseNumber, + deliveryDate: new Date(orderData[i].deliveryDate), + loadingDate: new Date(orderData[i].loadingDate), + orderQTY: orderData[i].orderQTY, + orderLu: orderData[i].orderLu, + deliveredQTY: orderData[i].deliveredQTY, + deliveredLu: orderData[i].deliveredLu, + remark: orderData[i].remark, + createdAsEDI: orderData[i].createdAsEDI, + currentState: orderData[i].currentState, + lstDateCheck: new Date(orderData[i].deliveryDate), //this will match the delivery date to start + customerAddressId: orderData[i].customerAddressId, + customerDescription: orderData[i].customerDescription, + orderFrom: "prod", // manual or prod. + // being edited change to true so it will essential lock all others from editing + // carrier + // carrier email. -- when dropped we can email the carrier this could be considered there confirmation. + addDate: sql`NOW()`, + updDate: sql`NOW()`, + }) + .onConflictDoUpdate({ + target: orderScheduler.orderNumber, + set: { + deliveryDate: new Date(orderData[i].deliveryDate), + loadingDate: new Date(orderData[i].loadingDate), + orderQTY: orderData[i].orderQTY, + orderLu: orderData[i].orderLu, + remark: orderData[i].remark, + currentState: orderData[i].currentState, + deliveredQTY: orderData[i].deliveredQTY, + deliveredLu: orderData[i].deliveredLu, + lstDateCheck: new Date(orderData[i].deliveryDate), + updDate: sql`NOW()`, + }, + }), + ); + + if (error) { + console.log(error); + log.error( + { error: error }, + `There was an error inserting/updating the order ${orderData[i].orderNumber}`, + ); + continue; + } + + log.info( + { data: data }, + `${orderData[i].orderNumber} was inserted or updated`, + ); + + //await delay + } + }, 60 * 1000); +}; diff --git a/app/src/internal/logistics/routes.ts b/app/src/internal/logistics/routes.ts new file mode 100644 index 0000000..ce82092 --- /dev/null +++ b/app/src/internal/logistics/routes.ts @@ -0,0 +1,16 @@ +import type { Express, Request, Response } from "express"; +import { requireAuth } from "../../pkg/middleware/authMiddleware.js"; +import schedule from "./routes/scheduler/scheduleRoutes.js"; + +export const setupLogisticsRoutes = (app: Express, basePath: string) => { + app.use(basePath + "/api/logistics/schedule", schedule); + + app.use( + basePath + "/api/admin/users", + requireAuth("user", ["systemAdmin"]) // will pass bc system admin but this is just telling us we need this + ); + app.use( + basePath + "/api/admin", + requireAuth("user", ["systemAdmin", "admin"]) // will pass bc system admin but this is just telling us we need this + ); +}; diff --git a/app/src/internal/logistics/routes/scheduler/getSchedule.ts b/app/src/internal/logistics/routes/scheduler/getSchedule.ts new file mode 100644 index 0000000..08fcc23 --- /dev/null +++ b/app/src/internal/logistics/routes/scheduler/getSchedule.ts @@ -0,0 +1,12 @@ +import { Router } from "express"; +import type { Request, Response } from "express"; +import { schedulerChange } from "../../../../ws/channels/scheduler.js"; + +const router = Router(); + +router.get("/", async (req: Request, res: Response) => { + schedulerChange({ name: "something" }); + res.status(200).json({ message: "Something " }); +}); + +export default router; diff --git a/app/src/internal/logistics/routes/scheduler/scheduleRoutes.ts b/app/src/internal/logistics/routes/scheduler/scheduleRoutes.ts new file mode 100644 index 0000000..528e25d --- /dev/null +++ b/app/src/internal/logistics/routes/scheduler/scheduleRoutes.ts @@ -0,0 +1,32 @@ +import { Router } from "express"; + +import getSchedule from "./getSchedule.js"; + +import { restrictToHosts } from "../../../../pkg/middleware/restrictToHosts.js"; +import { requireAuth } from "../../../../pkg/middleware/authMiddleware.js"; + +const router = Router(); + +router.use("/", getSchedule); +// router.use( +// "/", +// requireAuth("user", ["systemAdmin", "admin"]), +// restrictToHosts([ +// "usmcd1vms036.alpla.net", +// "USMCD1VMS036.alpla.net", +// "https://usmcd1vms036.alpla.net", +// ]), +// addServer +// ); +// router.use( +// "/", +// requireAuth("user", ["systemAdmin", "admin"]), +// restrictToHosts([ +// "usmcd1vms036.alpla.net", +// "USMCD1VMS036.alpla.net", +// "https://usmcd1vms036.alpla.net", +// ]), +// updateServer +// ); + +export default router; diff --git a/app/src/internal/routerHandler/routeHandler.ts b/app/src/internal/routerHandler/routeHandler.ts index 2da3a3c..0ad949c 100644 --- a/app/src/internal/routerHandler/routeHandler.ts +++ b/app/src/internal/routerHandler/routeHandler.ts @@ -2,12 +2,14 @@ import type { Express, Request, Response } from "express"; import { setupAuthRoutes } from "../auth/routes/routes.js"; import { setupAdminRoutes } from "../admin/routes.js"; import { setupSystemRoutes } from "../system/routes.js"; +import { setupLogisticsRoutes } from "../logistics/routes.js"; export const setupRoutes = (app: Express, basePath: string) => { // all routes setupAuthRoutes(app, basePath); setupAdminRoutes(app, basePath); setupSystemRoutes(app, basePath); + setupLogisticsRoutes(app, basePath); // always try to go to the app weather we are in dev or in production. app.get(basePath + "/", (req: Request, res: Response) => { diff --git a/app/src/pkg/db/schema/orderScheduler.ts b/app/src/pkg/db/schema/orderScheduler.ts new file mode 100644 index 0000000..54211b7 --- /dev/null +++ b/app/src/pkg/db/schema/orderScheduler.ts @@ -0,0 +1,51 @@ +import { + boolean, + date, + integer, + pgTable, + real, + text, + timestamp, + uniqueIndex, + uuid, +} from "drizzle-orm/pg-core"; +import type z from "zod"; + +export const orderScheduler = pgTable( + "orderScheduler", + { + schedule_id: uuid("schedule_id").defaultRandom().primaryKey(), + av: integer("av"), + description: text("description"), + orderType: text("order_type").notNull(), //orders || incoming + orderNumber: integer("order_number").notNull(), + header: text("header").notNull(), + lineItemNumber: text("line_item_number"), + customerReleaseNumber: text("customer_release_number"), + deliveryDate: timestamp("delivery_date").notNull(), + loadingDate: timestamp("loading_date"), + orderQTY: real("order_qty").notNull(), + orderLu: real("order_lu").notNull(), + deliveredQTY: real("delivered_qty").default(0.0), + deliveredLu: real("delivered_lu").default(0.0), + remark: text("remark"), + createdAsEDI: boolean("created_as_EDI"), + currentState: integer("current_state"), + lstDateCheck: timestamp("lst_date_check"), //this will match the delivery date to start and when moved in the front end run a function to alert or update via api + customerAddressId: integer("customer_address_id"), + customerDescription: text("customer_description"), + dock: text("dock"), + orderFrom: text("order_from"), // manual or prod. + // being edited change to true so it will essential lock all others from editing + // carrier + // carrier email. -- when dropped we can email the carrier this could be considered there confirmation. + addDate: timestamp("add_date").defaultNow(), + updDate: timestamp("upd_date").defaultNow(), + }, + (table) => [ + // uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`), + uniqueIndex("orderNumber").on(table.orderNumber), + ], +); + +export type OrderScheduler = z.infer;