feat(notificaitons): fixed and corrections to get them working properly

This commit is contained in:
2025-04-04 17:12:48 -05:00
parent 99477bac19
commit a7818b4ca3
21 changed files with 888 additions and 633 deletions

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,33 @@
import { createLog } from "../../../../logger/logger.js";
export const scacCheck = async (data: any) => {
createLog(
"info",
"ti",
"notify",
`Checking if ${data[0].addressAlias} has scac: ${
data[0].remark.split(",")[0] ? "there was one" : "no scac"
}`
);
const priceSheet = `
<PriceSheets>
<PriceSheet type="Carrier" isSelected="${
data[0].remark.split(",")[0] ? "true" : "false"
}">
<ContractId/>
${
data[0].remark.split(",")[0]
? `<SCAC>${data[0].remark
.split(",")[0]
.split(":")[1]
.toUpperCase()}</SCAC>`
: `<SCAC/>`
}
<Mode/>
</PriceSheet>
</PriceSheets>
`;
return priceSheet;
};

View File

@@ -0,0 +1,301 @@
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: header, error: headerError } = await tryCatch(
query(orders, "Ti get open headers")
);
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: orderData, error: ordersError } = await tryCatch(
query(orderToSend, "Ti get open headers")
);
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(
"[pfc]",
`${
orderData[0].artileType.toLowerCase() === "preform" ||
orderData[0].artileType.toLowerCase() === "metalCage"
? 40
: orderData[0].costCenter
}`
)
.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 uniqueOrders = Array.from(
new Set([
...notiSet[0].notifiySettings.releases,
{
releaseNumber: header[0].releaseNumber,
timeStamp: new Date(Date.now()),
},
])
);
const { data, error } = await tryCatch(
db
.update(notifications)
.set({
lastRan: sql`NOW()`,
notifiySettings: {
...notiSet[0].notifiySettings,
releases: uniqueOrders,
},
})
.where(eq(notifications.name, "tiIntergration"))
);
createLog("info", "ti", "notify", "done with this order");
return { message: "done with this order" };
};

View File

@@ -1,9 +1,6 @@
export let xmlPayloadTI = `
<service-request>
<service-id>ImportWeb</service-id>
<request-id>[requestID]</request-id>
<data>
<WebImport>
[WebImportHeader]
<WebImportFile>
<MercuryGate>
@@ -35,19 +32,18 @@ export let xmlPayloadTI = `
</Address>
</BillTo>
</Payment>
<PriceSheets>
<PriceSheet type="Carrier" isSelected="false"> // get this from the price sheet
<ContractId/>
<SCAC/>
<Mode/>
</PriceSheet>
</PriceSheets>
[priceSheet]
<!-- Comments here -->
<Comments>
<Comment type="SpecialInstructions">[specialInstructions]</Comment>
</Comments>
<Plan>
<Events count="2">
<Event type="Pickup" sequenceNum="1">
<Dates>
<Date type="earliest">[loadingDate]</Date>
<Date type="latest">[deliveryDate]</Date>
<Date type="earliest">[shippingHoursEarly]</Date>
<Date type="latest">[shippingHoursLate]</Date>
</Dates>
<Address type="" isResidential="" isPrimary="false">
<LocationCode/>
@@ -119,24 +115,20 @@ export let xmlPayloadTI = `
</ReferenceNumbers>
<Services/>
<EquipmentList/>
6
<Dimensions>
<Dimension type="RatingCount">[fullTotalPal]</Dimension>
</Dimensions>
<Dates>
<Pickup>
<Date type="earliest">[loadingDate]</Date>
<Date type="latest">[loadingDate]</Date>
<Date type="earliest">[shippingHoursEarly]</Date>
<Date type="latest">[shippingHoursLate]</Date>
</Pickup>
<Drop>
<Date type="earliest">[deliveryDate]</Date>
<Date type="latest">[deliveryDate]</Date>
</Drop>
</Dates>
<PriceSheets>
<PriceSheet type="Carrier" isSelected="false">
<ContractId/>
<SCAC/>
<Mode/>
</PriceSheet>
</PriceSheets>
[priceSheet]
<Shipper>
<Address type="" isResidential="" isPrimary="false">
<LocationCode/>
@@ -160,7 +152,7 @@ export let xmlPayloadTI = `
<Consignee>
<Address type="" isResidential="" isPrimary="false">
<LocationCode/>
<Name>[customer]</Name>
<Name>[customerName]</Name>
<AddrLine1>[customerStreetAddress]</AddrLine1>
<AddrLine2/>
<City>[customerCity]</City>
@@ -168,6 +160,17 @@ export let xmlPayloadTI = `
<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>