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) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting the printer.",
|
||||
message: "There was an error getting the settings.",
|
||||
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