feat(lstv2 move): moved lstv2 into this app to keep them combined and easier to maintain

This commit is contained in:
2025-09-19 22:22:05 -05:00
parent caf2315191
commit e4477402ad
847 changed files with 165801 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
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 { bow2incoming } from "../../../sqlServer/querys/notifications/bow2henkel.js";
const notification = async (notifyData: any) => {
/**
* Pass the entire notification over
*/
createLog("debug", "reprinting", "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;
}
//let labels: Labels[];
const { data: l, error: labelError } = await tryCatch(
query(
bow2incoming.replace(
"[time]",
notifyData.notifiySettings.processTime
),
"Label Reprints"
)
);
const labels: any = l?.data as any;
if (labelError) {
createLog(
"error",
"reprinting",
"notify",
`Failed to get the labels: ${labelError}`
);
return;
}
if (labels.length > 0) {
//send the email :D
const emailSetup = {
email: notifyData.emails,
subject: "Alert! New incoming goods has been received",
template: "bow2IncomingGoods",
context: {
items: labels,
time: notifyData.notifiySettings.processTime,
},
};
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()`,
notifiySettings: {
...notifyData.notifiySettings,
prodID: labels[0].IdEtikettenHistorie,
},
})
.where(eq(notifications.name, notifyData.name))
);
} else {
return;
}
};
export default notification;

View File

@@ -0,0 +1,95 @@
// SELECT count(*) FROM V_EtikettenGedruckt where AnzahlGedruckterKopien > 2 and CONVERT(varchar(5), Add_Date,108) not like CONVERT(varchar(5), Upd_Date,108) and Upd_Date > DATEADD(SECOND, -30,getdate()) and VpkVorschriftBez not like '%$%'
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 { sendEmail } from "../sendMail.js";
import { query } from "../../../sqlServer/prodSqlServer.js";
import { downTimeCheck } from "../../../sqlServer/querys/notifications/downtimecheck.js";
export interface DownTime {
downTimeId?: number;
machineAlias?: string;
}
export default async function reprintLabelMonitor(notifyData: any) {
// we will over ride this with users that want to sub to this
// a new table will be called subalerts and link to the do a kinda linkn where the user wants it then it dose subId: 1, userID: x, notificationId: y. then in here we look up the userid to get the email :D
// this could then leave the emails in the notificaion blank and let users sub to it.
if (notifyData.emails === "") {
createLog(
"error",
"notify",
"notify",
`There are no emails set for ${notifyData.name}`
);
return;
}
// console.log(data.secondarySetting[0].duration);
let dQuery = downTimeCheck
.replace("[dtDuration]", notifyData.notifiySettings?.duration)
.replace("[daysInPast]", notifyData.notifiySettings?.daysInPast);
//console.log(query);
let downTime: any = []; //DownTime[];
try {
const res: any = await query(dQuery, "downTimeCheck");
//console.log(labels.length);
downTime = res.data;
if (
downTime.length > 0
// && downTime[0]?.downTimeId > notifyData.notifiySettings.prodID
) {
//send the email :D
const emailSetup = {
email: notifyData.emails,
subject: `Alert! Downtime recorded greater than ${
notifyData.notifiySettings?.duration
}min ${
downTime.length === 1
? `on ${downTime[0].machineAlias}`
: ""
}`,
template: "downTimeCheck",
context: {
items: downTime,
secondarySetting: notifyData.notifiySettings,
},
};
const sentEmail = await sendEmail(emailSetup);
if (!sentEmail.success) {
createLog(
"error",
"notify",
"notify",
"Failed to send email, will try again on next interval"
);
return;
}
const { data, error } = await tryCatch(
db
.update(notifications)
.set({
lastRan: sql`NOW()`,
notifiySettings: {
...notifyData.notifiySettings,
prodID: downTime[0].downTimeId,
},
})
.where(eq(notifications.name, notifyData.name))
);
}
} catch (err) {
createLog(
"error",
"notify",
"notify",
`Error from running the downtimeCheck query: ${err}`
);
}
}

View File

@@ -0,0 +1,27 @@
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";
const notification = async (notifyData: any) => {
/**
* Pass the entire notification over
*/
createLog("info", "notify", "notify", `monitoring ${notifyData.name}`);
const { data, error } = await tryCatch(
db
.update(notifications)
.set({
lastRan: sql`NOW()`,
// notifiySettings: {
// ...updateSettings,
// prodID: labels[0].IdEtikettenHistorie,
// },
})
.where(eq(notifications.name, notifyData.name))
);
};
export default notification;

View File

@@ -0,0 +1,112 @@
import { isBefore } from "date-fns";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { query } from "../../../sqlServer/prodSqlServer.js";
import { currentInv } from "../../../sqlServer/querys/notifications/fifoIndex/currentInv.js";
import { shippedPallets } from "../../../sqlServer/querys/notifications/fifoIndex/shippedPallets.js";
import { db } from "../../../../../database/dbclient.js";
import { fifoIndex } from "../../../../../database/schema/fifoIndex.js";
export default async function fifoIndexCheck() {
/**
* getting the shipped pallets
*/
const { data: shipped, error: eShipped } = await tryCatch(
query(shippedPallets, "notify shipped pallets")
);
const { data: currentStuff, error: eCurrentInv } = await tryCatch(
query(currentInv, "notify shipped pallets")
);
// console.log(shipped?.data[2]);
// console.log(currentStuff?.data[2]);
/**
* We want to check if the each shippened pallet is out of fifo
*/
const check = shipped?.data.map((n: any) => {
/**
* Returns all data so we know if we are in or out.
*/
//check if there are pallets older than the current one we are mapped on.
const fifoCheck = currentStuff?.data.filter(
(i: any) => isBefore(i.prodDate, n.prodDate) && i.av === n.av
);
//console.log(fifoCheck.length);
if (fifoCheck.length > 0) {
// console.log("Out of fifo", {
// av: n.av,
// rn: n.runningNr,
// fRn: fifoCheck[0].runningNr,
// dates: [fifoCheck[0].prodDate, n.prodDate],
// });
}
return {
...n,
// currentInv: fifoCheck[0],
fifoFollowed: fifoCheck.length === 0 ? true : false,
};
});
/**
* lets see just the av that is our or in
*/
const avCheck = (check: any) => {
/**
* This will only return the data based on out of fifo.
*/
// check how many times each av showed up
const avCounts = check.reduce((a: any, c: any) => {
if (c.fifoFollowed === false) {
const avValue = c.av;
a[avValue] = (a[avValue] || 0) + 1;
}
return a;
}, {});
// transform them back to an avCount Object
const result = Object.keys(avCounts).map((av) => ({
av: parseInt(av, 10),
count: avCounts[av],
}));
return result;
};
const outOfFifo: any = avCheck(check);
const totalOut = outOfFifo.reduce((sum: any, c: any) => {
return sum + c.count;
}, 0);
/**
* add the data to the db
*/
for (let i = 0; i < check.length; i++) {
const { data: dbInsert, error: dbE } = await tryCatch(
db
.insert(fifoIndex)
.values({
lot: check[i].lot,
av: check[i].av,
runningNr: check[i].runningNr,
prodDate: check[i].prodDate,
fifoFollowed: check[i].fifoFollowed,
add_Date: check[i].add_Date,
})
.onConflictDoNothing()
);
}
return {
success: true,
message: "Fifo index data",
data: {
palletsOut: check,
totalShipped: shipped?.data.length,
inFifo: shipped?.data.length - totalOut,
outOfFifoData: outOfFifo,
},
};
}

View File

@@ -0,0 +1,107 @@
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 { palletsRemovedAswaste } from "../../../sqlServer/querys/notifications/palletsRemovedAsWaste.js";
import { format } from "date-fns-tz";
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;
}
const { data: l, error: palletError } = await tryCatch(
query(palletsRemovedAswaste, "Removed as waste check")
);
const pallets: any = l?.data as any;
if (palletError) {
createLog(
"error",
"reprinting",
"notify",
`Failed to get the labels: ${palletError}`
);
return;
}
console.log(pallets);
if (pallets.length > 0) {
//send the email :D
const emailSetup = {
email: notifyData.emails,
subject: `Alert! ${
pallets.length > 1 ? "Some pallets were" : "A pallet was "
} brought back in`,
template: "palletBookedAsWaste",
context: {
items: pallets.map((i: any) => {
return {
...i,
lastMovingDate: format(i.lastMovingDate, "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()`,
notifiySettings: {
...notifyData.notifiySettings,
prodID: pallets[0].runningnumber,
},
})
.where(eq(notifications.name, notifyData.name))
);
} else {
return;
}
};
export default notification;

