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:
@@ -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
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
60
server/services/logistics/route/getCycleCountChecks.ts
Normal file
60
server/services/logistics/route/getCycleCountChecks.ts
Normal 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;
|
||||||
@@ -32,7 +32,7 @@ export const createLabel = async (data: any, userPrinted: any) => {
|
|||||||
if (settingsError) {
|
if (settingsError) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: "There was an error getting the printer.",
|
message: "There was an error getting the settings.",
|
||||||
settingsError,
|
settingsError,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -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
|
||||||
|
`;
|
||||||
Reference in New Issue
Block a user