feat(notify): shortage bookings based on time and article type

This commit is contained in:
2025-06-20 11:18:37 -05:00
parent 095d724e65
commit c5bd5a7c0a
5 changed files with 268 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
import { eq, sql } from "drizzle-orm";
import { db } from "../../../../../database/dbclient.js";
import { notifications } from "../../../../../database/schema/notifications.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { createLog } from "../../../logger/logger.js";
import { query } from "../../../sqlServer/prodSqlServer.js";
import { sendEmail } from "../sendMail.js";
import { format } from "date-fns-tz";
import { shortageBookings } from "../../../sqlServer/querys/notifications/shortageBookings.js";
export interface Labels {
IdEtikettenHistorie?: number;
}
const notification = async (notifyData: any) => {
/**
* Pass the entire notification over
*/
createLog(
"info",
"wastebooking",
"notify",
`monitoring ${notifyData.name}`
);
// validate if there are any emails.
if (notifyData.emails === "") {
createLog(
"error",
"reprinting",
"notify",
`There are no emails set for ${notifyData.name}`
);
return;
}
//console.log(notifyData);
// update the settings so we have everything we need
let updatedQuery = shortageBookings
.replace("[time]", notifyData?.notifiySettings.time)
.replace("[type]", notifyData?.notifiySettings.type)
.replace("[avType]", notifyData?.notifiySettings.avType);
const { data: l, error: shortageError } = await tryCatch(
query(updatedQuery, "Removed as waste check")
);
const pallets: any = l?.data as any;
//console.log(updatedQuery);
//console.log(pallets);
if (shortageError) {
createLog(
"error",
"reprinting",
"notify",
`Failed to get the labels: ${shortageError}`
);
return;
}
if (pallets.length > 0) {
//send the email :D
const emailSetup = {
email: notifyData.emails,
subject: `Alert! New shortage booking as been completed in the last ${notifyData?.notifiySettings.time} min`,
template: "shortageBookings",
context: {
items: pallets.map((i: any) => {
return {
...i,
bookingDate: format(i.bookingDate, "M/d/yyyy"),
};
}),
},
};
const sentEmail = await sendEmail(emailSetup);
if (!sentEmail.success) {
createLog(
"error",
"reprinting",
"notify",
"Failed to send email, will try again on next interval"
);
return;
}
// // update the last time we ran and the prod id
// const notifUpdate = {
// prodID: labels[0].IdEtikettenHistorie,
// lastRan: nowDate(),
// };
// update the last time ran
const { data, error } = await tryCatch(
db
.update(notifications)
.set({
lastRan: sql`NOW()`,
})
.where(eq(notifications.name, notifyData.name))
);
} else {
return;
}
};
export default notification;

View File

@@ -117,6 +117,30 @@ export const note: any = [
active: false,
notifiySettings: { processTime: 15 },
},
{
name: "palletsRemovedAsWaste",
description:
"Validates stock to make sure, there are no pallets released that have been removed as waste already ",
checkInterval: 15,
timeType: "min",
emails: "blake.matthes@alpla.com",
active: false,
notifiySettings: { prodID: 1 },
},
{
name: "shortageBookings",
description:
"Checks for material shortage bookings by single av type or all types ",
checkInterval: 15,
timeType: "min",
emails: "blake.matthes@alpla.com",
active: false,
notifiySettings: {
time: 15,
type: "all", // change this to something else or leave blank to use the av type
avType: 1,
},
},
];
export const notificationCreate = async () => {

View File

@@ -0,0 +1,44 @@
<!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 labels have been brought back into the system either by relocate or by inventory taking order, please validate these labels and reblock them or reremove them.</p>
<table >
<thead>
<tr>
<th>AV</th>
<th>Desciption</th>
<th>Label Number</th>
<th>Last Moving Date</th>
</tr>
</thead>
<tbody>
{{#each items}}
<tr>
<td>{{av}}</td>
<td>{{alias}}</td>
<td>{{runningnumber}}</td>
<td>{{lastMovingDate}}</td>
</tr>
{{/each}}
</tbody>
</table>
<div>
<p>For a removal process logistcs will need to do this in lst so a reason for the removal can be added.</p>
</div>
<div>
<p>Thank you,</p>
<p>LST Team</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,60 @@
<!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>$shortage bookings were just done on the below pallet(s). </p>
<table >
<thead>
<tr>
<th>Material AV</th>
<th>Material Alias</th>
<th>Production Lot</th>
<th>Production Pallet Running number</th>
<th>Machine</th>
<th>Machine Name</th>
<th>Quantity Shorted</th>
<th>Shortage Date</th>
</tr>
</thead>
<tbody>
{{#each items}}
<tr>
<td>{{materialAV}}</td>
<td>{{materialAlias}}</td>
<td>{{productionlot}}</td>
<td>{{palletWithShortBookings}}</td>
<td>{{machineNumber}}</td>
<td>{{machineAlias}}</td>
<td>{{qtyShortpcs}}</td>
<td>{{bookingDate}}</td>
</tr>
{{/each}}
</tbody>
</table>
<div>
<p>This can be corrected by following the below simple instructions.</p>
<ol type="1">
<li>Bring the pallet back to PPOO</li>
<li>Book out the pallet</li>
<li>Make the corrections to stock for the above materials/packaging missing</li>
<li>Book the pallet back in.</li>
</ol>
<br/>
<p>For further instructions please reach out to regional support via helpdesk ticket</p>
</div>
<div>
<p>Thank you,</p>
<p>LST Team</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,31 @@
export const shortageBookings = `
use AlplaPROD_test1
Declare @range int = [time] -- change this to be range in minutues you want to monitor, this shouldnt be more than the interval check so we do not see duplicates
declare @avType nvarchar(3) = '[type]' --change to blank or single to have specific ones if all the type is ignored
declare @avTypeID NVARCHAR(MAX) = '[avType]' -- this can only be 1 article now.
select
IdArtikelVarianten as materialAV
,IdArtikelTyp
,ArtikelTypBez
,ArtikelVariantenAlias as materialAlias
,CAST(Menge as varchar) as qtyShortpcs
,ProduktionsLos as productionlot
,LEFT(PARSE(Right(barcode, 39) as int), LEN(PARSE(Right(barcode, 39)as int)) - 1) as palletWithShortBookings
,m.Standort as machineNumber
,m.Bezeichnung ,m.Bezeichnung as machineAlias
,Buchungsdatum as bookingDate
from [dbo].[V_LagerBuchungen] (nolock) s
left join
dbo.T_Maschine (nolock) as m
on m.IdMaschine = s.IdMaschine
where beleg like '%$Sho%' and s.Add_Date > DATEADD(MINUTE, -@range, getdate())
and (@avType = 'all' or IdArtikelTyp in (@avTypeID))
order by ProduktionsLos
`;