View File

@@ -0,0 +1,137 @@
// SELECT count(*) FROM V_EtikettenGedruckt where AnzahlGedruckterKopien > 2 and CONVERT(varchar(5), Add_Date,108) not like CONVERT(varchar(5), Upd_Date,108) and Upd_Date > DATEADD(SECOND, -30,getdate()) and VpkVorschriftBez not like '%$%'
import { isWeekend } from "date-fns";
import { createLog } from "../../../logger/logger.js";
import { query } from "../../../sqlServer/prodSqlServer.js";
import { sendEmail } from "../sendMail.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { db } from "../../../../../database/dbclient.js";
import { notifications } from "../../../../../database/schema/notifications.js";
import { eq, sql } from "drizzle-orm";
export interface PPOO {
IdPosition?: number;
}
export default async function reprintLabelMonitor(notifyData: any) {
createLog("info", "notify", "notify", `monitoring ${notifyData.name}`);
if (notifyData.emails === "") {
createLog(
"error",
"notify",
"notify",
`There are no emails set for ${notifyData.name}`
);
return;
}
// parse the secondarySetting back to json to use it.
// notifyData = { ...notifyData, secondarySetting: JSON.parse(notifyData.secondarySetting) };
// as this one goes to managers we want to not send on the weekends
const weekend = isWeekend(new Date(Date.now()));
if (weekend && notifyData.notifiySettings.weekend) {
createLog(
"info",
"notify",
"notify",
`${notifyData.name} will not run on the weekends`
);
return;
}
let notifyQuery = `
SELECT
--[EinlagerungsDatummin] as lastMovingDate,
round(VerfuegbareMengeVPKSum,2) as pallets
,VerfuegbareMengeSum as total
,round([GesperrteMengeVpkSum],2) as held
,round([GesperrteMengeSum],2) as heldQty
,[IdArtikelVarianten] as av
,[IdProdBereich] as pfcID
,[ArtikelVariantenBez] as articleDescription
,[ArtikelVariantenAlias] as articleDescriptionAlias
,[LagerAbteilungKurzBez] as location
,[Lfdnr] as runningNumber
,[Produktionslos] as lot
,[ProduktionsDatumMin] as productionDate
,IdPosition
FROM [AlplaPROD_test1].[dbo].[V_LagerPositionenBarcodes] (nolock)
where idlagerabteilung in ([locations]) and [ProduktionsDatumMin] < DATEadd( Hour, -[timeCheck], getdate())
order by [ProduktionsDatumMin] asc
`;
//update the time check
notifyQuery = notifyQuery.replaceAll(
"[timeCheck]",
notifyData.checkInterval
);
notifyQuery = notifyQuery.replaceAll(
"[locations]",
notifyData.notifiySettings.locations
);
let prod: PPOO[];
try {
const res: any = await query(notifyQuery, "Label Reprints");
prod = res.data;
//console.log(labels.length);
// const now = Date.now()
if (prod.length > 0) {
//send the email :D
// update the count with the result
const emailSetup = {
email: notifyData.emails,
subject: `Alert! Pallets in production greater than ${notifyData.checkTime} ${notifyData.timeType}`,
template: "productionCheck",
context: {
items: prod,
count: prod.length,
checkTime: notifyData.checkInterval,
timeCheck: notifyData.timeType,
},
};
const sentEmail = await sendEmail(emailSetup);
if (!sentEmail.success) {
createLog(
"error",
"notify",
"notify",
"Failed to send email, will try again on next interval"
);
return;
}
let updateSettings = notifyData.notifiySettings;
const { data, error } = await tryCatch(
db
.update(notifications)
.set({
lastRan: sql`NOW()`,
notifiySettings: {
...updateSettings,
count: prod.length,
prodID: prod[0].IdPosition,
},
})
.where(eq(notifications.name, notifyData.name))
);
} else {
return;
}
} catch (err) {
createLog(
"error",
"sql",
"error",
`Error from running the Label Reprints query: ${err}`
);
}
}

