Compare commits

...

8 Commits

24 changed files with 5867 additions and 64 deletions

View File

@@ -1,4 +1,4 @@
import {drizzle} from "drizzle-orm/postgres-js";
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import dotenv from "dotenv";
@@ -6,6 +6,17 @@ dotenv.config();
const database = process.env.DATABASE_URL || "";
const queryClient = postgres(database);
const queryClient = postgres(database, {
max: 10,
idle_timeout: 60,
connect_timeout: 10,
max_lifetime: 1000 * 60 * 5,
onnotice: (notice) => {
console.log("PG NOTICE:", notice.message);
},
// debug: (...args) => {
// console.log("QUERY DEBUG:", ...args);
// },
});
export const db = drizzle({client: queryClient});
export const db = drizzle({ client: queryClient });

View File

@@ -0,0 +1,3 @@
ALTER TABLE "qualityRequest" ALTER COLUMN "durationToMove" SET DATA TYPE integer;--> statement-breakpoint
ALTER TABLE "qualityRequest" ALTER COLUMN "palletStatus" SET DATA TYPE integer;--> statement-breakpoint
ALTER TABLE "qualityRequest" ALTER COLUMN "palletRequest" SET DATA TYPE integer;

View File

@@ -0,0 +1,10 @@
CREATE TABLE "ocmeCycleCounts" (
"ocme_id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"laneId" integer,
"warehouseName" numeric NOT NULL,
"laneName" numeric NOT NULL,
"good" boolean DEFAULT false,
"cycleCount" jsonb DEFAULT '[]'::jsonb,
"add_User" text DEFAULT 'LST_System' NOT NULL,
"add_Date" timestamp DEFAULT now()
);

View File

@@ -0,0 +1,3 @@
ALTER TABLE "ocmeCycleCounts" ALTER COLUMN "laneId" SET NOT NULL;--> statement-breakpoint
ALTER TABLE "ocmeCycleCounts" ALTER COLUMN "warehouseName" SET DATA TYPE text;--> statement-breakpoint
ALTER TABLE "ocmeCycleCounts" ALTER COLUMN "laneName" SET DATA TYPE text;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -351,6 +351,27 @@
"when": 1744649552936,
"tag": "0049_certain_tarot",
"breakpoints": true
},
{
"idx": 50,
"version": "7",
"when": 1744681925049,
"tag": "0050_flat_nuke",
"breakpoints": true
},
{
"idx": 51,
"version": "7",
"when": 1744684445081,
"tag": "0051_oval_warpath",
"breakpoints": true
},
{
"idx": 52,
"version": "7",
"when": 1744685129838,
"tag": "0052_dark_human_torch",
"breakpoints": true
}
]
}

View File

@@ -0,0 +1,37 @@
import {
text,
pgTable,
numeric,
timestamp,
boolean,
uuid,
integer,
jsonb,
} from "drizzle-orm/pg-core";
import { createSelectSchema } from "drizzle-zod";
export const ocmeCycleCounts = pgTable(
"ocmeCycleCounts",
{
ocme_id: uuid("ocme_id").defaultRandom().primaryKey(),
laneId: integer("laneId").notNull(),
warehouseName: text("warehouseName").notNull(),
laneName: text("laneName").notNull(),
good: boolean("good").default(false),
cycleCount: jsonb("cycleCount").default([]),
add_User: text("add_User").default("LST_System").notNull(),
add_Date: timestamp("add_Date").defaultNow(),
},
(table) => [
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
// uniqueIndex("role_name").on(table.name),
//uniqueIndex("ocme_runningNr").on(table.runningNr),
]
);
// Schema for inserting a user - can be used to validate API requests
// export const insertRolesSchema = createInsertSchema(roles, {
// name: z.string().min(3, {message: "Role name must be more than 3 letters"}),
// });
// Schema for selecting a Expenses - can be used to validate API responses
export const selectRolesSchema = createSelectSchema(ocmeCycleCounts);

View File

@@ -7,6 +7,7 @@ import {
boolean,
uuid,
uniqueIndex,
integer,
} from "drizzle-orm/pg-core";
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
import { z } from "zod";
@@ -23,11 +24,11 @@ export const qualityRequest = pgTable(
locationAtRequest: text("locationAtRequest"),
warehouseMovedTo: text("warehouseMovedTo"),
locationMovedTo: text("locationMovedTo"),
durationToMove: numeric("durationToMove"),
durationToMove: integer("durationToMove"),
locationDropOff: text("locationDropOff"),
palletStatus: numeric("palletStatus"),
palletStatus: integer("palletStatus"),
palletStatusText: text("palletStatusText"),
palletRequest: numeric("palletRequest"),
palletRequest: integer("palletRequest"),
add_date: timestamp("add_date").defaultNow(),
add_user: text("add_user").default("LST"),
upd_date: timestamp("upd_date").defaultNow(),

View File

@@ -39,6 +39,12 @@ export default function OcmeCycleCount() {
const onSubmit = async (data: any) => {
setData([]);
setCounting(true);
if (data.laneType === "") {
toast.error("Please select a type");
setCounting(false);
return;
}
toast.success(`Cycle count started`);
try {
const res = await axios.post("/ocme/api/v1/cycleCount", data, {
@@ -52,7 +58,7 @@ export default function OcmeCycleCount() {
reset();
}
if (res.data.success) {
if (!res.data.success) {
toast.success(res.data.message);
setCounting(false);
@@ -109,7 +115,7 @@ export default function OcmeCycleCount() {
<SelectItem value="name">
Name
</SelectItem>
<SelectItem value="laneId">
<SelectItem value="laneID">
Lane ID
</SelectItem>
</SelectContent>

View File

@@ -7,7 +7,7 @@ export function getinventoryCheck(data: any) {
queryFn: () => fetchStockSilo(data),
//enabled:
staleTime: 1000,
refetchInterval: 60 * 1000,
refetchInterval: 1000 * 60 * 15,
refetchOnWindowFocus: true,
});
}

View File

@@ -7,7 +7,7 @@ export function getlots() {
queryFn: () => fetchSettings(),
staleTime: 10 * 1000,
refetchInterval: 10 * 1000,
refetchInterval: 1000 * 10,
refetchOnWindowFocus: true,
});
}

View File

@@ -35,7 +35,7 @@
}
},
"admConfig": {
"build": 226,
"build": 227,
"oldBuild": "backend-0.1.3.zip"
},
"devDependencies": {

View File

@@ -3,6 +3,10 @@ import { alplaStockInv } from "./cycleCount/alplaStockInventory.js";
import { emptyCount } from "./cycleCount/emptyCycleCount.js";
import { fullLaneCount } from "./cycleCount/fullLaneCycleCount.js";
import { ocmeInv } from "./cycleCount/ocmeInventory.js";
import { ocmeCycleCounts } from "../../../../database/schema/ocmeCycleCounts.js";
import { db } from "../../../../database/dbclient.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { createLog } from "../../logger/logger.js";
export const prepareLane =
"https://usday1prod.alpla.net/application/public/v1.1/Warehousing/PrepareLaneForInventory";
@@ -74,6 +78,33 @@ export const cycleCount = async (lane: any, user: User) => {
}
// store in the db so we have a record.... for later when we fully randomize and automate this.
const postCount = {
laneId: ocme[0].alpla_laneID,
warehouseName: "",
laneName: alplaStock[0].alpla_laneDescription,
good: !combineBoth.every(
(s) => !s.info.includes("Validate") || !s.info.includes("sent")
),
cycleCount: combineBoth,
add_User: user.username,
};
const { data, error } = await tryCatch(
db.insert(ocmeCycleCounts).values(postCount)
);
if (error) {
createLog(
"error",
"lst",
"ocme",
`There was an error entering the cycle count data: ${error}`
);
}
if (data) {
createLog("info", "lst", "ocme", `Cycle Count data just added.`);
}
return combineBoth;
};

View File

@@ -10,62 +10,62 @@ import { verify } from "hono/jwt";
const app = new OpenAPIHono({ strict: false });
const AddSetting = z.object({
lane: z.string().openapi({ example: "L064" }),
lane: z.string().openapi({ example: "L064" }),
});
app.openapi(
createRoute({
tags: ["ocme"],
summary: "Cycle counts a lane based on the lane Alias",
method: "post",
path: "/cycleCount",
middleware: authMiddleware,
request: {
body: {
content: {
"application/json": { schema: AddSetting },
createRoute({
tags: ["ocme"],
summary: "Cycle counts a lane based on the lane Alias",
method: "post",
path: "/cycleCount",
middleware: authMiddleware,
request: {
body: {
content: {
"application/json": { schema: AddSetting },
},
},
},
},
},
responses: responses(),
}),
async (c) => {
apiHit(c, { endpoint: "api/auth/register" });
// make sure we have a vaid user being accessed thats really logged in
const body = await c.req.json();
responses: responses(),
}),
async (c) => {
apiHit(c, { endpoint: "api/auth/register" });
// make sure we have a vaid user being accessed thats really logged in
const body = await c.req.json();
const authHeader = c.req.header("Authorization");
const authHeader = c.req.header("Authorization");
const token = authHeader?.split("Bearer ")[1] || "";
let user: User;
const token = authHeader?.split("Bearer ")[1] || "";
let user: User;
try {
const payload = await verify(token, process.env.JWT_SECRET!);
user = payload.user as User;
} catch (error) {
return c.json({ message: "Unauthorized" }, 401);
try {
const payload = await verify(token, process.env.JWT_SECRET!);
user = payload.user as User;
} catch (error) {
return c.json({ message: "Unauthorized" }, 401);
}
try {
const cycleData = await cycleCount(body, user);
return c.json(
{
success: true,
message: `${body.lane} was just cycle counted.`,
data: cycleData,
},
200
);
} catch (error) {
return c.json(
{
success: false,
message: `There was an error cycle counting ${body.lane}`,
data: error,
},
400
);
}
}
try {
const cycleData = await cycleCount(body, user);
return c.json(
{
success: true,
message: `${body.lane} was just cycle counted.`,
data: cycleData,
},
200
);
} catch (error) {
return c.json(
{
success: false,
message: `There was an error cycle counting ${body.lane}`,
data: error,
},
400
);
}
}
);
export default app;

View File

@@ -31,6 +31,15 @@ export const assignedPrinters = async () => {
const printers: any = print.data ?? [];
const lots: any = l.data.length === 0 ? [] : l.data;
if (!lots) {
createLog(
"error",
"lst",
"ocp",
`There was an error getting the lots: ${lots.message}`
);
return;
}
for (let i = 0; i < printers.length; i++) {
// is the printer assinged in alplalabel online?
const assigned = lots?.filter(

View File

@@ -0,0 +1,171 @@
import { eq, sql } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js";
import { qualityRequest } from "../../../../database/schema/qualityRequest.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { query } from "../../sqlServer/prodSqlServer.js";
import { qrequestQuery } from "../../sqlServer/querys/quality/request.js";
import { timeZoneFix } from "../../../globalUtils/timeZoneFix.js";
export const addNewPallet = async (data: any, user: string) => {
/**
* Post new pallets
*/
if (parseInt(data.runningNr) <= 0) {
return {
sucess: false,
message: "Please add a valid running number.",
};
}
const updateQuery = qrequestQuery.replaceAll(
"[runningNumber]",
data.runningNr
);
const { data: c, error: ce } = await tryCatch(
db
.select()
.from(qualityRequest)
.where(eq(qualityRequest.runningNr, data.runningNr))
);
if (ce) {
return {
success: false,
message: "There was an error getting the quality request",
data: ce,
};
}
const palletData: any = c;
// if the pallet exist then tell the user to check on it
if (
(palletData && palletData[0]?.palletStatus === 1) ||
palletData[0]?.palletStatus === 4
) {
return {
success: false,
message: `Running number ${data.runningNr} is already pending or reactivated please follow up with the warehouse team on status to be moved.`,
};
}
if (palletData.length > 0) {
try {
// get the pallet info from stock
const { data: p, error: pe } = await tryCatch(
query(updateQuery, "quality request")
);
if (pe) {
return {
success: false,
message: "There was an error getting the pallet from stock",
data: pe,
};
}
const pData = {
warehouseAtRequest: p[0].warehouseAtRequest,
locationAtRequest: p[0].locationAtRequest,
warehouseMovedTo: null,
locationMovedTo: null,
palletStatus: 4,
durationToMove: 0,
palletStatusText: "reactivated",
palletRequest: palletData[0].palletStatus + 1,
upd_user: user,
upd_date: new Date(timeZoneFix()),
};
const { data: u, error } = await tryCatch(
db
.update(qualityRequest)
.set(pData)
.where(eq(qualityRequest.runningNr, data.runningNr))
);
if (error) {
return {
success: false,
message: `Running number: ${data.runningNr} encountered and error reactivated.`,
data: error,
};
}
if (data) {
return {
success: true,
message: `Running number: ${data.runningNr} was just reactivated.`,
data: u,
};
}
} catch (error) {
return {
success: false,
message:
"There was an error updating the pallet in quality request",
data: error,
};
}
}
// add new pallet
try {
const { data: p, error: pe } = await tryCatch(
query(updateQuery, "quality request")
);
if (p.length === 0) {
return {
success: false,
message: `Running Number ${data.runningNr} dose not exist in stock.`,
};
}
if (pe) {
return {
success: false,
message: "There was an error getting the pallet from stock",
data: pe,
};
}
const nData = {
article: p[0].article,
description: p[0].description,
runningNr: p[0].runningNr,
lotNr: p[0].lotNr,
warehouseAtRequest: p[0].warehouseAtRequest,
locationAtRequest: p[0].locationAtRequest,
locationDropOff: data.moveTo,
palletStatus: 1,
palletStatusText: "pending",
palletRequest: 1,
add_user: user,
upd_user: user,
};
const { data: u, error } = await tryCatch(
db.insert(qualityRequest).values(nData)
);
if (error) {
return {
success: false,
message: `Running number: ${data.runningNr} encountered and error reactivated.`,
data: error,
};
}
if (data) {
return {
success: true,
message: `Running number: ${data.runningNr} was just added.`,
data: u,
};
}
} catch (error) {
return {
success: false,
message: "There was an error adding the pallet in quality request",
data: error,
};
}
};

View File

@@ -0,0 +1,25 @@
import { desc } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js";
import { qualityRequest } from "../../../../database/schema/qualityRequest.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import qualityBlockingMonitor from "../../notifications/controller/notifications/qualityBlocking.js";
export const getRequest = async () => {
const { data, error } = await tryCatch(
db.select().from(qualityRequest).orderBy(desc(qualityRequest.add_date))
);
if (error) {
return {
success: false,
message: "There was an error getting the quality request",
data: error,
};
}
return {
success: true,
message: "Quality request pallets.",
data,
};
};

View File

@@ -0,0 +1,106 @@
import { eq, inArray } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js";
import { qualityRequest } from "../../../../database/schema/qualityRequest.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { createLog } from "../../logger/logger.js";
import { qrequestQuery } from "../../sqlServer/querys/quality/request.js";
import { query } from "../../sqlServer/prodSqlServer.js";
import { differenceInMinutes } from "date-fns";
import { timeZoneFix } from "../../../globalUtils/timeZoneFix.js";
export const qualityCycle = async () => {
/**
* Cycles the pallets in the quality request to see whats been moved or changed.
*/
// pallet request check interval 5min check to start
//setInterval(async () => {
// create the date stuff
const currentTime = new Date(Date.now());
// pull in all current pallets from our db
const { data, error } = await tryCatch(
db
.select()
.from(qualityRequest)
.where(inArray(qualityRequest.palletStatus, [1, 4, 5]))
);
if (error) {
createLog(
"error",
"lst",
"quality",
`There was an error getting quality request data: ${error}`
);
return {
success: false,
message: "There was an error getting quality request data",
};
}
const lstQData: any = data;
// get the pallets that currentStat is moved
// const res = await runQuery(palletMoveCheck, "palletCheck");
if (lstQData.length != 0) {
for (let i = 0; i < lstQData.length; i++) {
// run the pallet query we will compare the data.
// console.log(lstQData[i]);
//update query with plant token
// change the update the pallet number
const qPalletNumber = qrequestQuery.replaceAll(
"[runningNumber]",
lstQData[i].runningNr
);
let prodData: any = [];
prodData = await query(qPalletNumber, "Quality update check");
if (
lstQData[i]?.locationAtRequest != prodData[0]?.locationAtRequest
) {
// time to do the pallet update stuff
const qDataPost = {
warehouseMovedTo: prodData[0]?.warehouseAtRequest,
locationMovedTo: prodData[0]?.locationAtRequest,
durationToMove: differenceInMinutes(
timeZoneFix(),
lstQData[i].upd_date
),
palletStatus: 2,
palletStatusText: "moved",
upd_date: new Date(timeZoneFix()),
upd_user: "LST_System",
};
const updatePallet = await db
.update(qualityRequest)
.set(qDataPost)
.where(eq(qualityRequest.runningNr, lstQData[i].runningNr));
createLog(
"info",
"lst",
"quality",
`Pallet ${lstQData[i].runningNr} was updated`
);
} else {
createLog(
"debug",
"lst",
"quality",
`Pallet ${
lstQData[i].runningNr
} has not been moved yet it has been pending for ${differenceInMinutes(
timeZoneFix(),
lstQData[i].upd_date
)} min(s)`
);
}
}
} else {
createLog("debug", "lst", "quality", "nothing to update");
}
//}, 5 * 60 * 1000); // every 5 min
};

View File

@@ -1,9 +1,19 @@
import { OpenAPIHono } from "@hono/zod-openapi";
import { qualityRequest } from "../../../database/schema/qualityRequest.js";
import { db } from "../../../database/dbclient.js";
import { qualityCycle } from "./controller/qualityCycle.js";
import request from "./route/getRequest.js";
import postReq from "./route/postNewRequest.js";
// pallet status data.
export const statusOptions = [
{ name: "pending", uid: "1" },
{ name: "moved", uid: "2" },
{ name: "removed", uid: "3" },
{ name: "reactivated", uid: "4" },
{ name: "canceled", uid: "5" },
];
const app = new OpenAPIHono();
const routes = [] as const;
const routes = [request, postReq] as const;
const appRoutes = routes.forEach((route) => {
app.route("/quality", route);
@@ -15,6 +25,15 @@ app.all("/quality/*", (c) => {
});
});
await db.select().from(qualityRequest);
/**
* Initial and run the cycle up for checking the pallet moves for quality
*/
setTimeout(() => {
qualityCycle();
}, 1000 * 5);
setInterval(() => {
qualityCycle();
}, 1000 * 60);
export default app;

View File

@@ -0,0 +1,34 @@
// an external way to creating logs
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { getRequest } from "../controller/getRequests.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
const app = new OpenAPIHono({ strict: false });
app.openapi(
createRoute({
tags: ["quality"],
summary: "Returns all pallets requested",
method: "get",
path: "/getrequest",
responses: responses(),
}),
async (c) => {
const { data, error } = await tryCatch(getRequest());
if (error) {
return c.json({
success: false,
message: "There was an error getting the printers",
});
}
return c.json({
success: data.success,
message: data.message,
data: data.data,
});
}
);
export default app;

View File

@@ -0,0 +1,73 @@
// an external way to creating logs
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { getRequest } from "../controller/getRequests.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { authMiddleware } from "../../auth/middleware/authMiddleware.js";
import { addNewPallet } from "../controller/addNewPallet.js";
import { verify } from "hono/jwt";
const app = new OpenAPIHono({ strict: false });
const Body = z.object({
runningNr: z.number().openapi({ example: 1528 }),
moveTo: z.string().openapi({ example: "rework" }),
});
app.openapi(
createRoute({
tags: ["quality"],
summary: "Returns all pallets requested",
method: "post",
path: "/newrequest",
middleware: authMiddleware,
request: {
body: {
content: {
"application/json": { schema: Body },
},
},
},
responses: responses(),
}),
async (c) => {
const authHeader = c.req.header("Authorization");
const token = authHeader?.split("Bearer ")[1] || "";
const payload = await verify(token, process.env.JWT_SECRET!);
const user: any = payload.user;
const { data: b, error: e } = await tryCatch(c.req.json());
if (e) {
return c.json({
success: false,
message: "Missing Data",
});
}
const body: any = b;
// console.log(body);
// if (!body.runningNr) {
// return c.json({
// success: false,
// message: "Missing mandatory data.",
// });
// }
const { data, error } = await tryCatch(
addNewPallet(body, user?.username)
);
if (error) {
return c.json({
success: false,
message: "There was an error adding the new pallet",
});
}
return c.json({
success: data?.success,
message: data?.message,
data: data?.data,
});
}
);
export default app;

View File

@@ -0,0 +1,14 @@
export const qrequestQuery = `
select IdArtikelVarianten as article,
ArtikelVariantenBez as description,
Lfdnr as runningNr,
Produktionslos as lotNr,
IdWarenlager as idWarehouse,
WarenLagerKurzBez as warehouseAtRequest,
IdLagerAbteilung as idLocation,
LagerAbteilungKurzBez as locationAtRequest,
BewegungsDatumMax as lastMove
from AlplaPROD_test1.dbo.V_LagerPositionenBarcodes (nolock)
where /* VerfuegbareMengeSum = 0 and */ IdLagerAbteilung not in (0, 20000, 21000) and lfdnr = [runningNumber]
`;