Compare commits

...

18 Commits

Author SHA1 Message Date
8943407f27 feat(finaly): the final push before moving all to the new lst 2025-09-19 22:16:47 -05:00
0bbe411db0 feat(docs): added in link to the main docs and to the material xfer 2025-09-19 07:56:43 -05:00
99b3ad633c fix(materials): changes for consuming peices vs units 2025-09-18 11:00:54 -05:00
c892348d19 refactor(manual print): added a min length of 10 characters 2025-09-18 11:00:18 -05:00
360549aaf4 fix(consume material): changes made to remove the rest of the auth needs 2025-09-18 10:59:55 -05:00
bdc1e72fc1 refactor(frontend): comment required now when doing manual labels 2025-09-17 20:02:14 -05:00
4cae96b1ee refactor(detach): just a log to monitor better 2025-09-11 06:42:31 -05:00
1cde8ab2e6 fix(mm query): more changes to the material query to please more plants 2025-09-11 06:42:02 -05:00
03e8378213 fix(inhouse): changes to no longer error 2025-09-11 06:39:36 -05:00
275c93dc79 refactor(detach silo): changes to now show the error in the console in the browser 2025-09-11 06:39:07 -05:00
fbb8c21d5c feat(loreal forecast): added in an email to be sent when we are missing skus 2025-09-11 06:38:36 -05:00
dce93d3de2 ci(release): bump build number to 652 2025-09-11 06:32:08 -05:00
faf4e9f9ab ci(release): bump build number to 651 2025-09-11 06:30:48 -05:00
a9783a7d35 ci(release): bump build number to 650 2025-09-10 11:15:07 -05:00
e9ca6dbbb2 ci(release): bump build number to 649 2025-09-10 11:13:59 -05:00
e996f99400 ci(release): bump build number to 648 2025-09-10 06:59:19 -05:00
3b939ff2d3 ci(release): bump build number to 647 2025-09-09 21:21:08 -05:00
a7ff88025e chore(release): 2.27.0 2025-09-09 21:20:39 -05:00
28 changed files with 703 additions and 83 deletions

View File