View File

@@ -0,0 +1,144 @@
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 { blockQuery } from "../../../sqlServer/querys/notifications/blocking.js";
export default async function qualityBlockingMonitor(notifyData: any) {
createLog("info", "blocking", "notify", `monitoring ${notifyData.name}`);
if (notifyData.emails === "") {
createLog(
"error",
"blocking",
"notify",
`There are no emails set for ${notifyData.name}`
);
return {
success: false,
message: `There are no emails set for ${notifyData.name}`,
};
}
const { data: noti, error: notiError } = await tryCatch(
db
.select()
.from(notifications)
.where(eq(notifications.name, notifyData.name))
);
if (notiError) {
throw Error(`${JSON.stringify(notiError)}`);
}
const notiData: any = noti;
const blockingOrders = notiData[0]?.notifiySettings.sentBlockingOrders.map(
(l: any) => {
return l.blockingOrder;
}
);
// console.log(blockingOrders);
// let blockingQuery = blockQuery.replaceAll(
// "[sentBlockingOrders]",
// blockingOrders
// );
let blockingQuery = blockQuery.replaceAll(
"[lastBlocking]",
notiData[0]?.notifiySettings.prodID
);
const { data: b, error: blockingError } = await tryCatch(
query(blockingQuery, "Quality Blocking")
);
const blocking: any = b?.data as any;
if (blockingError) {
return {
success: false,
message: "Error getting blocking orders",
data: blockingError,
};
}
if (blocking.length > 0) {
const emailSetup = {
email: notifyData.emails,
subject:
blocking.length > 0
? `Alert! New blocking orders.`
: blocking[0].subject,
template: "qualityBlocking",
context: {
items: blocking,
},
};
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",
};
}
const newBlockingOrders = blocking.map((b: any) => {
return {
blockingOrder: b.HumanReadableId,
timeStamp: new Date(Date.now()),
};
});
const uniqueOrders = Array.from(
new Set([
...notifyData.notifiySettings.sentBlockingOrders,
...newBlockingOrders,
])
);
console.log(uniqueOrders);
const { data, error } = await tryCatch(
db
.update(notifications)
.set({
lastRan: sql`NOW()`,
notifiySettings: {
...notifyData.notifiySettings,
prodID: blocking[0].HumanReadableId,
sentBlockingOrders: uniqueOrders,
},
})
.where(eq(notifications.name, notifyData.name))
);
if (error) {
createLog(
"error",
"blocking",
"notify",
"Error updating the blocking orders"
);
return {
success: false,
message: "Error updating the blocking orders",
data: error,
};
}
}
return {
success: true,
message: "Blocking query ran successfully",
blocking,
};
}

View File

