feat(materials): added in a bigger window on eom transfer lots

This commit is contained in:
2025-08-28 10:14:41 -05:00
parent 0a6ddea8c0
commit 53ed2c4e6a
5 changed files with 286 additions and 39 deletions

View File

@@ -6,9 +6,12 @@ import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { createLog } from "../../../logger/logger.js";
import { query } from "../../../sqlServer/prodSqlServer.js";
import { labelInfo } from "../../../sqlServer/querys/warehouse/labelInfo.js";
import { format, formatDuration, intervalToDuration } from "date-fns";
import { shiftChange } from "../../../sqlServer/querys/misc/shiftChange.js";
import { success } from "zod/v4";
type NewLotData = {
runnungNumber: number;
runningNumber: number;
lotNumber: number;
originalAmount: number;
level: number;
@@ -16,6 +19,17 @@ type NewLotData = {
type: "lot" | "eom";
};
interface PendingJob {
timeoutId: NodeJS.Timeout;
runningNumber: string | number;
data: any;
consumeLot: any;
newQty: any;
scheduledFor: Date;
}
export const pendingJobs = new Map<string | number, PendingJob>();
/**
* Move manual material to a new lot.
*
@@ -28,11 +42,116 @@ type NewLotData = {
* type what way are we lots
*/
export const lotMaterialTransfer = async (data: NewLotData) => {
let timeoutTrans: number = data.type === "lot" ? 1 : 10;
// check if we already have this running number scheduled
if (pendingJobs.has(data.runningNumber)) {
const job = pendingJobs.get(data.runningNumber) as PendingJob;
const duration = intervalToDuration({
start: new Date(),
end: job.scheduledFor,
});
createLog(
"error",
"materials",
"ocp",
`${
data.runningNumber
} is pending to be transfered already, remaining time ${formatDuration(
duration,
{ format: ["hours", "minutes", "seconds"] }
)}`
);
return {
success: false,
message: `${
data.runningNumber
} is pending to be transfered already, remaining time ${formatDuration(
duration,
{ format: ["hours", "minutes", "seconds"] }
)}`,
data: [],
};
}
// get the shift time
const { data: shift, error: shiftError } = (await tryCatch(
query(shiftChange, "shift change from material.")
)) as any;
if (shiftError) {
createLog(
"error",
"materials",
"ocp",
"There was an error getting the shift times will use fallback times"
);
}
// shift split
const shiftTimeSplit = shift?.data[0]?.shiftChange.split(":");
//console.log(shiftTimeSplit);
// Current time
const now = new Date();
// Target time: today at 06:35
const target = new Date(
now.getFullYear(),
now.getMonth(),
1, //now.getDate(),
shiftTimeSplit.length > 0 ? parseInt(shiftTimeSplit[0]) - 1 : 5, // this will parse the hour to remove teh zero
shiftTimeSplit.length > 0 ? parseInt(shiftTimeSplit[1]) + 3 : 3,
0,
0
);
// to early time
const early = new Date(
now.getFullYear(),
now.getMonth(),
1, //now.getDate(),
shiftTimeSplit.length > 0 ? parseInt(shiftTimeSplit[0]) - 1 : 5, // this will parse the hour to remove teh zero
shiftTimeSplit.length > 0 ? parseInt(shiftTimeSplit[1]) : 0,
0,
0
);
// next month just to be here
const nextMonth = new Date(
now.getFullYear(),
now.getMonth() + 1,
1, //now.getDate(),
shiftTimeSplit.length > 0 ? parseInt(shiftTimeSplit[0]) - 1 : 5, // this will parse the hour to remove teh zero
shiftTimeSplit.length > 0 ? parseInt(shiftTimeSplit[1]) : 0,
0,
0
);
// console.log(early, target);
// if we are to early return early only if we are sending over eom
if (data.type === "eom" && (early > now || target < now)) {
createLog(
"error",
"materials",
"ocp",
`Eom transfers is not allowed right now please try again at ${format(
nextMonth,
"M/d/yyyy hh:mm"
)} `
);
return {
success: false,
message: `Eom transfers is not allowed right now please try again at ${format(
nextMonth,
"M/d/yyyy hh:mm"
)} `,
data: [],
};
}
let timeoutTrans: number = data.type === "lot" ? 30 : 10;
// get the barcode, and layoutID from the running number
const { data: label, error: labelError } = (await tryCatch(
query(
labelInfo.replace("[runningNr]", `${data.runnungNumber}`),
labelInfo.replace("[runningNr]", `${data.runningNumber}`),
"Get label info"
)
)) as any;
@@ -56,11 +175,11 @@ export const lotMaterialTransfer = async (data: NewLotData) => {
"error",
"materials",
"ocp",
`${data.runnungNumber}: dose not exist or no longer in stock.`
`${data.runningNumber}: dose not exist or no longer in stock.`
);
return {
success: false,
message: `${data.runnungNumber}: dose not exist or no longer in stock.`,
message: `${data.runningNumber}: dose not exist or no longer in stock.`,
data: [],
};
}
@@ -72,11 +191,11 @@ export const lotMaterialTransfer = async (data: NewLotData) => {
"error",
"materials",
"ocp",
`${data.runnungNumber}: currently in stock and not consumed to a lot.`
`${data.runningNumber}: currently in stock and not consumed to a lot.`
);
return {
success: false,
message: `${data.runnungNumber}: currently in stock and not consumed to a lot.`,
message: `${data.runningNumber}: currently in stock and not consumed to a lot.`,
data: [],
};
}
@@ -132,11 +251,11 @@ export const lotMaterialTransfer = async (data: NewLotData) => {
"error",
"materials",
"ocp",
`RN:${data.runnungNumber}, Reprinting Error: ${reprint.data.data.message}`
`RN:${data.runningNumber}, Reprinting Error: ${reprint.data.data.message}`
);
return {
success: false,
message: `RN:${data.runnungNumber}, Reprinting Error: ${reprint.data.data.message}`,
message: `RN:${data.runningNumber}, Reprinting Error: ${reprint.data.data.message}`,
data: reprint,
};
}
@@ -161,11 +280,11 @@ export const lotMaterialTransfer = async (data: NewLotData) => {
"error",
"materials",
"ocp",
`RN:${data.runnungNumber}, Return Error ${matReturn.data.data.errors[0].message}`
`RN:${data.runningNumber}, Return Error ${matReturn.data.data.errors[0].message}`
);
return {
success: false,
message: `RN:${data.runnungNumber}, Return Error ${matReturn.data.data.errors[0].message}`,
message: `RN:${data.runningNumber}, Return Error ${matReturn.data.data.errors[0].message}`,
data: matReturn,
};
}
@@ -175,9 +294,66 @@ export const lotMaterialTransfer = async (data: NewLotData) => {
barcode: label?.data[0].Barcode,
};
const delay =
data.type === "lot"
? timeoutTrans * 1000
: target.getTime() - now.getTime();
const transfer = await transferMaterial(delay, data, consumeLot, newQty);
if (!transfer.success) {
return {
success: transfer.success,
message: transfer.message,
data: transfer.data,
};
}
const duration = intervalToDuration({ start: now, end: target });
const pretty = formatDuration(duration, {
format: ["hours", "minutes", "seconds"],
});
if (data.type === "eom") {
return {
success: true,
message: `RN:${data.runningNumber}: qty: ${newQty}, will be transfered to lot: ${data.lotNumber}, in ${pretty} `,
data: [],
};
} else {
return {
success: true,
message: `RN:${data.runningNumber}: qty: ${newQty}, was transfered to lot: ${data.lotNumber}`,
data: [],
};
}
};
const transferMaterial = async (
delay: number,
data: any,
consumeLot: any,
newQty: any
) => {
//console.log(data);
if (pendingJobs.has(data.runningNumber)) {
createLog(
"error",
"materials",
"ocp",
`${data.runningNumber} is pending to be transfered already`
);
return {
success: false,
message: `${data.runningNumber} is pending to be transfered already`,
data: [],
};
}
const scheduledFor = new Date(Date.now() + delay);
// sets the time out based on the type of transfer sent over.
setTimeout(
async () => {
const timeoutId = setTimeout(async () => {
try {
const { data: matConsume, error: matConsumeError } =
(await tryCatch(
runProdApi({
@@ -187,45 +363,59 @@ export const lotMaterialTransfer = async (data: NewLotData) => {
})
)) as any;
if (!matConsume.success) {
if (!matConsume?.success) {
createLog(
"error",
"materials",
"ocp",
`RN:${data.runnungNumber}, Consume Error ${matConsume.data.data.errors[0].message}`
`RN:${data.runningNumber}, Consume Error ${
matConsume?.data?.data?.errors?.[0]?.message ??
"Unknown"
}`
);
return {
success: false,
message: `RN:${data.runnungNumber}, Consume Error ${matConsume.data.data.errors[0].message}`,
data: matConsume,
};
return; // still hits finally
}
createLog(
"info",
"materials",
"ocp",
`RN:${data.runnungNumber}: qty: ${newQty}, was transfered to lot:${data.lotNumber}`
`RN:${data.runningNumber}: qty: ${newQty}, was transferred to lot:${data.lotNumber}`
);
},
data.type === "lot" ? timeoutTrans * 1000 : timeoutTrans * 1000 * 60
);
} catch (err) {
createLog(
"error",
"materials",
"ocp",
`RN:${data.runningNumber}, ${err}`
);
} finally {
// Always clear the pending entry, even if error
pendingJobs.delete(data.runningNumber);
}
}, delay);
if (data.type === "eom") {
return {
success: true,
message: `RN:${data.runnungNumber}: qty: ${newQty}, will be transfered to:${data.lotNumber}, in ${timeoutTrans}min`,
data: [],
};
} else {
return {
success: true,
message: `RN:${data.runnungNumber}: qty: ${newQty}, was transfered to lot:${data.lotNumber}`,
data: [],
};
}
pendingJobs.set(data.runningNumber, {
timeoutId,
runningNumber: data.runningNumber,
data,
consumeLot,
newQty,
scheduledFor,
});
// Immediately say we scheduled it
return {
success: true,
message: `Transfer for ${data.runningNumber} scheduled`,
data: [],
};
};
// setInterval(() => {
// console.log(pendingJobs);
// }, 5000);
// setTimeout(async () => {
// lotMaterialTransfer({
// runnungNumber: 603468,

View File

@@ -25,6 +25,7 @@ import bookInLabel from "./routes/labeling/bookIn.js";
import labelRatio from "./routes/labeling/getLabelRatio.js";
import resetRatio from "./routes/labeling/resetLabelRatio.js";
import materialTransferLot from "./routes/materials/lotTransfer.js";
import pendingTransfers from "./routes/materials/currentPending.js";
import { zechitti1Connect } from "./controller/specialProcesses/zechettis/zechetti1.js";
const app = new OpenAPIHono();
@@ -51,6 +52,7 @@ const routes = [
dycoClose,
// materials
materialTransferLot,
pendingTransfers,
] as const;
const setting = await db.select().from(settings);

View File

@@ -0,0 +1,51 @@
// an external way to creating logs
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { apiHit } from "../../../../globalUtils/apiHits.js";
import { pendingJobs } from "../../controller/materials/lotTransfer.js";
import { format, formatDuration, intervalToDuration } from "date-fns";
const app = new OpenAPIHono({ strict: false });
app.openapi(
createRoute({
tags: ["ocp"],
summary: "Returns pending transfers",
method: "get",
path: "/pendingtransfers",
responses: responses(),
}),
async (c) => {
apiHit(c, { endpoint: "/pendingtransfers" });
const pending = Array.from(pendingJobs.entries()).map(
([runningNumber, job]) => {
const duration = intervalToDuration({
start: new Date(),
end: job.scheduledFor,
});
return {
runningNumber,
lot: job.data?.lotNumber,
newQty: job.newQty,
consumeLot: job.consumeLot,
scheduledFor: format(job.scheduledFor, "M/d/yyyy HH:mm"),
remainingMs: formatDuration(duration, {
format: ["hours", "minutes", "seconds"],
}),
};
}
);
//console.log(pending);
return c.json({
success: true,
message: "Current Pending trnasfers",
data: [pending],
});
}
);
export default app;

View File

@@ -9,7 +9,7 @@ import { lotMaterialTransfer } from "../../controller/materials/lotTransfer.js";
const app = new OpenAPIHono({ strict: false });
const LotTransfer = z.object({
runnungNumber: z.number().openapi({ example: 1234 }),
runningNumber: z.number().openapi({ example: 1234 }),
lotNumber: z.number().openapi({ example: 1235 }),
originalAmount: z.number().openapi({ example: 457 }),
level: z.number().openapi({ examples: [0.24, 0.5, 0.75, 0.95] }),
@@ -45,7 +45,7 @@ app.openapi(
);
if (transferError) {
console.log(transferError);
//console.log(transferError);
return c.json({
success: false,
message: