feat(cyclecountcheck): added i cycle count check this is in mem right now

if this becomes a resource hog we will move it to the db. no need for it to really be presitant
This commit is contained in:
2025-04-09 17:50:06 -05:00
parent 2e2699ab3c
commit 6665b77e09
6 changed files with 349 additions and 1 deletions

View File

@@ -0,0 +1,59 @@
import { differenceInDays, differenceInSeconds, format } from "date-fns";
import { timeZoneFix } from "../../../../../globalUtils/timeZoneFix.js";
import { createLog } from "../../../../logger/logger.js";
import { delay } from "../../../../../globalUtils/delay.js";
import { tryCatch } from "../../../../../globalUtils/tryCatch.js";
import { query } from "../../../../sqlServer/prodSqlServer.js";
import { cycleCountCheck } from "../../../../sqlServer/querys/warehouse/cycleCountCheck.js";
// setting timer for updating stockCheck on a restart will always check.
let lastCheck = 0;
export const lanes: any = [];
export const getLanesToCycleCount = async () => {
const currentTime: any = timeZoneFix();
// store the lanes in memeory
createLog("info", "warehouse", "logistics", "Empty lane triggered update.");
lastCheck = currentTime;
const ageQuery = cycleCountCheck.replaceAll("[ageOfRow]", `1000`);
const { data: prodLanes, error: pl } = await tryCatch(
query(ageQuery, "Get Stock lane date.")
);
// run the update on the lanes
for (let i = 0; i < prodLanes.length; i++) {
const createLane = {
laneID: prodLanes[i]?.laneID,
warehouseID: prodLanes[i]?.warehouseID,
warehouseName: prodLanes[i]?.warehouseName || "na",
Description: prodLanes[i]?.Description,
LastMoveDate: prodLanes[i]?.LastMoveDate
? format(prodLanes[i]?.LastInv, "M/d/yyyy")
: undefined,
LastInv: format(prodLanes[i]?.LastInv, "M/d/yyyy"),
rowType: prodLanes[i].rowType,
DaysSinceLast:
differenceInDays(
new Date(prodLanes[i].LastInv),
new Date(prodLanes[i].LastMoveDate)
) <= 0
? 0
: differenceInDays(
new Date(prodLanes[i].LastInv),
new Date(prodLanes[i].LastMoveDate)
),
upd_date: format(new Date(Date.now()), "M/d/yyyy"),
};
lanes.push(createLane);
createLog(
"debug",
"warehouse",
"logistics",
`${lanes[i].Description} was just added`
);
await delay(10);
//delay to slow this thing down
}
};

View File

@@ -0,0 +1,28 @@
import { lanes } from "./cyclecountCheck.js";
export const getCycleCountCheck = async (
age: number = 1000,
type: string = ""
) => {
/**
* Get the lane data based on the age and type
*/
let filteredLanes = lanes.filter((t: any) => t.DaysSinceLast >= age);
if (type != "") {
return {
sucess: true,
message: `${filteredLanes.length} lanes that are of type ${type} and have not been cycle counted in the last ${age} days.`,
data: filteredLanes.filter(
(t: any) => t.rowType === type.toUpperCase()
),
};
} else {
return {
success: true,
message: `${filteredLanes.length} lanes grabed that have not been cycle counted in the last ${age} days.`,
data: filteredLanes,
};
}
};

View File

@@ -0,0 +1,60 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { getCycleCountCheck } from "../controller/warehouse/cycleCountChecks/getCycleCountCheck.js";
const app = new OpenAPIHono();
const Body = z
.object({
age: z.number().optional().openapi({ example: 90 }),
//email: z.string().optional().openapi({example: "s.smith@example.com"}),
type: z.string().optional().openapi({ example: "fg" }),
})
.openapi("User");
app.openapi(
createRoute({
tags: ["logistics"],
summary: "Returns lanes that need cycle counted",
method: "post",
path: "/cyclecountcheck",
request: {
body: {
content: {
"application/json": { schema: Body },
},
},
},
// description:
// "Provided a running number and lot number you can consume material.",
responses: responses(),
}),
async (c: any) => {
//apiHit(c, { endpoint: "api/sqlProd/close" });
const { data: body, error: be } = await tryCatch(c.req.json());
if (be) {
return c.json({ success: false, message: "Missing Data." });
}
const check: any = body;
const { data: lanes, error: le } = await tryCatch(
getCycleCountCheck(check.age, check.type)
);
if (le) {
return c.json({
success: false,
message: "Error getting lane data.",
data: le,
});
}
return c.json({
success: lanes.success,
message: lanes.message,
data: lanes.data,
});
}
);
export default app;

View File

@@ -32,7 +32,7 @@ export const createLabel = async (data: any, userPrinted: any) => {
if (settingsError) {
return {
success: false,
message: "There was an error getting the printer.",
message: "There was an error getting the settings.",
settingsError,
};
}

View File

@@ -0,0 +1,121 @@
import { eq } from "drizzle-orm";
import { db } from "../../../../../database/dbclient.js";
import { settings } from "../../../../../database/schema/settings.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { createLog } from "../../../logger/logger.js";
import { getPrinters } from "./getPrinters.js";
import { autoLabelingStats } from "./printerStatus.js";
let isPrinterCycling = false;
let actualPrinterCycle: any;
export const printerCycleAutoLabelers = async () => {
/**
* Will only check the auto labelers for status updates.
*/
if (isPrinterCycling)
return {
success: false,
message: "Printers are already being cycled.",
};
createLog("info", "ocp", "ocp", "AutoLabeling cycle has started.");
// get the printers
const { data: s, error: se } = await tryCatch(
db.select().from(settings).where(eq(settings.name, "ocpCycleDelay"))
);
if (se) {
createLog(
"error",
"ocp",
"ocp",
"There was an error getting the ocpCycleDelay."
);
return {
success: false,
message: "Error getting printers.",
};
}
const ocpDelay: any = s;
isPrinterCycling = true;
// start the actual printer cycle
actualPrinterCycle = setInterval(async () => {
const { data, error } = await tryCatch(getPrinters());
if (error) {
createLog(
"error",
"ocp",
"ocp",
"There was an error getting the printers."
);
return {
success: false,
message: "Error getting printers.",
};
}
let printers: any = data.data;
// only keep the assigned ones
printers = printers.filter((p: any) => p.assigned === true);
// for printers we want to ignore there must be a remark stateing to ignore.
printers = printers.filter((p: any) => !p.remark.includes("ignore"));
printers.forEach(async (p: any) => {
/**
* if the last timeprinted would be greater than x well just change the status to idle and extended based on the 2 times.
*
* to get a printer going again label will need to come from the front end as that will just unpause the printer and start the labeling, or the api for manual print
* well need to adjust this to actually print the label then unpause it.
*
* it will be
*
* less than x since time printed run the printer status
* greater than x but less than y change the status to idle, but ping to make sure its online,
* if greater than y change to extended idle but stil also ping to make sure its online.
*/
// ignore pdf printer as we want it here for testing purposes
if (p.name.toLowerCase() === "pdf24") {
return;
}
if (p.name === "Autolabeler") {
await autoLabelingStats(p);
return;
}
});
}, parseInt(ocpDelay[0]?.value) * 2 * 1000);
return { success: true, message: "AutoLabeling cycle has been started." };
};
export const stopPrinterCycle = async () => {
/**
* We will stop the print cylce this is more an emergancy thing.
*/
if (actualPrinterCycle && !actualPrinterCycle._destroyed) {
createLog("info", "ocp", "ocp", "AutoLabeling cycle is being stopped.");
clearInterval(actualPrinterCycle);
isPrinterCycling = false;
return {
success: true,
message: "AutoLabeling cycle has been stopped.",
};
} else {
createLog(
"info",
"ocp",
"ocp",
"AutoLabeling cycle is already stopped."
);
isPrinterCycling = false;
return {
success: true,
message: "AutoLabeling cycle is already Stopped.",
};
}
};

View File

@@ -0,0 +1,80 @@
export const cycleCountCheck = `
-- Define the structure of the result set from the stored procedure
DECLARE @results TABLE (
IdLocation INT,
LastMoveDate Date
)
-- insert into the temp table
insert into @results
select IdLagerAbteilung, MAX(CaSE WHEN CONVERT(char(10), Buchungsdatum, 120) IS NULL THEN '1900-01-01' ELSE CONVERT(char(10), Buchungsdatum, 120) END) AS LastLocMov
from AlplaPROD_test1.dbo.V_LagerBuchungen x(nolock)
group by IdLagerAbteilung
select * from (
select x.IdLagerAbteilung as laneID,
x.IdWarenLager as warehouseID,
w.Bezeichnung as warehouseName,
w.LagerTyp as warehouseIDTyp,
w.Standort as warehouseLocation,
x.Bezeichnung as Description,
LastMoveDate,
CASE WHEN CONVERT(char(10), i.Datum, 120) is null then getdate() - 365 else CONVERT(char(10), i.Datum, 120) end as LastInv,
--create the types of warehouses to choose from
case
-- empty
when (sum(EinlagerungsMengeSum) is null and Datum < LastMoveDate) or (
(sum(EinlagerungsMengeSum) is null and Datum < DATEADD(day, -[ageOfRow], getdate()))
) then 'EMPTY'
-- finished goods
when w.LagerTyp = 2 and w.Standort = 10 then 'FG'
-- external finished goods
when w.LagerTyp = 2 and w.Standort = 20 then 'EXTERNAL'
-- silos
when w.LagerTyp in (3) and x.MaterialSilo = 1 then 'BULK'
-- MATERIALS
when w.LagerTyp = 3 and x.MaterialSilo = 0 then 'MATERIALS'
-- MATERIALS
when w.LagerTyp = 11 then 'WASTE'
-- MATERIALS
when w.LagerTyp = 9 then 'PACKAGING'
end as rowType,
CASE WHEN DateDiff(DAY,i.Datum,getDate()) is null then 1000 else DateDiff(DAY,i.Datum,getDate()) end as DaysSinceLast
from AlplaPROD_test1.dbo.T_LagerAbteilungen as x (NOLOCK)
-- last move
left join
@results as b on
x.IdLagerAbteilung = b.IdLocation
-- last inv
left join
(select * from [AlplaPROD_test1].[dbo].[T_LagerAbteilungenInventuren] (nolock)) as i on x.IdLagerAbteilung =
i.IdLagerAbteilung
-- useing this to determin only if the lane is empty
left join
(select * from [AlplaPROD_test1].dbo.V_LagerPositionenBarcodes (nolock)) as y on x.IdLagerAbteilung =
y.IdLagerAbteilung
-- get the warehosue type
left join
(select * from [AlplaPROD_test1].dbo.T_WarenLager (nolock)) as w on x.IdWarenLager = w.IdWarenLager
where x.aktiv = 1 and x.IdWarenLager not in (1,5,6)
group by x.IdLagerAbteilung,
x.IdWarenLager,
w.LagerTyp,
w.Standort,
x.Bezeichnung,
LastMoveDate,
i.Datum,
x.MaterialSilo,
w.Bezeichnung
) xb
`;