@@ -0,0 +1,129 @@
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";
export interface Labels {
IdEtikettenHistorie?: number;
}
const notification = async (notifyData: any) => {
/**
* Pass the entire notification over
*/
createLog("debug", "reprinting", "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;
}
// well set a backup default time here
let timeCheck = `DATEADD(SECOND, -30, getdate()) `;
// set the time of getting the label
if (notifyData.timeType === "sec") {
timeCheck = `DATEADD(SECOND, -${notifyData.checkInterval}, getdate()) `;
} else if (notifyData.timeType === "min") {
timeCheck = `DATEADD(MINUTE, -${notifyData.checkInterval}, getdate()) `;
}
let reprintQuery = `
SELECT
IdEtikettenHistorie,
IdArtikelvarianten as av,
ArtikelVariantenBez as alias,
LfdNr as runningNumber,
CONVERT(VARCHAR, CAST(Add_Date AS DATETIME), 100) Add_Date,
Add_User,
CONVERT(VARCHAR, CAST(Upd_Date AS DATETIME), 100) Upd_Date,
Upd_User,
EtikettenDruckerBezeichnung as printer,
AnzahlGedruckterKopien as totalPrinted
FROM Alplaprod_test1.dbo.V_EtikettenGedruckt (nolock)
where AnzahlGedruckterKopien > 2
and CONVERT(varchar(5), Add_Date,108) not like CONVERT(varchar(5), Upd_Date,108)
and Upd_Date > DATEADD(SECOND, -30, getdate())
and VpkVorschriftBez not like '%$%'
`;
//update the time check
reprintQuery = reprintQuery.replaceAll(
"DATEADD(SECOND, -30, getdate()) ",
timeCheck
);
//let labels: Labels[];
const { data: l, error: labelError } = await tryCatch(
query(reprintQuery, "Label Reprints")
);
const labels: any = l?.data as any;
if (labelError) {
createLog(
"error",
"reprinting",
"notify",
`Failed to get the labels: ${labelError}`
);
return;
}
if (labels.length > 0) {
//send the email :D
const emailSetup = {
email: notifyData.emails,
subject: "Alert! Label Reprinted",
template: "reprintLabels",
context: {
items: labels,
},
};
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()`,
notifiySettings: {
...notifyData.notifiySettings,
prodID: labels[0].IdEtikettenHistorie,
},
})
.where(eq(notifications.name, notifyData.name))
);
} else {
return;
}
};
export default notification;

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

@@ -0,0 +1,130 @@
// SELECT count(*) FROM V_EtikettenGedruckt where AnzahlGedruckterKopien > 2 and CONVERT(varchar(5), Add_Date,108) not like CONVERT(varchar(5), Upd_Date,108) and Upd_Date > DATEADD(SECOND, -30,getdate()) and VpkVorschriftBez not like '%$%'
import { isWeekend } from "date-fns";
import { createLog } from "../../../logger/logger.js";
import { sendEmail } from "../sendMail.js";
import { query } from "../../../sqlServer/prodSqlServer.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { notifications } from "../../../../../database/schema/notifications.js";
import { eq, sql } from "drizzle-orm";
import { db } from "../../../../../database/dbclient.js";
export interface PPOO {
IdPosition?: number;
}
export default async function reprintLabelMonitor(notifyData: any) {
createLog("info", "notify", "notify", `monitoring ${notifyData.name}`);
if (notifyData.emails === "") {
createLog(
"error",
"notify",
"notify",
`There are no emails set for ${notifyData.notificationName}`
);
return;
}
// as this one goes to managers we want to not send on the weekends
const weekend = isWeekend(new Date(Date.now()));
if (weekend && notifyData.notifiySettings.weekend) {
createLog(
"info",
"notify",
"notify",
`${notifyData.name} will not run on the weekends`
);
return;
}
let noteQuery = `
SELECT
--[EinlagerungsDatummin] as lastMovingDate,
round(VerfuegbareMengeVPKSum,2) as pallets
,VerfuegbareMengeSum as total
,round([GesperrteMengeVpkSum],2) as held
,round([GesperrteMengeSum],2) as heldQty
,[IdArtikelVarianten] as av
,[IdProdBereich] as pfcID
,[ArtikelVariantenBez] as articleDescription
,[ArtikelVariantenAlias] as articleDescriptionAlias
,[LagerAbteilungKurzBez] as location
,[Lfdnr] as runningNumber
,[Produktionslos] as lot
,[ProduktionsDatumMin] as productionDate
,IdPosition
FROM [AlplaPROD_test1].[dbo].[V_LagerPositionenBarcodes] (nolock)
where idlagerabteilung in ([locations]) and [ProduktionsDatumMin] < DATEadd( Hour, -[timeCheck], getdate())
order by [ProduktionsDatumMin] asc
`;
//update the time check
noteQuery = noteQuery
.replaceAll("[timeCheck]", notifyData.checkInterval)
.replaceAll("[locations]", notifyData.notifiySettings.locations);
let stage: PPOO[];
try {
const res = await query(noteQuery, "Staging checks");
stage = res?.data as any;
//console.log(labels.length);
// const now = Date.now()
if (stage.length > 0) {
//send the email :D
// update the count with the result
const emailSetup = {
email: notifyData.emails,
subject: `Alert! Pallets in staging greater than ${notifyData.checkInterval} ${notifyData.timeType}`,
template: "stagingCheck",
context: {
items: stage,
count: stage.length,
checkTime: notifyData.checkInterval,
timeCheck: notifyData.timeType,
},
};
const sentEmail = await sendEmail(emailSetup);
if (!sentEmail.success) {
createLog(
"error",
"notify",
"notify",
"Failed to send email, will try again on next interval"
);
return;
}
// update the last time we ran and the prod id
let updateSettings = notifyData.notifiySettings;
const { data, error } = await tryCatch(
db
.update(notifications)
.set({
lastRan: sql`NOW()`,
notifiySettings: {
...updateSettings,
count: stage.length,
},
})
.where(eq(notifications.name, notifyData.name))
);
} else {
return;
}
} catch (err) {
createLog(
"error",
"notify",
"notify",
`Error from running the Label Reprints query: ${err}`
);
}
}

View File

@@ -0,0 +1,20 @@
export const dateCorrection = async (newDate: Date) => {
/**
* corrects the date format to be what ti is expecting
*/
const newDateFormat = new Date(newDate)
.toLocaleString("en-US", {
timeZone: "UTC",
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hourCycle: "h23", // Ensures 24-hour format
})
.replace(",", "");
return newDateFormat;
};

View File

@@ -0,0 +1,23 @@
const requestUser = process.env.REQUESTUSER || "";
export const headerUpdate = async (data: any, plantToken: any) => {
// update the header
let webHeader = `
<request-id>[requestID]</request-id>
<data>
<WebImport>
<WebImportHeader>
<FileName>[requestID].XML</FileName>
<Type>SOTransportLoader</Type>
<UserName>[requestUser]</UserName>
</WebImportHeader>
`;
webHeader = webHeader
.replaceAll(
"[requestID]",
`${data[0].releaseNumber}-${plantToken[0].value}`
)
.replaceAll("[requestUser]", requestUser);
return webHeader;
};

View File

@@ -0,0 +1,54 @@
import { freightClass } from "../../../../../globalUtils/freightClass.js";
export const loadItems = async (data: any) => {
let itemGroups = "";
for (let i = 0; i < data.length; i++) {
let newItem = `
<ItemGroup id="" isShipUnit="false" isHandlingUnit="false" sequence="${
i + 1
}">
<ContainedBy id=""/>
<LineItem lineNumber="${i + 1}"/>
<Dimensions>
<Dimension type="Length" uom="IN">${(
data[i].pkgLengh / 25.4
).toFixed(2)}</Dimension>
<Dimension type="Width" uom="IN">${(
data[i].pkgWidth / 25.4
).toFixed(2)}</Dimension>
<Dimension type="Height" uom="IN">${Math.round(
data[i].pkgHeight / 25.4
).toFixed(2)}</Dimension>
</Dimensions>
<Description>${`av ${data[i].article} ${data[i].articleAlias}`}</Description>
<FreightClasses>
<FreightClass type="">${freightClass(
data[i].pkgWeight,
data[i].pkgLengh,
data[i].pkgWidth,
data[i].pkgHeight
)}</FreightClass>
</FreightClasses>
<Commodity/>
<NmfcCode/>
<HazardousMaterial>false</HazardousMaterial>
<HazMatDetail/>
<Weights>
<Weight type="actual" uom="KG">${
data[i].pkgWeight * data[i].Pallets
}</Weight>
</Weights>
<Quantities>
<Quantity type="actual" uom="pallet">${
data[i].Pallets
}</Quantity>
</Quantities>
</ItemGroup>
`;
itemGroups += newItem;
}
return itemGroups;
};

View File

@@ -0,0 +1,40 @@
import axios from "axios";
import querystring from "querystring";
import { createLog } from "../../../../logger/logger.js";
//tiCreds
const userid = process.env.USERID || "";
const password = process.env.PASSWORD || "";
export const postToTi = async (data: string) => {
const formBody = querystring.stringify({
userid,
password,
request: data,
});
axios
.post(
"https://t-insightws.mercurygate.net/MercuryGate/common/remoteService.jsp",
formBody,
{
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
}
)
.then((response) => {
//console.log(response.data)
createLog("info", "ti", "notify", "Data was sent over to TI");
return {
success: true,
message: "Data was sent over to TI",
};
})
.catch((error) => {
createLog("error", "ti", "notify", error);
return {
success: false,
message: "Error sending data to TI",
data: error,
};
});
};

View File

@@ -0,0 +1,36 @@
import { createLog } from "../../../../logger/logger.js";
export const scacCheck = async (data: any) => {
createLog(
"info",
"ti",
"notify",
`Checking if ${data[0].addressAlias} has remark: ${data[0].remark}`
);
const priceSheet = `
<PriceSheets>
<PriceSheet type="Carrier" isSelected="${
data[0].remark.includes("SCAC") && // make sure we have the word scac
data[0].remark.split(",")[0] && // and its valid as well
!data[0].remark.includes("[") // if they funny and put [carrier] just ignore it.
? "true"
: "false"
}">
<ContractId/>
${
data[0].remark.split(",")[0]
? `<SCAC>${data[0].remark
.split(",")[0]
.split(":")[1]
?.toUpperCase()
.slice(0, 4)}</SCAC>`
: `<SCAC/>`
}
<Mode/>
</PriceSheet>
</PriceSheets>
`;
return priceSheet;
};

View File

@@ -0,0 +1,327 @@
import { eq, sql } from "drizzle-orm";
import { db } from "../../../../../../database/dbclient.js";
import { serverData } from "../../../../../../database/schema/serverData.js";
import { settings } from "../../../../../../database/schema/settings.js";
import { tryCatch } from "../../../../../globalUtils/tryCatch.js";
import { notifications } from "../../../../../../database/schema/notifications.js";
import { getHeaders } from "../../../../sqlServer/querys/notifications/ti/getHeaders.js";
import { query } from "../../../../sqlServer/prodSqlServer.js";
import { createLog } from "../../../../logger/logger.js";
import { getOrderToSend } from "../../../../sqlServer/querys/notifications/ti/getOrderToSend.js";
import { xmlPayloadTI } from "./tiXmlPayload.js";
import { headerUpdate } from "./headerUpdate.js";
import { loadItems } from "./loadItems.js";
import { dateCorrection } from "./dateCorrection.js";
import { scacCheck } from "./scacCodeCheck.js";
import { postToTi } from "./postToTI.js";
export const tiImport = async () => {
// get the plant token
let payload = xmlPayloadTI;
const { data: plantData, error: plantError } = await tryCatch(
db.select().from(settings)
);
if (plantError)
return {
success: false,
message: "Error Getting Plant Data.",
data: plantError,
};
const plantToken = plantData?.filter((n) => n.name === "plantToken");
const { data: plantInfo, error: plantEr } = await tryCatch(
db
.select()
.from(serverData)
.where(eq(serverData.plantToken, plantToken[0].value))
);
if (plantEr)
return {
success: false,
message: "Error Getting Plant Data.",
data: plantEr,
};
// parsing posting window
const plantI = plantInfo!;
// order notifications
const { data: notificationSet, error: notificationSettingsErr } =
await tryCatch(
db
.select()
.from(notifications)
.where(eq(notifications.name, "tiIntergration"))
);
if (notificationSettingsErr)
return {
success: false,
message: "Notification missing.",
data: notificationSettingsErr,
};
const notiSet: any = notificationSet;
const customerAccountNum = plantI[0].customerTiAcc as string; // tiIntergration
// get current releaes not in the already sent oders
const releaseString = notiSet[0].notifiySettings.releases
.map((num: any) => `'${num.releaseNumber}'`)
.join(", ");
let orders = getHeaders
.replaceAll("[from]", notiSet[0]?.notifiySettings.start)
.replaceAll("[to]", notiSet[0]?.notifiySettings.end)
.replaceAll("[exclude]", releaseString);
// get the headers pending
const { data: h, error: headerError } = await tryCatch(
query(orders, "Ti get open headers")
);
const header: any = h?.data as any;
if (headerError) {
createLog(
"error",
"ti",
"notify",
`Error getting headers: ${headerError}`
);
return {
success: false,
message: "Error getting headers",
data: headerError,
};
}
if (header.length === 0) {
createLog(
"info",
"ti",
"notify",
"There are no pending orders to be sent over to ti."
);
return {
success: true,
message: "There are no pending orders to be sent over to ti.",
};
}
createLog(
"info",
"tiIntergration",
"notify",
`There are a total of ${header.length} to send over`
);
/**
* Update the query to get only the first header
*/
// update query to have the correct plant token
let orderToSend = getOrderToSend
.replaceAll("test1", plantToken[0].value)
.replaceAll("[releaseToProcess]", `'${header[0].releaseNumber}'`)
.replaceAll("[from]", notiSet[0].notifiySettings.start)
.replaceAll("[to]", notiSet[0].notifiySettings.end);
// get the headers pending
const { data: o, error: ordersError } = await tryCatch(
query(orderToSend, "Ti get open headers")
);
const orderData: any = o?.data as any;
if (ordersError)
return {
success: false,
message: "Error getting getting orders",
data: ordersError,
};
// update the special instructions section
const otherSettings = plantI[0]?.otherSettings as {
specialInstructions: string;
active: boolean;
}[];
const specialInfo = otherSettings[0].specialInstructions.replaceAll(
"[header]",
orderData[0].Header
);
// add the full amount of pallets sending over
let fullPalToSend = orderData.reduce(
(acc: any, o: any) => acc + o.Pallets,
0
);
//console.log("payload", payload);
payload = payload
.replaceAll(
`[WebImportHeader]`,
await headerUpdate(orderData, plantToken)
)
.replaceAll(`[items]`, await loadItems(orderData))
.replaceAll(`[customerAccountNum]`, customerAccountNum)
.replaceAll("[fullTotalPal]", fullPalToSend)
// add in release info
.replaceAll(`[shipNumber]`, orderData[0].releaseNumber)
.replaceAll(`[loadNumber]`, orderData[0].releaseNumber);
// add in the multi release numbers
let multiRelease = ``;
if (orderData.length > 0) {
for (let i = 0; i < orderData.length; i++) {
const newRelease = `
<ReferenceNumber type="Release Number" isPrimary="false">${orderData[i].releaseNumber}</ReferenceNumber>`;
multiRelease += newRelease;
}
payload = payload.replaceAll("[multieReleaseNumber]", multiRelease);
} else {
payload = payload.replaceAll("[multieReleaseNumber]", "");
}
// add the correct date stuff
payload = payload
.replaceAll(
"[loadingDate]",
await dateCorrection(orderData[0].LoadingDate)
)
.replaceAll(
"[deliveryDate]",
await dateCorrection(orderData[0].DeliveryDate)
);
// shipping ours corrections
const formattedDate = orderData[0].LoadingDate.toLocaleDateString("en-US", {
month: "2-digit",
day: "2-digit",
year: "numeric",
});
const shippingHours = JSON.parse(plantI[0]?.shippingHours!);
payload = payload
.replaceAll(
"[shippingHoursEarly]",
`${formattedDate} ${shippingHours[0].early}`
)
.replaceAll(
"[shippingHoursLate]",
`${formattedDate} ${shippingHours[0].late}`
);
// special instructions
if (otherSettings[0].specialInstructions.length != 0) {
payload = payload.replaceAll("[specialInstructions]", specialInfo);
} else {
payload = payload.replaceAll("[specialInstructions]", "");
}
// shipper info
payload = payload
.replaceAll("[plantName]", `Alpla ${plantI[0]?.sName!}`)
.replaceAll("[plantStreetAddress]", plantI[0]?.streetAddress!)
.replaceAll("[plantCity]", plantI[0]?.cityState!.split(",")[0])
.replaceAll("[plantState]", plantI[0]?.cityState!.split(",")[1])
.replaceAll("[plantZipCode]", plantI[0]?.zipcode!)
.replaceAll("[contactNum]", plantI[0]?.contactPhone!)
.replaceAll("[contactEmail]", plantI[0]?.contactEmail!)
// customer info
.replaceAll("[customerName]", orderData[0].addressAlias)
.replaceAll("[customerStreetAddress]", orderData[0].streetAddress)
.replaceAll("[customerCity]", orderData[0].city.split(",")[0])
.replaceAll("[customerState]", orderData[0].city.split(",")[1])
.replaceAll("[customerZip]", orderData[0].zipCode)
.replaceAll("[customerPO]", orderData[0].Header)
// .replaceAll(
// "[glCoding]",
// `52410-${
// orderData[0].artileType.toLowerCase() === "preform" ||
// orderData[0].artileType.toLowerCase() === "metalCage"
// ? 31
// : plantI[0].greatPlainsPlantCode
// }`
// ) // {"52410 - " + (artileType.toLowerCase() === "preform" || artileType.toLowerCase() === "metalCage" ? 31: plantInfo[0].greatPlainsPlantCode)}
.replaceAll("[glCoding]", `52410`)
.replaceAll(
"[pfc]",
`${
orderData[0].artileType.toLowerCase() === "preform" ||
orderData[0].artileType.toLowerCase() === "metalCage"
? 40
: orderData[0].costCenter
}`
)
.replaceAll(
"[locCode]",
`${
orderData[0].artileType.toLowerCase() === "preform" ||
orderData[0].artileType.toLowerCase() === "metalCage"
? 31
: plantI[0].greatPlainsPlantCode
}`
)
.replaceAll("[priceSheet]", await scacCheck(orderData));
//send over to be processed
//console.log("payload", payload);
const { data: tiPost, error: tiError } = await tryCatch(postToTi(payload));
if (tiError) {
return {
success: false,
message: "Error posting to TI",
error: tiError,
};
}
/**
* Update the db so we dont try to pull the next one
*/
const currentDate = new Date(Date.now());
const uniqueOrders = Array.from(
new Set([
...notiSet[0].notifiySettings.releases,
{
releaseNumber: header[0].releaseNumber,
timeStamp: currentDate,
},
])
);
// 45 days ago
const dateLimit = new Date(
currentDate.getTime() - 45 * 24 * 60 * 60 * 1000
);
// filter dates
let filteredOrders = uniqueOrders.filter((item) => {
const time = new Date(item.timeStamp).getTime();
return time >= dateLimit.getTime();
});
const { data, error } = await tryCatch(
db
.update(notifications)
.set({
lastRan: sql`NOW()`,
notifiySettings: {
...notiSet[0].notifiySettings,
releases: filteredOrders,
},
})
.where(eq(notifications.name, "tiIntergration"))
);
createLog("info", "ti", "notify", "done with this order");
return { message: "done with this order" };
};

