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 {
date,
integer,
pgTable,
text,
timestamp,
uuid,
date,
integer,
pgTable,
text,
timestamp,
uuid,
} from "drizzle-orm/pg-core";
import { createSelectSchema } from "drizzle-zod";
export const invHistoricalData = pgTable(
"invHistoricalData",
{
inv_id: uuid("inv_id").defaultRandom().primaryKey(),
histDate: date("histDate").notNull(), // this date should always be yesterday when we post it.
plantToken: text("plantToken"),
article: text("article").notNull(),
articleDescription: text("articleDescription").notNull(),
materialType: text("materialType"),
total_QTY: text("total_QTY"),
avaliable_QTY: text("avaliable_QTY"),
coa_QTY: text("coa_QTY"),
held_QTY: text("held_QTY"),
lot_Number: text("lot_number"),
consignment: text("consignment"),
location: text("location"),
upd_user: text("upd_user").default("lst"),
upd_date: timestamp("upd_date").defaultNow(),
}
// (table) => [
// // uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
// uniqueIndex("role_name").on(table.name),
// ]
"invHistoricalData",
{
inv_id: uuid("inv_id").defaultRandom().primaryKey(),
histDate: date("histDate").notNull(), // this date should always be yesterday when we post it.
plantToken: text("plantToken"),
article: text("article").notNull(),
articleDescription: text("articleDescription").notNull(),
materialType: text("materialType"),
total_QTY: text("total_QTY"),
avaliable_QTY: text("avaliable_QTY"),
coa_QTY: text("coa_QTY"),
held_QTY: text("held_QTY"),
lot_Number: text("lot_number"),
consignment: text("consignment"),
location: text("location"),
whseId: text("whse_id").default(""),
whseName: text("whse_name").default("missing whseName"),
upd_user: text("upd_user").default("lst"),
upd_date: timestamp("upd_date").defaultNow(),
},
// (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

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 { invHistoricalData } from "../../../../database/schema/historicalINV.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
@@ -8,56 +8,79 @@ import { createLog } from "../../logger/logger.js";
// id: string
// }
export const psiGetInventory = async (
avs: string,
startDate: string,
endDate: string
avs: string,
startDate: string,
endDate: string,
whseToInclude: string,
exludeLanes: string
) => {
let articles: any = [];
let articles: any = [];
if (!avs) {
return {
success: false,
message: `Missing av's please send at least one over`,
data: [],
};
}
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 ids = avs.split(",").map((id) => id.trim());
const whse = whseToInclude
? whseToInclude
.split(",")
.map((w) => w.trim())
.filter(Boolean)
: [];
const { data, error } = (await tryCatch(
db
.select()
.from(invHistoricalData)
.where(
and(
inArray(invHistoricalData.article, ids),
between(invHistoricalData.histDate, startDate, endDate)
)
)
//.limit(100)
)) as any;
const locations = exludeLanes
? exludeLanes.split(",").map((l) => l.trim()).filter(Boolean)
: [];
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,
};
}
const conditions = [
inArray(invHistoricalData.article, ids),
between(invHistoricalData.histDate, startDate, endDate),
];
articles = data;
console.log(articles.length);
return {
success: true,
message: "PSI planning Data",
data: articles,
};
// only add the warehouse condition if there are any whse values
if (whse.length > 0) {
console.log("adding whse to include in");
conditions.push(inArray(invHistoricalData.whseId, whse));
}
// 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 getOpenOrders from "./route/getOpenOrders.js";
import psiArticleData from "./route/getPsiArticleData.js";
import psiForecastData from "./route/getPsiForecast.js";
import psiInventory from "./route/getPsiinventory.js";
import psiPlanningData from "./route/getPsiPlanningData.js";
import psiProductionData from "./route/getPsiProductionData.js";
@@ -37,6 +38,7 @@ const routes = [
psiPlanningData,
psiProductionData,
psiInventory,
psiForecastData,
] as const;
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 { apiHit } from "../../../globalUtils/apiHits.js";
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" }),
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();
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
)
);
// 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,
q["whseToInclude"] ? q["whseToInclude"][0] : null,
q["exludeLanes"] ? q["exludeLanes"][0] : null,
),
);
if (error) {
console.log(error);
return c.json(
{
success: false,
message: "There was an error getting the production.",
data: error,
},
400
);
}
if (error) {
console.log(error);
return c.json(
{
success: false,
message: "There was an error getting the production.",
data: error,
},
400,
);
}
//console.log(data);
//console.log(data);
return c.json(
{
success: data.success,
message: data.message,
data: data.data,
},
data.success ? 200 : 400
);
}
return c.json(
{
success: data.success,
message: data.message,
data: data.data,
},
data.success ? 200 : 400,
);
},
);
export default app;

View File

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

View File

@@ -76,7 +76,10 @@ export const historicalInvIMmport = async () => {
coa_QTY: i.COA_QTY,
held_QTY: i.Held_QTY,
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
----,IdAdressen,
,x.AdressBez
,x.IdLagerAbteilung as 'location'
,x.LagerAbteilungKurzBez
,x.IdWarenlager as warehouseID
,x.WarenLagerKurzBez as warehouseName
--,*
from [AlplaPROD_test1].dbo.[V_LagerPositionenBarcodes] (nolock) x
@@ -39,6 +43,10 @@ group by x.idartikelVarianten, ArtikelVariantenAlias, c.Description
--,IdAdressen
,x.AdressBez
,IdProdPlanung
,x.IdLagerAbteilung
,x.LagerAbteilungKurzBez
,x.IdWarenlager
,x.WarenLagerKurzBez
--, x.Lfdnr
order by x.IdArtikelVarianten