@@ -1,5 +1,36 @@
# All CHanges to LST can be found below. # All CHanges to LST can be found below.
## [2.27.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.26.0...v2.27.0) (2025-09-10)
### 🌟 Enhancements
* **eom:** lastSales, lastPurch added to be pulled with new template ([7cc3778](https://git.tuffraid.net/cowch/lstV2/commits/7cc3778506fc92392ca8431aee0edb203861e10d))
### 🛠️ Code Refactor
* **cosume:** changes to allow non logged in users to use this function ([271cdbd](https://git.tuffraid.net/cowch/lstV2/commits/271cdbdbfa2478ecf56e9b01a4474508acacda2e))
* **mm query:** changes to the query to see pkg and materials properly and not duplicates ([57966ac](https://git.tuffraid.net/cowch/lstV2/commits/57966ac9de72543014451b7ccd75539296ccaa51))
* **rfid:** changes to show all tags vs only 3 if there are more ([b677bc1](https://git.tuffraid.net/cowch/lstV2/commits/b677bc14981faff30c91f6ffe4602319dd3c6016))
* **silo card:** changes to allow viewers to see and able to attach and detach ([796a8dc](https://git.tuffraid.net/cowch/lstV2/commits/796a8dccd2807890abdff7c8dacf8b2246eb265e))
* **sql:** articles added in UOM ([cfed981](https://git.tuffraid.net/cowch/lstV2/commits/cfed981928a56389e09ef428c43ceabc1caec28e))
### 🐛 Bug fixes
* **article check:** corrected the query to not have a specfic plant in it ([79f1f8f](https://git.tuffraid.net/cowch/lstV2/commits/79f1f8f91ba33533de7f4a7cc91503cfd8dd4ce5))
* **bookin:** corrected the bookin in error response ([1f8b8a7](https://git.tuffraid.net/cowch/lstV2/commits/1f8b8a7248c11c7e264c8c5ae7c042c5a0878c46))
* **bookin:** corrections to only show the message on error vs the json ([b3ce767](https://git.tuffraid.net/cowch/lstV2/commits/b3ce767b323c990c0ccf35ad6c2c67136a27272e))
* **db:** changes to the user so it deletes correctly ([932a72b](https://git.tuffraid.net/cowch/lstV2/commits/932a72ba884673471f0056e721cc3f2c8e34b4f3))
* **eomservice:** changes to stop a crash incase the sql returned nothing due to start up ([6caa598](https://git.tuffraid.net/cowch/lstV2/commits/6caa5984e7a3e7b48b119c176835663ffec71151))
* **frontend:** typos ([c6f6ef6](https://git.tuffraid.net/cowch/lstV2/commits/c6f6ef626295f452cdf26f6776b74cfb3b1a10f5))
* **label query:** fixes to only pull in active layouts ([99ecf52](https://git.tuffraid.net/cowch/lstV2/commits/99ecf52218556e048ba9262e74f9b3d020dea31d))
* **main material check:** corrections to properly ignore pkg during color checks ([6910550](https://git.tuffraid.net/cowch/lstV2/commits/6910550de769dce04b1045f96ab19cf7b8d1ef8c))
* **material check:** split manual material out of the mm to properly catch it ([a53915a](https://git.tuffraid.net/cowch/lstV2/commits/a53915ad8cbec5bd8d6ba4643c460ad0162249e2))
* **materials:** more fixes to try and please all plants to use this version and not call me ([00899a5](https://git.tuffraid.net/cowch/lstV2/commits/00899a5b778eab792b350a0b47589de1524d91c8))
* **register:** changes to not give everyone system admin ([415d2e4](https://git.tuffraid.net/cowch/lstV2/commits/415d2e4a1d851cc46ac64ffc814a280a02293bbc))
## [2.26.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.25.0...v2.26.0) (2025-08-28) ## [2.26.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.25.0...v2.26.0) (2025-08-28)

View File

@@ -1,3 +1,7 @@
# THIS VERSION IS NO LONGER BEING UPDATED PLEASE GO TO THE NEW REPO LINK BELOW
[NEW LST REPO](https://git.tuffraid.net/cowch/lst)
# lstV2 # lstV2
Logistics Support Tool V2 Logistics Support Tool V2

View File

@@ -1,26 +1,30 @@
import {LstCard} from "@/components/extendedUI/LstCard"; import { LstCard } from "@/components/extendedUI/LstCard";
import {Button} from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import {CardHeader} from "@/components/ui/card"; import { CardHeader } from "@/components/ui/card";
import {Input} from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import {Label} from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import {useSessionStore} from "@/lib/store/sessionStore";
import axios from "axios";
import {useState} from "react";
import {useForm} from "react-hook-form"; import axios from "axios";
import {toast} from "sonner"; import { useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
export default function ConsumeMaterial() { export default function ConsumeMaterial() {
const {register: register1, handleSubmit: handleSubmit1, reset} = useForm(); const {
register: register1,
handleSubmit: handleSubmit1,
reset,
} = useForm();
const [submitting, setSubmitting] = useState(false); const [submitting, setSubmitting] = useState(false);
const {token} = useSessionStore();
const handleConsume = async (data: any) => { const handleConsume = async (data: any) => {
setSubmitting(true); setSubmitting(true);
try { try {
const result = await axios.post(`/api/logistics/consume`, data, { // const result = await axios.post(`/api/logistics/consume`, data, {
headers: {Authorization: `Bearer ${token}`}, // headers: {Authorization: `Bearer ${token}`},
}); // });
const result = await axios.post(`/api/logistics/consume`, data);
if (result.data.success) { if (result.data.success) {
toast.success(result.data.message); toast.success(result.data.message);
setSubmitting(false); setSubmitting(false);
@@ -33,12 +37,14 @@ export default function ConsumeMaterial() {
toast.error(result.data.message); toast.error(result.data.message);
} }
} catch (error: any) { } catch (error: any) {
//console.log(error); console.log(error);
setSubmitting(false); setSubmitting(false);
if (error.status === 401) { if (error.status === 401) {
toast.error("Unauthorized to do this task."); toast.error("Unauthorized to do this task.");
} else { } else {
toast.error("Unexpected error if this continues please constact an admin."); toast.error(
"Unexpected error if this continues please constact an admin."
);
} }
} }
}; };
@@ -53,7 +59,9 @@ export default function ConsumeMaterial() {
<LstCard> <LstCard>
<form onSubmit={handleSubmit1(handleConsume)}> <form onSubmit={handleSubmit1(handleConsume)}>
<div className="m-2"> <div className="m-2">
<Label htmlFor="runningNr">Enter unit running number</Label> <Label htmlFor="runningNr">
Enter unit running number
</Label>
<Input <Input
className="mt-2" className="mt-2"
//defaultValue="634" //defaultValue="634"
@@ -62,7 +70,9 @@ export default function ConsumeMaterial() {
/> />
</div> </div>
<div className="m-2"> <div className="m-2">
<Label htmlFor="lotNum">Enter lot number</Label> <Label htmlFor="lotNum">
Enter lot number
</Label>
<Input <Input
className="mt-2" className="mt-2"
//defaultValue="634" //defaultValue="634"
@@ -71,7 +81,12 @@ export default function ConsumeMaterial() {
/> />
</div> </div>
<Button className="m-2" color="primary" type="submit" disabled={submitting}> <Button
className="m-2"
color="primary"
type="submit"
disabled={submitting}
>
Consume materal Consume materal
</Button> </Button>
</form> </form>
@@ -81,12 +96,19 @@ export default function ConsumeMaterial() {
<LstCard> <LstCard>
<div className="w-96 p-1"> <div className="w-96 p-1">
<ol> <ol>
<li>1. Enter the running number of the material you would like to consume</li> <li>
<li>2. Enter the lot number you will be consuming to</li> 1. Enter the running number of the
material you would like to consume
</li>
<li>
2. Enter the lot number you will be
consuming to
</li>
<li>3. Press consume material</li> <li>3. Press consume material</li>
</ol> </ol>
<p className="text-pretty w-96"> <p className="text-pretty w-96">
*This process is only for barcoded material, if it is set to auto consume you will *This process is only for barcoded material,
if it is set to auto consume you will
encounter and error. encounter and error.
</p> </p>
</div> </div>

View File

@@ -14,13 +14,16 @@ import {
TooltipContent, TooltipContent,
TooltipTrigger, TooltipTrigger,
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
import { useSettingStore } from "@/lib/store/useSettings";
export default function TransferToNextLot() { export default function TransferToNextLot() {
const [gaylordFilled, setGaylordFilled] = useState([0]); const [gaylordFilled, setGaylordFilled] = useState([0]);
const [actualAmount, setActualAmount] = useState(0); const [actualAmount, setActualAmount] = useState(0);
const [tab, setTab] = useState("esitmate"); const [tab, setTab] = useState("esitmate");
const [typeSwitch, setTypeSwitch] = useState(false); const [typeSwitch, setTypeSwitch] = useState(false);
const { settings } = useSettingStore();
const server = settings.filter((n: any) => n.name === "dbServer");
const form = useAppForm({ const form = useAppForm({
defaultValues: { defaultValues: {
runningNumber: "", runningNumber: "",
@@ -111,7 +114,7 @@ export default function TransferToNextLot() {
} }
> >
<p className="text-center"> <p className="text-center">
Almost full Almost full - 95%
</p> </p>
</button> </button>
<button <button
@@ -129,7 +132,7 @@ export default function TransferToNextLot() {
} }
> >
<p className="text-center"> <p className="text-center">
About 75% full About full - 75%
</p> </p>
</button> </button>
<button <button
@@ -145,7 +148,7 @@ export default function TransferToNextLot() {
} }
> >
<p className="text-center"> <p className="text-center">
Half full Half full - 50%
</p> </p>
</button> </button>
<button <button
@@ -163,7 +166,7 @@ export default function TransferToNextLot() {
} }
> >
<p className="text-center"> <p className="text-center">
Almost empty Almost empty - 25%
</p> </p>
</button> </button>
</div> </div>
@@ -218,7 +221,7 @@ export default function TransferToNextLot() {
name="runningNumber" name="runningNumber"
children={(field) => ( children={(field) => (
<field.InputField <field.InputField
label="Runnung Number" label="Running Number"
inputType="number" inputType="number"
required={true} required={true}
/> />
@@ -398,6 +401,19 @@ export default function TransferToNextLot() {
process, this process will just get process, this process will just get
the gaylord to the next lot. the gaylord to the next lot.
</p> </p>
<br />
{settings.length > 0 && (
<p>
For more in depth instructions
please{" "}
<a
href={`https://${server[0].value}.alpla.net/lst/d/docs/ocp/ocp#tranfer-partial-estimated-quantity-to-the-next-lot`}
target="_blank"
>
<em>CLICK HERE</em>
</a>
</p>
)}
</div> </div>
) : ( ) : (
<div> <div>
@@ -437,6 +453,19 @@ export default function TransferToNextLot() {
process, this process will just get process, this process will just get
the gaylord to the next lot. the gaylord to the next lot.
</p> </p>
<br />
{settings.length > 0 && (
<p>
For more in depth instructions
please{" "}
<a
href={`https://${server[0].value}.alpla.net/lst/d/docs/ocp/ocp#tranfer-partial-estimated-quantity-to-the-next-lot`}
target="_blank"
>
<em>CLICK HERE</em>
</a>
</p>
)}
</div> </div>
)} )}
</LstCard> </LstCard>

View File

@@ -48,6 +48,7 @@ export function DetachSilo(props: any) {
); );
if (res.status === 200) { if (res.status === 200) {
console.log(res.data.data);
toast.success(res.data.message); toast.success(res.data.message);
refetch(); refetch();

View File

@@ -243,7 +243,8 @@ export default function ManualPrintForm() {
<Textarea <Textarea
//label="Comments" //label="Comments"
placeholder="add more info as needed." placeholder="add more info as needed."
{...register("additionalComments")} {...(register("additionalComments"),
{ required: true, minLength: 10 })}
/> />
</div> </div>

View File

@@ -28,6 +28,7 @@ import { useLogout } from "@/hooks/useLogout";
import ExportInventoryData from "@/components/logistics/warehouse/ExportInventoryData"; import ExportInventoryData from "@/components/logistics/warehouse/ExportInventoryData";
import { AddCards } from "@/components/dashboard/AddCards"; import { AddCards } from "@/components/dashboard/AddCards";
import DMButtons from "@/components/logistics/dm/DMButtons"; import DMButtons from "@/components/logistics/dm/DMButtons";
import { useSettingStore } from "@/lib/store/useSettings";
//import { AddCards } from "@/components/dashboard/AddCards"; //import { AddCards } from "@/components/dashboard/AddCards";
// same as the layout // same as the layout
@@ -38,6 +39,9 @@ export const Route = createRootRoute({
const { user } = useSessionStore(); const { user } = useSessionStore();
const logout = useLogout(); const logout = useLogout();
const location = useLocation(); const location = useLocation();
const { settings } = useSettingStore();
const server = settings.filter((n: any) => n.name === "dbServer");
return ( return (
<div className="overflow-hidden"> <div className="overflow-hidden">
@@ -61,6 +65,17 @@ export const Route = createRootRoute({
<div className="m-1"> <div className="m-1">
<ModeToggle /> <ModeToggle />
</div> </div>
<div className="mr-1 ml-1">
{settings.length > 0 && (
<a
href={`https://${server[0].value}.alpla.net/lst/d`}
target="_blank"
>
LST - Docs
</a>
)}
</div>
{session ? ( {session ? (
<div className="m-1"> <div className="m-1">
<DropdownMenu> <DropdownMenu>

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "lstv2", "name": "lstv2",
"version": "2.26.0", "version": "2.27.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "lstv2", "name": "lstv2",
"version": "2.26.0", "version": "2.27.0",
"dependencies": { "dependencies": {
"@dotenvx/dotenvx": "^1.45.1", "@dotenvx/dotenvx": "^1.45.1",
"@hono/node-server": "^1.14.4", "@hono/node-server": "^1.14.4",

View File

@@ -1,6 +1,6 @@
{ {
"name": "lstv2", "name": "lstv2",
"version": "2.26.0", "version": "2.27.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "concurrently -n \"server,frontend\" -c \"#007755,#2f6da3\" \"npm run dev:server\" \"cd frontend && npm run dev\"", "dev": "concurrently -n \"server,frontend\" -c \"#007755,#2f6da3\" \"npm run dev:server\" \"cd frontend && npm run dev\"",
@@ -36,7 +36,7 @@
} }
}, },
"admConfig": { "admConfig": {
"build": 646, "build": 661,
"oldBuild": "backend-0.1.3.zip" "oldBuild": "backend-0.1.3.zip"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -0,0 +1,63 @@
import { and, between, inArray, sql } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js";
import { invHistoricalData } from "../../../../database/schema/historicalINV.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { createLog } from "../../logger/logger.js";
// type ArticleData = {
// id: string
// }
export const psiGetInventory = async (
avs: string,
startDate: string,
endDate: string
) => {
let articles: any = [];
if (!avs) {
return {
success: false,
message: `Missing av's please send at least one over`,
data: [],
};
}
const ids = avs.split(",").map((id) => id.trim());
const { data, error } = (await tryCatch(
db
.select()
.from(invHistoricalData)
.where(
and(
inArray(invHistoricalData.article, ids),
between(invHistoricalData.histDate, startDate, endDate)
)
)
//.limit(100)
)) as any;
if (error) {
createLog(
"error",
"datamart",
"datamart",
`There was an error getting the planning info: ${JSON.stringify(
error
)}`
);
return {
success: false,
messsage: `There was an error getting the planning info`,
data: error,
};
}
articles = data;
console.log(articles.length);
return {
success: true,
message: "PSI planning Data",
data: articles,
};
};

View File

@@ -0,0 +1,63 @@
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { createLog } from "../../logger/logger.js";
import { query } from "../../sqlServer/prodSqlServer.js";
import { planningNumbersByAVDate } from "../../sqlServer/querys/psiReport/planningNumbersByAv.js";
// type ArticleData = {
// id: string
// }
export const psiGetPlanningData = async (
avs: string,
startDate: string,
endDate: string
) => {
let articles: any = [];
if (!avs) {
return {
success: false,
message: `Missing av's please send at least one over`,
data: [],
};
}
const { data, error } = (await tryCatch(
query(
planningNumbersByAVDate
.replace("[articles]", avs)
.replace("[startDate]", startDate)
.replace("[endDate]", endDate),
"PSI planning info"
)
)) as any;
if (error) {
createLog(
"error",
"datamart",
"datamart",
`There was an error getting the planning info: ${JSON.stringify(
error
)}`
);
return {
success: false,
messsage: `There was an error getting the planning info`,
data: error,
};
}
articles = data.data;
return {
success: true,
message: "PSI planning Data",
data: articles.map((n: any) => {
if (n.PalDay) {
return { ...n, PalDay: n.PalDay.toFixed(2) };
}
return n;
}),
};
};

View File

@@ -0,0 +1,63 @@
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { createLog } from "../../logger/logger.js";
import { query } from "../../sqlServer/prodSqlServer.js";
import { productionNumbers } from "../../sqlServer/querys/psiReport/prodcuctionNumbers.js";
// type ArticleData = {
// id: string
// }
export const psiGetProductionData = async (
avs: string,
startDate: string,
endDate: string
) => {
let articles: any = [];
if (!avs) {
return {
success: false,
message: `Missing av's please send at least one over`,
data: [],
};
}
const { data, error } = (await tryCatch(
query(
productionNumbers
.replace("[articles]", avs)
.replace("[startDate]", startDate)
.replace("[endDate]", endDate),
"PSI production info"
)
)) as any;
if (error) {
createLog(
"error",
"datamart",
"datamart",
`There was an error getting the planning info: ${JSON.stringify(
error
)}`
);
return {
success: false,
messsage: `There was an error getting the planning info`,
data: error,
};
}
articles = data.data;
return {
success: true,
message: "PSI planning Data",
data: articles.map((n: any) => {
if (n.PalDay) {
return { ...n, PalDay: n.PalDay.toFixed(2) };
}
return n;
}),
};
};

View File

@@ -10,6 +10,9 @@ import addressCorrections from "./route/getCityStateData.js";
import fifoIndex from "./route/getFifoIndex.js"; import fifoIndex from "./route/getFifoIndex.js";
import financeAudit from "./route/getFinanceAudit.js"; import financeAudit from "./route/getFinanceAudit.js";
import psiArticleData from "./route/getPsiArticleData.js"; import psiArticleData from "./route/getPsiArticleData.js";
import psiPlanningData from "./route/getPsiPlanningData.js";
import psiProductionData from "./route/getPsiProductionData.js";
import psiInventory from "./route/getPsiinventory.js";
const app = new OpenAPIHono(); const app = new OpenAPIHono();
@@ -25,6 +28,9 @@ const routes = [
fifoIndex, fifoIndex,
financeAudit, financeAudit,
psiArticleData, psiArticleData,
psiPlanningData,
psiProductionData,
psiInventory,
] as const; ] as const;
const appRoutes = routes.forEach((route) => { const appRoutes = routes.forEach((route) => {

View File

@@ -0,0 +1,64 @@
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 { psiGetPlanningData } from "../controller/psiGetPlanningData.js";
const app = new OpenAPIHono({ strict: false });
const Body = z.object({
includeRunnningNumbers: z.string().openapi({ example: "x" }),
});
app.openapi(
createRoute({
tags: ["dataMart"],
summary: "Returns the psiarticleData.",
method: "get",
path: "/psiplanningdata",
request: {
body: {
content: {
"application/json": { schema: Body },
},
},
},
responses: responses(),
}),
async (c) => {
const q: any = c.req.queries();
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/psiplanningdata" });
//console.log(articles["avs"][0]);
const { data, error } = await tryCatch(
psiGetPlanningData(
q["avs"] ? q["avs"][0] : null,
q["startDate"] ? q["startDate"][0] : null,
q["endDate"] ? q["endDate"][0] : null
)
);
if (error) {
console.log(error);
return c.json(
{
success: false,
message: "There was an error getting the planning.",
data: error,
},
400
);
}
//console.log(data);
return c.json(
{
success: data.success,
message: data.message,
data: data.data,
},
data.success ? 200 : 400
);
}
);
export default app;

View File

@@ -0,0 +1,64 @@
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 { psiGetProductionData } from "../controller/psiGetProductionData.js";
const app = new OpenAPIHono({ strict: false });
const Body = z.object({
includeRunnningNumbers: z.string().openapi({ example: "x" }),
});
app.openapi(
createRoute({
tags: ["dataMart"],
summary: "Returns the psiproductiondata.",
method: "get",
path: "/psiproductiondata",
request: {
body: {
content: {
"application/json": { schema: Body },
},
},
},
responses: responses(),
}),
async (c) => {
const q: any = c.req.queries();
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/psiproductiondata" });
//console.log(articles["avs"][0]);
const { data, error } = await tryCatch(
psiGetProductionData(
q["avs"] ? q["avs"][0] : null,
q["startDate"] ? q["startDate"][0] : null,
q["endDate"] ? q["endDate"][0] : null
)
);
if (error) {
console.log(error);
return c.json(
{
success: false,
message: "There was an error getting the production.",
data: error,
},
400
);
}
//console.log(data);
return c.json(
{
success: data.success,
message: data.message,
data: data.data,
},
data.success ? 200 : 400
);
}
);
export default app;

View File

@@ -0,0 +1,64 @@
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 { psiGetInventory } from "../controller/psiGetInventory.js";
const app = new OpenAPIHono({ strict: false });
const Body = z.object({
includeRunnningNumbers: z.string().openapi({ example: "x" }),
});
app.openapi(
createRoute({
tags: ["dataMart"],
summary: "Returns the getPsiinventory.",
method: "get",
path: "/getpsiinventory",
request: {
body: {
content: {
"application/json": { schema: Body },
},
},
},
responses: responses(),
}),
async (c) => {
const q: any = c.req.queries();
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/getpsiinventory" });
//console.log(articles["avs"][0]);
const { data, error } = await tryCatch(
psiGetInventory(
q["avs"] ? q["avs"][0] : null,
q["startDate"] ? q["startDate"][0] : null,
q["endDate"] ? q["endDate"][0] : null
)
);
if (error) {
console.log(error);
return c.json(
{
success: false,
message: "There was an error getting the production.",
data: error,
},
400
);
}
//console.log(data);
return c.json(
{
success: data.success,
message: data.message,
data: data.data,
},
data.success ? 200 : 400
);
}
);
export default app;

View File

@@ -27,7 +27,7 @@ export const deleteHistory = async () => {
.where( .where(
lte( lte(
invHistoricalData.histDate, invHistoricalData.histDate,
sql`(NOW() - INTERVAL '45 day')::date` sql`(NOW() - INTERVAL '365 day')::date`
) )
) )
); );

View File

@@ -7,6 +7,8 @@ import { postForecast } from "../postForecast.js";
import { query } from "../../../../../sqlServer/prodSqlServer.js"; import { query } from "../../../../../sqlServer/prodSqlServer.js";
import { activeArticle } from "../../../../../sqlServer/querys/dataMart/article.js"; import { activeArticle } from "../../../../../sqlServer/querys/dataMart/article.js";
import { addDays } from "date-fns"; import { addDays } from "date-fns";
import { sendEmail } from "../../../../../notifications/controller/sendMail.js";
import { createLog } from "../../../../../logger/logger.js";
let customerID = 4; let customerID = 4;
export const lorealForecast = async (data: any, user: any) => { export const lorealForecast = async (data: any, user: any) => {
@@ -111,6 +113,9 @@ export const lorealForecast = async (data: any, user: any) => {
); );
if (activeAV.length === 0) { if (activeAV.length === 0) {
if (typeof forcast.customerArticleNo === "number") {
missingSku.push(forcast);
}
continue; continue;
} }
@@ -181,6 +186,9 @@ export const lorealForecast = async (data: any, user: any) => {
); );
if (activeAV.length === 0) { if (activeAV.length === 0) {
if (typeof forcast.customerArticleNo === "number") {
missingSku.push(forcast);
}
continue; continue;
} }
@@ -190,6 +198,56 @@ export const lorealForecast = async (data: any, user: any) => {
//console.log(comForecast); //console.log(comForecast);
// email the for the missing ones
const missedGrouped = Object.values(
missingSku.reduce((acc: any, item: any) => {
const key = item.customerArticleNo;
if (!acc[key]) {
// first time we see this customer
acc[key] = item;
} else {
// compare dates and keep the earliest
if (
new Date(item.requirementDate) <
new Date(acc[key].requirementDate)
) {
acc[key] = item;
}
}
return acc;
}, {})
);
const emailSetup = {
email: "Blake.matthes@alpla.com; Stuart.Gladney@alpla.com; Harold.Mccalister@alpla.com; Jenn.Osbourn@alpla.com",
subject:
missedGrouped.length > 0
? `Alert! There are ${missedGrouped.length}, missing skus.`
: `Alert! There is a missing SKU.`,
template: "missingLorealSkus",
context: {
items: missedGrouped,
},
};
const { data: sentEmail, error: sendEmailError } = await tryCatch(
sendEmail(emailSetup)
);
if (sendEmailError) {
createLog(
"error",
"blocking",
"notify",
"Failed to send email, will try again on next interval"
);
return {
success: false,
message: "Failed to send email, will try again on next interval",
};
}
// if the customerarticle number is not matching just ignore it // if the customerarticle number is not matching just ignore it
const predefinedObject = { const predefinedObject = {
receivingPlantId: plantToken[0].value, receivingPlantId: plantToken[0].value,

View File

@@ -13,7 +13,7 @@ type Data = {
runningNr: string; runningNr: string;
lotNum: number; lotNum: number;
}; };
export const consumeMaterial = async (data: Data, prod: any) => { export const consumeMaterial = async (data: Data) => {
const { runningNr, lotNum } = data; const { runningNr, lotNum } = data;
// replace the rn // replace the rn
@@ -28,12 +28,7 @@ export const consumeMaterial = async (data: Data, prod: any) => {
barcode = r?.data; barcode = r?.data;
} catch (error) { } catch (error) {
console.log(error); console.log(error);
createLog( createLog("error", "", "logistics", `Error getting barcode: ${error}`);
"error",
prod.user.username,
"logistics",
`Error getting barcode: ${error}`
);
} }
if (barcode.length === 0) { if (barcode.length === 0) {

View File

@@ -51,15 +51,14 @@ app.openapi(
); );
} }
apiHit(c, { endpoint: "/consume", lastBody: data }); apiHit(c, { endpoint: "/consume", lastBody: data });
const authHeader = c.req.header("Authorization"); //const authHeader = c.req.header("Authorization");
const token = authHeader?.split("Bearer ")[1] || ""; //const token = authHeader?.split("Bearer ")[1] || "";
try { //const payload = await verify(token, process.env.JWT_SECRET!);
const payload = await verify(token, process.env.JWT_SECRET!);
try { try {
//return apiReturn(c, true, access?.message, access?.data, 200); //return apiReturn(c, true, access?.message, access?.data, 200);
const consume = await consumeMaterial(data, payload); const consume = await consumeMaterial(data);
return c.json( return c.json(
{ success: consume?.success, message: consume?.message }, { success: consume?.success, message: consume?.message },
200 200
@@ -76,9 +75,6 @@ app.openapi(
400 400
); );
} }
} catch (error) {
return c.json({ success: false, message: "Unauthorized" }, 401);
}
} }
); );
export default app; export default app;

View File

@@ -57,6 +57,7 @@ app.openapi(
}); });
} }
console.log(silo);
return c.json({ return c.json({
success: silo.success, success: silo.success,
message: silo.message, message: silo.message,

View File

@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{{!-- <link rel="stylesheet" href="styles/styles.css" /> --}}
{{> styles}}
</head>
<body>
<p>All,</p>
<p>The below SKU's do not currently have an av and will be ignored in the forcast import.</p>
<p>The date and qty are the first time needed showing from teh vmi report</p>
<table >
<thead>
<tr>
<th>Customer Article Number</th>
<th>First Date Needed</th>
<th>Quantity</th>
</tr>
</thead>
<tbody>
{{#each items}}
<tr>
<td>{{customerArticleNo}}</td>
<td>{{requirementDate}}</td>
<td>{{quantity}}</td>
</tr>
{{/each}}
</tbody>
</table>
<div>
<p>Thank you,</p>
<p>LST Team</p>
</div>
</body>
</html>

View File

@@ -289,11 +289,13 @@ export const labelingProcess = async ({
"error", "error",
"labeling", "labeling",
"ocp", "ocp",
`There was an error booking in the label: ${book.data?.errors[0]?.message}` `There was an error booking in the label: ${JSON.stringify(
book.data
)}`
); );
return { return {
success: false, success: false,
message: `Error Booking in label: ${book.data?.errors[0]?.message}`, message: `Error Booking in label`,
data: book, data: book,
}; };
} }

View File

@@ -33,13 +33,13 @@ export const delieryInhouse = async (data: any) => {
"error", "error",
"labeling", "labeling",
"ocp", "ocp",
`${data.printer.name}, Error:${res.data.Message}` `${data.printer?.name}, Error:${res.data.Message}`
); );
//printerUpdate(data.printer, 7, "Error while deliverying inhouse."); //printerUpdate(data.printer, 7, "Error while deliverying inhouse.");
return { return {
success: true, success: true,
message: `${data.printer.name} had an error while trying to deliver.`, message: `${data.printer?.name} had an error while trying to deliver.`,
data: res.data, data: res.data,
}; };
} // label was just delivered } // label was just delivered

View File

@@ -52,7 +52,7 @@ MaterialHumanReadableId
,x.EffectiveConsumption as consumption -- this is how much was consummed via cmd 112 ,x.EffectiveConsumption as consumption -- this is how much was consummed via cmd 112
,x.TotalDemand as totalDemand -- the total demand needed to finish the lot out ,x.TotalDemand as totalDemand -- the total demand needed to finish the lot out
,case when cp.Pieces >= 0.001 then (lot.TotalProducedQuantity+1) * cp.Pieces else ,case when cp.Pieces >= 0.001 then (lot.TotalProducedLoadingUnits+1) * cp.Pieces else
(a.Weight *((case when cp.Percentage is null then 80 else cp.Percentage end) / 100) * ((lot.TotalProducedLoadingUnits+1) * p.LoadingUnitPieces)) / 1000 end totalNeeded (a.Weight *((case when cp.Percentage is null then 80 else cp.Percentage end) / 100) * ((lot.TotalProducedLoadingUnits+1) * p.LoadingUnitPieces)) / 1000 end totalNeeded
,case when IsMainMaterial = 1 then ,case when IsMainMaterial = 1 then
@@ -135,4 +135,6 @@ group by IdArtikelVarianten,ArtikelVariantenBez
l.IdArtikelVarianten = MaterialHumanReadableId l.IdArtikelVarianten = MaterialHumanReadableId
where lot.ProductionLotHumanReadableId = @lot and MaterialDescription not like '%nopal%' where lot.ProductionLotHumanReadableId = @lot and MaterialDescription not like '%nopal%'
and MaterialDescription NOT LIKE '%bb%'
and MaterialDescription NOT LIKE '%mcg%'
`; `;

View File

@@ -1,33 +1,34 @@
export const planningNumbersByAVDate = ` export const planningNumbersByAVDate = `
use AlplaPROD_test1 use AlplaPROD_test1
declare @start_date nvarchar(30) = [startDate] --'2025-01-01' declare @start_date nvarchar(30) = '[startDate]' --'2025-01-01'
declare @end_date nvarchar(30) = [endDate] --'2025-08-09' declare @end_date nvarchar(30) = '[endDate]' --'2025-08-09'
/* /*
articles will need to be passed over as well as the date structure we want to see articles will need to be passed over as well as the date structure we want to see
*/ */
-- planned lots in planning select x.IdArtikelvarianten As Article,
select V_ProdLosProduktionJeProdTag_PLANNING.IdArtikelvarianten As Article,
ProduktionAlias as Description, ProduktionAlias as Description,
standort as MachineId, standort as MachineId,
MaschinenBezeichnung as MachineName, MaschinenBezeichnung as MachineName,
--MaschZyklus as PlanningCycleTime, --MaschZyklus as PlanningCycleTime,
V_ProdLosProduktionJeProdTag_PLANNING.IdProdPlanung as LotNumber, x.IdProdPlanung as LotNumber,
FORMAT(ProdTag, 'MM/dd/yyyy') as ProductionDay, FORMAT(ProdTag, 'MM/dd/yyyy') as ProductionDay,
V_ProdLosProduktionJeProdTag_PLANNING.planMenge as TotalPlanned, x.planMenge as TotalPlanned,
ProduktionMenge as QTYPerDay, ProduktionMenge as QTYPerDay,
round(ProduktionMengeVPK, 2) PalDay, round(ProduktionMengeVPK, 2) PalDay,
Status as finished Status as finished
--MaschStdAuslastung as nee --MaschStdAuslastung as nee
from dbo.V_ProdLosProduktionJeProdTag_PLANNING (nolock) from dbo.V_ProdLosProduktionJeProdTag_PLANNING (nolock) as x
left join left join
dbo.V_ProdPlanung (nolock) on dbo.V_ProdPlanung (nolock) as p on
V_ProdLosProduktionJeProdTag_PLANNING .IdProdPlanung = V_ProdPlanung.IdProdPlanung x.IdProdPlanung = p.IdProdPlanung
where V_ProdLosProduktionJeProdTag_PLANNING.IdArtikelvarianten in ([articles]) and ProdTag between @start_date and @end_date --and IdProdPlanung = 18442 where ProdTag between @start_date and @end_date
and p.IdArtikelvarianten in ([articles])
--and V_ProdLosProduktionJeProdTag_PLANNING.IdKunde = 10
--and IdProdPlanung = 18442
order by ProdTag order by ProdTag desc
`; `;

View File

@@ -1,8 +1,8 @@
export const productionNumbers = ` export const productionNumbers = `
use [test1_AlplaPROD2.0_Reporting] use [test1_AlplaPROD2.0_Reporting]
declare @startDate nvarchar(30) = [startDate] --'2024-12-30' declare @startDate nvarchar(30) = '[startDate]' --'2024-12-30'
declare @endDate nvarchar(30) = [endDate] --'2025-08-09' declare @endDate nvarchar(30) = '[endDate]' --'2025-08-09'
select MachineLocation, select MachineLocation,
ArticleHumanReadableId as article, ArticleHumanReadableId as article,

View File

@@ -0,0 +1,34 @@
export const planningNumbersByAVDate = `
use AlplaPROD_test1
declare @start_date nvarchar(30) = '[startDate]' --'2025-01-01'
declare @end_date nvarchar(30) = '[endDate]' --'2025-08-09'
/*
articles will need to be passed over as well as the date structure we want to see
*/
select x.IdArtikelvarianten As Article,
ProduktionAlias as Description,
standort as MachineId,
MaschinenBezeichnung as MachineName,
--MaschZyklus as PlanningCycleTime,
x.IdProdPlanung as LotNumber,
FORMAT(ProdTag, 'MM/dd/yyyy') as ProductionDay,
x.planMenge as TotalPlanned,
ProduktionMenge as QTYPerDay,
round(ProduktionMengeVPK, 2) PalDay,
Status as finished
--MaschStdAuslastung as nee
from dbo.V_ProdLosProduktionJeProdTag_PLANNING (nolock) as x
left join
dbo.V_ProdPlanung (nolock) as p on
x.IdProdPlanung = p.IdProdPlanung
where ProdTag between @start_date and @end_date
and p.IdArtikelvarianten in ([articles])
--and V_ProdLosProduktionJeProdTag_PLANNING.IdKunde = 10
--and IdProdPlanung = 18442
order by ProdTag desc
`;