View File

@@ -0,0 +1,207 @@
export let xmlPayloadTI = `
<service-request>
<service-id>ImportWeb</service-id>
[WebImportHeader]
<WebImportFile>
<MercuryGate>
<Header>
<SenderID/>
<ReceiverID/>
<DocTypeID>MasterBillOfLading</DocTypeID>
<DocCount>1</DocCount>
</Header>
<Load action="UpdateOrAdd">
<Enterprise name="" customerAcctNum="[customerAccountNum]"/>
<AssignedTo/>
<ReferenceNumbers>
<ReferenceNumber type="Load Number" isPrimary="true">[loadNumber]</ReferenceNumber>
</ReferenceNumbers>
<Payment>
<Method>Prepaid</Method>
<BillTo thirdParty="False">
<Address Type="BillTo" isResidential="False">
<Alias/>
<Name>ALPLA</Name>
<AddrLine1>CO TRANSPORTATION INSIGHT</AddrLine1>
<AddrLine2>PO BOX 23000</AddrLine2>
<City>HICKORY</City>
<StateProvince>NC</StateProvince>
<PostalCode>28603</PostalCode>
<CountryCode>USA</CountryCode>
<Contacts/>
</Address>
</BillTo>
</Payment>
[priceSheet]
<!-- Comments here -->
<Comments>
<Comment type="SpecialInstructions">[specialInstructions]</Comment>
</Comments>
<Plan>
<Events count="2">
<Event type="Pickup" sequenceNum="1">
<Dates>
<Date type="earliest">[shippingHoursEarly]</Date>
<Date type="latest">[shippingHoursLate]</Date>
</Dates>
<Address type="" isResidential="" isPrimary="false">
<LocationCode/>
<Name>[plantName]</Name>
<AddrLine1>[plantStreetAddress]</AddrLine1>
<AddrLine2/>
<City>[plantCity]</City>
<StateProvince>[plantState]</StateProvince>
<PostalCode>[plantZipCode]</PostalCode>
<CountryCode>USA</CountryCode>
<Contacts>
<Contact type="">
<Name/>
<ContactMethods>
<ContactMethod sequenceNum="1" type="phone">[contactNum]</ContactMethod>
<ContactMethod sequenceNum="1" type="email">[contactEmail]</ContactMethod>
</ContactMethods>
</Contact>
</Contacts>
</Address>
<Shipments>
<ReferenceNumbers>
<ReferenceNumber type="Shipment Number" isPrimary="true">[shipNumber]</ReferenceNumber>
</ReferenceNumbers>
</Shipments>
</Event>
<Event type="Drop" sequenceNum="2">
<Dates>
<Date type="earliest">[loadingDate]</Date>
<Date type="latest">[deliveryDate]</Date>
</Dates>
<Address type="" isResidential="" isPrimary="false">
<LocationCode/>
<Name>[customerName]</Name>
<AddrLine1>[customerStreetAddress]</AddrLine1>
<AddrLine2/>
<City>[customerCity]</City>
<StateProvince>[customerState]</StateProvince>
<PostalCode>[customerZip]</PostalCode>
<CountryCode>USA</CountryCode>
<Contacts>
<Contact type="">
<Name/>
<ContactMethods>
<ContactMethod sequenceNum="1" type="phone">800-555-1122</ContactMethod>
</ContactMethods>
</Contact>
</Contacts>
</Address>
<Shipments>
<ReferenceNumbers>
<ReferenceNumber type="Shipment Number" isPrimary="true">[shipNumber]</ReferenceNumber>
</ReferenceNumbers>
</Shipments>
</Event>
</Events>
</Plan>
<Shipments>
<Shipment type="Regular" action="UpdateOrAdd">
<Status>Pending</Status>
<Enterprise name="" customerAcctNum="[customerAccountNum]"/>
<ReferenceNumbers>
<ReferenceNumber type="Shipment Number" isPrimary="true">[shipNumber]</ReferenceNumber>
<ReferenceNumber type="PO Number" isPrimary="false">[customerPO]</ReferenceNumber>
[multieReleaseNumber]
<!-- Comments here -->
<!-- <ReferenceNumber type="Store Number" isPrimary="false">[glCoding]</ReferenceNumber> -->
<ReferenceNumber type="GL Account Code" isPrimary="false">[glCoding]</ReferenceNumber>
<ReferenceNumber type="Profit Center" isPrimary="false">[pfc]</ReferenceNumber>
<ReferenceNumber type="Location Code" isPrimary="false">[locCode]</ReferenceNumber>
</ReferenceNumbers>
<Services/>
<EquipmentList/>
<Dimensions>
<Dimension type="RatingCount">[fullTotalPal]</Dimension>
</Dimensions>
<Dates>
<Pickup>
<Date type="earliest">[shippingHoursEarly]</Date>
<Date type="latest">[shippingHoursLate]</Date>
</Pickup>
<Drop>
<Date type="earliest">[deliveryDate]</Date>
<Date type="latest">[deliveryDate]</Date>
</Drop>
</Dates>
[priceSheet]
<Shipper>
<Address type="" isResidential="" isPrimary="false">
<LocationCode/>
<Name>[plantName]</Name>
<AddrLine1>[plantStreetAddress]</AddrLine1>
<AddrLine2/>
<City>[plantCity]</City>
<StateProvince>[plantState]</StateProvince>
<PostalCode>[plantZipCode]</PostalCode>
<CountryCode>USA</CountryCode>
<Contacts>
<Contact type="">
<Name/>
<ContactMethods>
<ContactMethod sequenceNum="1" type="phone">[contactNum]</ContactMethod>
</ContactMethods>
</Contact>
</Contacts>
</Address>
</Shipper>
<Consignee>
<Address type="" isResidential="" isPrimary="false">
<LocationCode/>
<Name>[customerName]</Name>
<AddrLine1>[customerStreetAddress]</AddrLine1>
<AddrLine2/>
<City>[customerCity]</City>
<StateProvince>[customerState]</StateProvince>
<PostalCode>[customerZip]</PostalCode>
<CountryCode>USA</CountryCode>
<Contacts />
<!-- Location contacts are optional -->
<Contacts>
<Contact type="">
<Name>Alpla</Name>
<ContactMethods>
<!-- Valid contactMethod types are phone, fax, and email -->
<ContactMethod sequenceNum="1" type="phone">[contactNum]</ContactMethod>
<ContactMethod sequenceNum="1" type="email">[contactEmail]</ContactMethod>
</ContactMethods>
</Contact>
</Contacts>
</Address>
</Consignee>
<ItemGroups>
[items]
</ItemGroups>
<Payment>
<Method>Prepaid</Method>
<BillTo thirdParty="False">
<Address Type="BillTo" isResidential="False">
<Alias/>
<Name>ALPLA</Name>
<AddrLine1>CO TRANSPORTATION INSIGHT</AddrLine1>
<AddrLine2>PO BOX 23000</AddrLine2>
<City>HICKORY</City>
<StateProvince>NC</StateProvince>
<PostalCode>28603</PostalCode>
<CountryCode>USA</CountryCode>
<Contacts/>
</Address>
</BillTo>
</Payment>
</Shipment>
</Shipments>
</Load>
</MercuryGate>
</WebImportFile>
</WebImport>
</data>
</service-request>
`;

View File

@@ -0,0 +1,36 @@
import { delay } from "../../../../globalUtils/delay.js";
import { createLog } from "../../../logger/logger.js";
import { tiImport } from "./tiFullFlow/tiImport.js";
// add a running check so we cant flag it twice
export let tiExportRunning = false;
export const runTiImport = async () => {
let finished = false;
let test: any;
tiExportRunning = true;
do {
createLog("info", "ti", "notify", "processing new data");
// code block to be executed
test = await tiImport();
createLog(
"info",
"ti",
"notify",
`Still more to process? ${test.success ? "No" : "Yes"}`
);
if (test.success) {
finished = true;
}
if (!test.success) {
//errors are handled in the tiImport function
tiExportRunning = false;
}
await delay(1000 * 5);
} while (!finished);
tiExportRunning = false;
return { success: true, message: "Finished processing all data." };
};
export default runTiImport;