refactor(datamart): psi work

This commit is contained in:
2025-12-30 07:12:38 -06:00
parent f3333ce020
commit 096cc18477
9 changed files with 315 additions and 162 deletions

View File

@@ -1,36 +1,38 @@
import { import {
date, date,
integer, integer,
pgTable, pgTable,
text, text,
timestamp, timestamp,
uuid, uuid,
} from "drizzle-orm/pg-core"; } from "drizzle-orm/pg-core";
import { createSelectSchema } from "drizzle-zod"; import { createSelectSchema } from "drizzle-zod";
export const invHistoricalData = pgTable( export const invHistoricalData = pgTable(
"invHistoricalData", "invHistoricalData",
{ {
inv_id: uuid("inv_id").defaultRandom().primaryKey(), inv_id: uuid("inv_id").defaultRandom().primaryKey(),
histDate: date("histDate").notNull(), // this date should always be yesterday when we post it. histDate: date("histDate").notNull(), // this date should always be yesterday when we post it.
plantToken: text("plantToken"), plantToken: text("plantToken"),
article: text("article").notNull(), article: text("article").notNull(),
articleDescription: text("articleDescription").notNull(), articleDescription: text("articleDescription").notNull(),
materialType: text("materialType"), materialType: text("materialType"),
total_QTY: text("total_QTY"), total_QTY: text("total_QTY"),
avaliable_QTY: text("avaliable_QTY"), avaliable_QTY: text("avaliable_QTY"),
coa_QTY: text("coa_QTY"), coa_QTY: text("coa_QTY"),
held_QTY: text("held_QTY"), held_QTY: text("held_QTY"),
lot_Number: text("lot_number"), lot_Number: text("lot_number"),
consignment: text("consignment"), consignment: text("consignment"),
location: text("location"), location: text("location"),
upd_user: text("upd_user").default("lst"), whseId: text("whse_id").default(""),
upd_date: timestamp("upd_date").defaultNow(), whseName: text("whse_name").default("missing whseName"),
} upd_user: text("upd_user").default("lst"),
// (table) => [ upd_date: timestamp("upd_date").defaultNow(),
// // uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`), },
// uniqueIndex("role_name").on(table.name), // (table) => [
// ] // // uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
// uniqueIndex("role_name").on(table.name),
// ]
); );
// Schema for inserting a user - can be used to validate API requests // Schema for inserting a user - can be used to validate API requests

View File

@@ -0,0 +1,42 @@
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { createLog } from "../../logger/logger.js";
import { query } from "../../sqlServer/prodSqlServer.js";
import { forecastData } from "../../sqlServer/querys/psiReport/forecast.js";
// type ArticleData = {
// id: string
// }
export const getGetPSIForecastData = async (customer: string) => {
let articles: any = [];
let queryData = forecastData;
console.log(customer);
if (customer) {
queryData = forecastData.replace("[customer]", customer);
}
const { data, error } = (await tryCatch(
query(queryData, "PSI forecast info"),
)) as any;
if (error) {
createLog(
"error",
"datamart",
"datamart",
`There was an error getting the forecast info: ${JSON.stringify(error)}`,
);
return {
success: false,
messsage: `There was an error getting the forecast info`,
data: error,
};
}
articles = data.data;
return {
success: true,
message: "PSI forecast Data",
data: articles,
};
};

View File

@@ -1,4 +1,4 @@
import { and, between, inArray, sql } from "drizzle-orm"; import { and, between, inArray, notInArray, sql } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js"; import { db } from "../../../../database/dbclient.js";
import { invHistoricalData } from "../../../../database/schema/historicalINV.js"; import { invHistoricalData } from "../../../../database/schema/historicalINV.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js"; import { tryCatch } from "../../../globalUtils/tryCatch.js";
@@ -8,56 +8,79 @@ import { createLog } from "../../logger/logger.js";
// id: string // id: string
// } // }
export const psiGetInventory = async ( export const psiGetInventory = async (
avs: string, avs: string,
startDate: string, startDate: string,
endDate: string endDate: string,
whseToInclude: string,
exludeLanes: string
) => { ) => {
let articles: any = []; let articles: any = [];
if (!avs) { if (!avs) {
return { return {
success: false, success: false,
message: `Missing av's please send at least one over`, message: `Missing av's please send at least one over`,
data: [], data: [],
}; };
} }
const ids = avs.split(",").map((id) => id.trim()); const ids = avs.split(",").map((id) => id.trim());
const whse = whseToInclude
? whseToInclude
.split(",")
.map((w) => w.trim())
.filter(Boolean)
: [];
const { data, error } = (await tryCatch( const locations = exludeLanes
db ? exludeLanes.split(",").map((l) => l.trim()).filter(Boolean)
.select() : [];
.from(invHistoricalData)
.where(
and(
inArray(invHistoricalData.article, ids),
between(invHistoricalData.histDate, startDate, endDate)
)
)
//.limit(100)
)) as any;
if (error) { const conditions = [
createLog( inArray(invHistoricalData.article, ids),
"error", between(invHistoricalData.histDate, startDate, endDate),
"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; // only add the warehouse condition if there are any whse values
console.log(articles.length); if (whse.length > 0) {
return { console.log("adding whse to include in");
success: true, conditions.push(inArray(invHistoricalData.whseId, whse));
message: "PSI planning Data", }
data: articles,
}; // locations we dont want in the system
if (locations.length > 0) {
console.log("adding excluded lanes in ",locations);
conditions.push(notInArray(invHistoricalData.location, locations));
}
const query = db
.select()
.from(invHistoricalData)
.where(and(...conditions));
// optional tryCatch or await as you had
const { data, error } = (await tryCatch(query)) 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

@@ -13,6 +13,7 @@ import getInhouseDeliveryByDate from "./route/getInHouseDeliveryDateByRange.js";
import currentInv from "./route/getInventory.js"; import currentInv from "./route/getInventory.js";
import getOpenOrders from "./route/getOpenOrders.js"; import getOpenOrders from "./route/getOpenOrders.js";
import psiArticleData from "./route/getPsiArticleData.js"; import psiArticleData from "./route/getPsiArticleData.js";
import psiForecastData from "./route/getPsiForecast.js";
import psiInventory from "./route/getPsiinventory.js"; import psiInventory from "./route/getPsiinventory.js";
import psiPlanningData from "./route/getPsiPlanningData.js"; import psiPlanningData from "./route/getPsiPlanningData.js";
import psiProductionData from "./route/getPsiProductionData.js"; import psiProductionData from "./route/getPsiProductionData.js";
@@ -37,6 +38,7 @@ const routes = [
psiPlanningData, psiPlanningData,
psiProductionData, psiProductionData,
psiInventory, psiInventory,
psiForecastData,
] as const; ] as const;
const appRoutes = routes.forEach((route) => { const appRoutes = routes.forEach((route) => {

View File

@@ -0,0 +1,65 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { apiHit } from "../../../globalUtils/apiHits.js";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { getGetPSIForecastData } from "../controller/psiForecastData.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 psiforecastdata.",
method: "get",
path: "/psiforecastdata",
request: {
body: {
content: {
"application/json": { schema: Body },
},
},
},
responses: responses(),
}),
async (c) => {
const customer: any = c.req.queries();
// make sure we have a vaid user being accessed thats really logged in
apiHit(c, { endpoint: "/psiforecastdata" });
//console.log(articles["avs"][0]);
let customeArticle = null;
if (customer) {
customeArticle = customer["customer"][0];
}
const { data, error } = await tryCatch(
getGetPSIForecastData(customeArticle),
);
if (error) {
console.log(error);
return c.json(
{
success: false,
message: "There was an error getting the articles.",
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

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

View File

@@ -2,65 +2,71 @@ import { OpenAPIHono } from "@hono/zod-openapi";
const app = new OpenAPIHono(); const app = new OpenAPIHono();
import stats from "./route/stats.js";
import history from "./route/invHistory.js";
import { createJob } from "../notifications/utils/processNotifications.js";
import { historicalInvIMmport } from "./utils/historicalInv.js";
import { tryCatch } from "../../globalUtils/tryCatch.js"; import { tryCatch } from "../../globalUtils/tryCatch.js";
import { createLog } from "../logger/logger.js";
import { createJob } from "../notifications/utils/processNotifications.js";
import { query } from "../sqlServer/prodSqlServer.js"; import { query } from "../sqlServer/prodSqlServer.js";
import { shiftChange } from "../sqlServer/querys/misc/shiftChange.js"; import { shiftChange } from "../sqlServer/querys/misc/shiftChange.js";
import { createLog } from "../logger/logger.js"; import gpData from "./route/getGpData.js";
import lastPurch from "./route/getLastPurchPrice.js"; import lastPurch from "./route/getLastPurchPrice.js";
import lastSales from "./route/getLastSalesPrice.js"; import lastSales from "./route/getLastSalesPrice.js";
import gpData from "./route/getGpData.js";
import consumptionData from "./route/getProductionConsumption.js"; import consumptionData from "./route/getProductionConsumption.js";
import purchased from "./route/getPurchased.js";
import regrind from "./route/getregrind.js"; import regrind from "./route/getregrind.js";
import soldItems from "./route/getSoldItems.js"; import soldItems from "./route/getSoldItems.js";
import purchased from "./route/getPurchased.js"; import history from "./route/invHistory.js";
import stats from "./route/stats.js";
import { historicalInvIMmport } from "./utils/historicalInv.js";
const routes = [ const routes = [
stats, stats,
history, history,
lastPurch, lastPurch,
lastSales, lastSales,
gpData, gpData,
consumptionData, consumptionData,
regrind, regrind,
soldItems, soldItems,
purchased, purchased,
] as const; ] as const;
const appRoutes = routes.forEach((route) => { const appRoutes = routes.forEach((route) => {
app.route("/eom", route); app.route("/eom", route);
}); });
setTimeout(async () => { setTimeout(async () => {
const { data: shift, error: shiftError } = (await tryCatch( const { data: shift, error: shiftError } = (await tryCatch(
query(shiftChange, "shift change from material.") query(shiftChange, "shift change from material."),
)) as any; )) as any;
if (shiftError) { if (shiftError) {
createLog( createLog(
"error", "error",
"eom", "eom",
"eom", "eom",
"There was an error getting the shift times will use fallback times" "There was an error getting the shift times will use fallback times",
); );
} }
// shift split // shift split
const shiftTimeSplit = shift?.data[0]?.shiftChange.split(":"); const shiftTimeSplit = shift?.data[0]?.shiftChange.split(":");
const cronSetup = `${ const cronSetup = `${
shiftTimeSplit?.length > 0 ? `${parseInt(shiftTimeSplit[1])}` : "0" shiftTimeSplit?.length > 0 ? `${parseInt(shiftTimeSplit[1])}` : "0"
} ${ } ${
shiftTimeSplit?.length > 0 ? `${parseInt(shiftTimeSplit[0])}` : "7" shiftTimeSplit?.length > 0 ? `${parseInt(shiftTimeSplit[0])}` : "7"
} * * *`; } * * *`;
//console.log(cronSetup); //console.log(cronSetup);
createJob("eom_historical_inv", cronSetup, historicalInvIMmport); createJob("eom_historical_inv", cronSetup, historicalInvIMmport);
}, 5 * 1000); }, 5 * 1000);
// the time we want to run the hostircal data should be the same time the historical data run on the server // the time we want to run the hostircal data should be the same time the historical data run on the server
// getting this from the shift time // getting this from the shift time
if (process.env.NODE_ENV?.trim() !== "production") {
setTimeout(() => {
historicalInvIMmport();
}, 15 * 1000);
}
export default app; export default app;

View File

@@ -76,7 +76,10 @@ export const historicalInvIMmport = async () => {
coa_QTY: i.COA_QTY, coa_QTY: i.COA_QTY,
held_QTY: i.Held_QTY, held_QTY: i.Held_QTY,
consignment: i.Consigment, consignment: i.Consigment,
lot_Number: i.lot, lot_Number: i.Lot,
location: i.location,
whseId: i.warehouseID,
whseName: i.warehouseName,
}; };
}); });

View File

@@ -17,6 +17,10 @@ x.ArtikelVariantenAlias as Alias
,IdProdPlanung as Lot ,IdProdPlanung as Lot
----,IdAdressen, ----,IdAdressen,
,x.AdressBez ,x.AdressBez
,x.IdLagerAbteilung as 'location'
,x.LagerAbteilungKurzBez
,x.IdWarenlager as warehouseID
,x.WarenLagerKurzBez as warehouseName
--,* --,*
from [AlplaPROD_test1].dbo.[V_LagerPositionenBarcodes] (nolock) x from [AlplaPROD_test1].dbo.[V_LagerPositionenBarcodes] (nolock) x
@@ -39,6 +43,10 @@ group by x.idartikelVarianten, ArtikelVariantenAlias, c.Description
--,IdAdressen --,IdAdressen
,x.AdressBez ,x.AdressBez
,IdProdPlanung ,IdProdPlanung
,x.IdLagerAbteilung
,x.LagerAbteilungKurzBez
,x.IdWarenlager
,x.WarenLagerKurzBez
--, x.Lfdnr --, x.Lfdnr
order by x.IdArtikelVarianten order by x.IdArtikelVarianten