feat(notify): material per day for the next 90 days
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
import {
|
||||
addDays,
|
||||
format,
|
||||
formatISO,
|
||||
isBefore,
|
||||
parseISO,
|
||||
startOfWeek,
|
||||
} from "date-fns";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { query } from "../../../sqlServer/prodSqlServer.js";
|
||||
import { materialPerDay } from "../../../sqlServer/querys/dataMart/materialPerDay.js";
|
||||
|
||||
function toDate(val: any) {
|
||||
if (val instanceof Date) return val;
|
||||
if (typeof val === "string") return parseISO(val.replace(" ", "T"));
|
||||
return new Date(val);
|
||||
}
|
||||
|
||||
export function sumByMaterialAndWeek(data: any) {
|
||||
/** @type {Record<string, Record<string, number>>} */
|
||||
const grouped: any = {};
|
||||
|
||||
for (const r of data) {
|
||||
const mat = String(r.MaterialHumanReadableId);
|
||||
const d = toDate(r.CalDate);
|
||||
const week = formatISO(startOfWeek(d, { weekStartsOn: 1 }), {
|
||||
representation: "date",
|
||||
});
|
||||
|
||||
grouped[mat] ??= {};
|
||||
grouped[mat][week] ??= 0;
|
||||
grouped[mat][week] += Number(r.DailyMaterialDemand) || 0;
|
||||
}
|
||||
|
||||
const result = [];
|
||||
for (const [mat, weeks] of Object.entries(grouped)) {
|
||||
// @ts-ignore
|
||||
for (const [week, total] of Object.entries(weeks)) {
|
||||
result.push({
|
||||
MaterialHumanReadableId: mat,
|
||||
WeekStart: week,
|
||||
WeeklyDemand: Number(total).toFixed(2),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export default async function materialPerDayCheck() {
|
||||
/**
|
||||
* getting the shipped pallets
|
||||
*/
|
||||
|
||||
const startDate = format(new Date(Date.now()), "yyyy-MM-dd");
|
||||
const endDate = format(addDays(new Date(Date.now()), 90), "yyyy-MM-dd");
|
||||
|
||||
const { data, error } = (await tryCatch(
|
||||
query(
|
||||
materialPerDay
|
||||
.replace("[startDate]", startDate)
|
||||
.replace("[endDate]", endDate),
|
||||
"material check",
|
||||
),
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Error getting the material data",
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
message: data.message,
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "material data",
|
||||
data: sumByMaterialAndWeek(data.data),
|
||||
};
|
||||
}
|
||||
@@ -1,60 +1,60 @@
|
||||
import { OpenAPIHono } from "@hono/zod-openapi";
|
||||
|
||||
import sendemail from "./routes/sendMail.js";
|
||||
import { tryCatch } from "../../globalUtils/tryCatch.js";
|
||||
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 fifoIndex from "./routes/fifoIndex.js";
|
||||
import notifyStats from "./routes/getActiveNotifications.js";
|
||||
import notify from "./routes/getNotifications.js";
|
||||
import tiTrigger from "./routes/manualTiggerTi.js";
|
||||
import materialCheck from "./routes/materialPerDay.js";
|
||||
import blocking from "./routes/qualityBlocking.js";
|
||||
import sendemail from "./routes/sendMail.js";
|
||||
import { note, notificationCreate } from "./utils/masterNotifications.js";
|
||||
import { startNotificationMonitor } from "./utils/processNotifications.js";
|
||||
import notifyStats from "./routes/getActiveNotifications.js";
|
||||
import tiTrigger from "./routes/manualTiggerTi.js";
|
||||
import blocking from "./routes/qualityBlocking.js";
|
||||
import notify from "./routes/getNotifications.js";
|
||||
import fifoIndex from "./routes/fifoIndex.js";
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
|
||||
const routes = [
|
||||
sendemail,
|
||||
notifyStats,
|
||||
tiTrigger,
|
||||
blocking,
|
||||
notify,
|
||||
fifoIndex,
|
||||
sendemail,
|
||||
notifyStats,
|
||||
tiTrigger,
|
||||
blocking,
|
||||
notify,
|
||||
fifoIndex,
|
||||
materialCheck,
|
||||
] as const;
|
||||
|
||||
const appRoutes = routes.forEach((route) => {
|
||||
app.route("/notify", route);
|
||||
app.route("/notify", route);
|
||||
});
|
||||
|
||||
app.all("/notify/*", (c) => {
|
||||
return c.json({
|
||||
success: false,
|
||||
message: "you have encounted a notication route that dose not exist.",
|
||||
});
|
||||
return c.json({
|
||||
success: false,
|
||||
message: "you have encounted a notication route that dose not exist.",
|
||||
});
|
||||
});
|
||||
|
||||
// check if the mastNotications is changed compared to the db and add if needed.
|
||||
const { data: notes, error: notesError } = await tryCatch(
|
||||
db.select().from(notifications)
|
||||
db.select().from(notifications),
|
||||
);
|
||||
|
||||
if (notesError) {
|
||||
createLog(
|
||||
"error",
|
||||
"notify",
|
||||
"notify",
|
||||
`There was an error getting the notifications: ${JSON.stringify(
|
||||
notesError
|
||||
)}`
|
||||
);
|
||||
createLog(
|
||||
"error",
|
||||
"notify",
|
||||
"notify",
|
||||
`There was an error getting the notifications: ${JSON.stringify(
|
||||
notesError,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
notificationCreate();
|
||||
startNotificationMonitor();
|
||||
notificationCreate();
|
||||
startNotificationMonitor();
|
||||
}, 5 * 1000);
|
||||
|
||||
export default app;
|
||||
|
||||
37
lstV2/server/services/notifications/routes/materialPerDay.ts
Normal file
37
lstV2/server/services/notifications/routes/materialPerDay.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
// an external way to creating logs
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import fifoIndexCheck from "../controller/notifications/fifoIndex.js";
|
||||
import materialPerDayCheck from "../controller/notifications/materialPerDay.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
|
||||
app.openapi(
|
||||
createRoute({
|
||||
tags: ["notify"],
|
||||
summary: "",
|
||||
method: "get",
|
||||
path: "/materialperday",
|
||||
//middleware: authMiddleware,
|
||||
responses: responses(),
|
||||
}),
|
||||
async (c) => {
|
||||
/**
|
||||
* get the blocking notification stuff
|
||||
*/
|
||||
apiHit(c, { endpoint: "/materialperday" });
|
||||
|
||||
/**
|
||||
* getting the shipped pallets
|
||||
*/
|
||||
const checkedData = await materialPerDayCheck();
|
||||
|
||||
return c.json({
|
||||
success: checkedData.success,
|
||||
message: checkedData.message,
|
||||
data: checkedData.data,
|
||||
});
|
||||
},
|
||||
);
|
||||
export default app;
|
||||
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* This will return the material demand per day
|
||||
* startdate and end date should be passed over
|
||||
*/
|
||||
export const materialPerDay = `
|
||||
use [test3_AlplaPROD2.0_Read]
|
||||
|
||||
DECLARE @ShiftStartHour INT = 6
|
||||
declare @startDate nvarchar(max) = '[startDate]'
|
||||
declare @endDate nvarchar(max) = '[endDate]'
|
||||
;with Calendar as (
|
||||
select cast(@startDate as date) CalDate
|
||||
union all
|
||||
select dateadd(day,1,CalDate)
|
||||
from Calendar
|
||||
where CalDate < @endDate
|
||||
),
|
||||
DailySplit AS (
|
||||
SELECT
|
||||
-- Lot fields
|
||||
l.Id AS ProductionLotId,
|
||||
l.ProductionLotHumanReadableId,
|
||||
l.ArticleHumanReadableId,
|
||||
l.ArticleDescription,
|
||||
l.LocationId,
|
||||
l.MachineHumanReadableId,
|
||||
l.MachineDescription,
|
||||
l.StartDate,
|
||||
l.FinishDate,
|
||||
l.ProductionCustomerDescription,
|
||||
l.ProductionCustomerHumanReadableId,
|
||||
l.PlannedQuantity,
|
||||
l.PlannedLoadingUnit,
|
||||
l.Cavity,
|
||||
l.Utilisation,
|
||||
l.TotalMaterialDemand AS LotTotalMaterialDemand,
|
||||
|
||||
-- Material fields
|
||||
m.MaterialHumanReadableId,
|
||||
m.MaterialDescription,
|
||||
m.TotalDemand AS LotMaterialTotalDemand,
|
||||
c.CalDate,
|
||||
|
||||
DATEDIFF(SECOND,l.StartDate,l.FinishDate) AS LotDurationSec,
|
||||
|
||||
-- build shift‑based 24‑hour window (e.g. 06:00 → next day 06:00)
|
||||
CASE
|
||||
WHEN l.StartDate > DATEADD(HOUR,@ShiftStartHour,CAST(c.CalDate AS DATETIME2(7)))
|
||||
THEN l.StartDate
|
||||
ELSE DATEADD(HOUR,@ShiftStartHour,CAST(c.CalDate AS DATETIME2(7)))
|
||||
END AS DayStart,
|
||||
|
||||
CASE
|
||||
WHEN l.FinishDate < DATEADD(SECOND,-0.0000001,
|
||||
DATEADD(HOUR,@ShiftStartHour,
|
||||
DATEADD(DAY,1,CAST(c.CalDate AS DATETIME2(7)))))
|
||||
THEN l.FinishDate
|
||||
ELSE DATEADD(SECOND,-0.0000001,
|
||||
DATEADD(HOUR,@ShiftStartHour,
|
||||
DATEADD(DAY,1,CAST(c.CalDate AS DATETIME2(7)))))
|
||||
END AS DayEnd
|
||||
FROM [issueMaterial].[ProductionLot] (nolock) AS l
|
||||
LEFT JOIN [issueMaterial].[MaterialDemand] (nolock) AS m
|
||||
ON m.ProductionLotId = l.Id
|
||||
CROSS JOIN Calendar AS c
|
||||
WHERE DATEADD(HOUR,@ShiftStartHour,CAST(c.CalDate AS DATETIME2))
|
||||
< l.FinishDate
|
||||
AND DATEADD(HOUR,@ShiftStartHour+24,CAST(c.CalDate AS DATETIME2))
|
||||
> l.StartDate
|
||||
--and l.[ProductionLotHumanReadableId] = 26364
|
||||
),
|
||||
Fraction AS (
|
||||
SELECT
|
||||
ds.*,
|
||||
DATEDIFF(SECOND,ds.DayStart,ds.DayEnd) AS OverlapSec
|
||||
FROM DailySplit ds
|
||||
),
|
||||
Normalized AS (
|
||||
SELECT
|
||||
f.*,
|
||||
f.OverlapSec * 1.0 /
|
||||
NULLIF(SUM(f.OverlapSec) OVER
|
||||
(PARTITION BY f.ProductionLotId, f.MaterialHumanReadableId),0)
|
||||
AS NormalizedFraction
|
||||
FROM Fraction f
|
||||
)
|
||||
SELECT
|
||||
n.ProductionLotId,
|
||||
n.ProductionLotHumanReadableId,
|
||||
n.ArticleHumanReadableId,
|
||||
n.ArticleDescription,
|
||||
n.LocationId,
|
||||
n.MachineHumanReadableId,
|
||||
n.MachineDescription,
|
||||
n.StartDate,
|
||||
n.FinishDate,
|
||||
n.ProductionCustomerDescription,
|
||||
n.ProductionCustomerHumanReadableId,
|
||||
n.PlannedQuantity,
|
||||
n.PlannedLoadingUnit,
|
||||
n.Cavity,
|
||||
n.Utilisation,
|
||||
n.LotTotalMaterialDemand,
|
||||
n.MaterialHumanReadableId,
|
||||
n.MaterialDescription,
|
||||
n.LotMaterialTotalDemand,
|
||||
n.CalDate,
|
||||
n.NormalizedFraction * n.LotMaterialTotalDemand AS DailyMaterialDemand
|
||||
FROM Normalized n
|
||||
ORDER BY
|
||||
n.MaterialHumanReadableId,
|
||||
n.CalDate,
|
||||
n.ProductionLotHumanReadableId
|
||||
OPTION (MAXRECURSION 0);
|
||||
`;
|
||||
Reference in New Issue
Block a user