diff --git a/backend/notification/notification.manualTrigger.ts b/backend/notification/notification.manualTrigger.ts new file mode 100644 index 0000000..2e8c545 --- /dev/null +++ b/backend/notification/notification.manualTrigger.ts @@ -0,0 +1,96 @@ +import { eq } from "drizzle-orm"; +import { type Response, Router } from "express"; +import { db } from "../db/db.controller.js"; +import { notifications } from "../db/schema/notifications.schema.js"; +import { auth } from "../utils/auth.utils.js"; +import { apiReturn } from "../utils/returnHelper.utils.js"; +import { tryCatch } from "../utils/trycatch.utils.js"; + +const r = Router(); + +r.post("/", async (req, res: Response) => { + const hasPermissions = await auth.api.userHasPermission({ + body: { + //userId: req?.user?.id, + role: req.user?.roles as any, + permissions: { + notifications: ["readAll"], // This must match the structure in your access control + }, + }, + }); + + if (!hasPermissions) { + return apiReturn(res, { + success: false, + level: "error", + module: "notification", + subModule: "post", + message: `You do not have permissions to be here`, + data: [], + status: 400, + }); + } + + const { data: nName, error: nError } = await tryCatch( + db + .select() + .from(notifications) + .where(eq(notifications.name, req.body.name)), + ); + + if (nError) { + return apiReturn(res, { + success: false, + level: "error", + module: "notification", + subModule: "get", + message: `There was an error getting the notifications `, + data: [nError], + status: 400, + }); + } + + const { data: sub, error: sError } = await tryCatch( + db + .select() + .from(notifications) + .where(eq(notifications.name, req.body.name)), + ); + + if (sError) { + return apiReturn(res, { + success: false, + level: "error", + module: "notification", + subModule: "get", + message: `There was an error getting the subs `, + data: [sError], + status: 400, + }); + } + + const emailString = [ + ...new Set( + sub.flatMap((e: any) => + e.emails?.map((email: any) => email.trim().toLowerCase()), + ), + ), + ].join(";"); + + console.log(emailString); + const { default: runFun } = await import( + `./notification.${req.body.name.trim()}.js` + ); + const manual = await runFun(nName[0], "blake.matthes@alpla.com"); + + return apiReturn(res, { + success: true, + level: "info", + module: "notification", + subModule: "post", + message: `Manual Trigger ran`, + data: manual ?? [], + status: 200, + }); +}); +export default r; diff --git a/backend/notification/notification.qualityBlocking.ts b/backend/notification/notification.qualityBlocking.ts new file mode 100644 index 0000000..5dc20e8 --- /dev/null +++ b/backend/notification/notification.qualityBlocking.ts @@ -0,0 +1,109 @@ +import { eq } from "drizzle-orm"; +import { db } from "../db/db.controller.js"; +import { notifications } from "../db/schema/notifications.schema.js"; +import { prodQuery } from "../prodSql/prodSqlQuery.controller.js"; +import { + type SqlQuery, + sqlQuerySelector, +} from "../prodSql/prodSqlQuerySelector.utils.js"; +import { delay } from "../utils/delay.utils.js"; +import { returnFunc } from "../utils/returnHelper.utils.js"; +import { sendEmail } from "../utils/sendEmail.utils.js"; +import { tryCatch } from "../utils/trycatch.utils.js"; +import { v1QueryRun } from "../utils/pgConnectToLst.utils.js"; + +const func = async (data: any, emails: string) => { + // TODO: remove this disable once all 17 plants are on this new lst + v1QueryRun(`update public.notifications set active = false where name = '${data.name}'`) + + const { data: l, error: le } = (await tryCatch( + db.select().from(notifications).where(eq(notifications.id, data.id)), + )) as any; + + if (le) { + return returnFunc({ + success: false, + level: "error", + module: "notification", + subModule: "query", + message: `${data.name} encountered an error while trying to get initial info`, + data: le as any, + notify: true, + }); + } + + // search the query db for the query by name + const sqlQuery = sqlQuerySelector(`${data.name}`) as SqlQuery; + // create the ignore audit logs ids + + // get get the latest blocking order id that was sent + const blockingOrderId = l[0].options[0].lastBlockingOrderIdSent ?? 69; + + // run the check + const { data: queryRun, error } = await tryCatch( + prodQuery( + sqlQuery.query.replace("[lastBlocking]", blockingOrderId), + `Running notification query: ${l[0].name}`, + ), + ); + + if (error) { + return returnFunc({ + success: false, + level: "error", + module: "notification", + subModule: "query", + message: `Data for: ${l[0].name} encountered an error while trying to get it`, + data: error as any, + notify: true, + }); + } + + if (queryRun.data.length > 0) { + for (const bo of queryRun.data) { + const sentEmail = await sendEmail({ + email: emails, + subject: bo.subject, + template: "qualityBlocking", + context: { + items: bo, + }, + }); + + if (!sentEmail?.success) { + return returnFunc({ + success: false, + level: "error", + module: "notification", + subModule: "email", + message: `${l[0].name} failed to send the email`, + data: sentEmail?.data as any, + notify: true, + }); + } + + await delay(1500); + + const { error: dbe } = await tryCatch( + db + .update(notifications) + .set({ options: [{ lastBlockingOrderIdSent: bo.blockingNumber }] }) + .where(eq(notifications.id, data.id)), + ); + + if (dbe) { + return returnFunc({ + success: false, + level: "error", + module: "notification", + subModule: "query", + message: `Data for: ${l[0].name} encountered an error while trying to get it`, + data: dbe as any, + notify: true, + }); + } + } + } +}; + +export default func; diff --git a/backend/notification/notification.routes.ts b/backend/notification/notification.routes.ts index 9fe22ca..2ab0874 100644 --- a/backend/notification/notification.routes.ts +++ b/backend/notification/notification.routes.ts @@ -1,5 +1,6 @@ import type { Express } from "express"; import { requireAuth } from "../middleware/auth.middleware.js"; +import manual from "./notification.manualTrigger.js"; import getNotifications from "./notification.route.js"; import updateNote from "./notification.update.route.js"; import deleteSub from "./notificationSub.delete.route.js"; @@ -11,6 +12,7 @@ export const setupNotificationRoutes = (baseUrl: string, app: Express) => { //stats will be like this as we dont need to change this app.use(`${baseUrl}/api/notification`, requireAuth, getNotifications); app.use(`${baseUrl}/api/notification`, requireAuth, updateNote); + app.use(`${baseUrl}/api/notification/manual`, requireAuth, manual); app.use(`${baseUrl}/api/notification/sub`, requireAuth, subs); app.use(`${baseUrl}/api/notification/sub`, requireAuth, newSub); app.use(`${baseUrl}/api/notification/sub`, requireAuth, updateSub); diff --git a/backend/notification/notifications.master.ts b/backend/notification/notifications.master.ts index 17f4c22..d61bbf0 100644 --- a/backend/notification/notifications.master.ts +++ b/backend/notification/notifications.master.ts @@ -22,7 +22,7 @@ const note: NewNotification[] = [ "Checks for new blocking orders that have been entered, recommend to get the most recent order in here before activating.", active: false, interval: "10", - options: [{ sentBlockingOrders: [{ timeStamp: "0", blockingOrder: 1 }] }], + options: [{ lastBlockingOrderIdSent: 1 }], }, { name: "alplaPurchaseHistory", diff --git a/backend/prodSql/queries/qualityBlocking.sql b/backend/prodSql/queries/qualityBlocking.sql new file mode 100644 index 0000000..6d7ad3d --- /dev/null +++ b/backend/prodSql/queries/qualityBlocking.sql @@ -0,0 +1,44 @@ +use [test1_AlplaPROD2.0_Read] + +SELECT +'Alert! new blocking order: #' + cast(bo.HumanReadableId as varchar) + ' - ' + bo.ArticleVariantDescription as subject +,cast(bo.[HumanReadableId] as varchar) as blockingNumber +,bo.[ArticleVariantDescription] as article +,cast(bo.[CustomerHumanReadableId] as varchar) + ' - ' + bo.[CustomerDescription] as customer +,convert(varchar(10), bo.[BlockingDate], 101) + ' ' + convert(varchar(5), bo.[BlockingDate], 108) as blockingDate +,cast(ArticleVariantHumanReadableId as varchar) + ' - ' + ArticleVariantDescription as av +,case when bo.Remark = '' or bo.Remark is NULL then 'Please reach out to quality for the reason this was placed on hold as a remark was not entered during the blocking processs' else bo.Remark end as remark +,cast(FORMAT(TotalAmountOfPieces, '###,###') as varchar) + ' / ' + cast(LoadingUnit as varchar) as peicesAndLoadingUnits +,bo.ProductionLotHumanReadableId as lotNumber +,cast(osd.IdBlockingDefectsGroup as varchar) + ' - ' + osd.Description as mainDefectGroup +,cast(df.HumanReadableId as varchar) + ' - ' + os.Description as mainDefect +,lot.MachineLocation as line +--,* +FROM [blocking].[BlockingOrder] (nolock) as bo + + +/*** get the defect details ***/ +join +[blocking].[BlockingDefect] (nolock) AS df +on df.id = bo.MainDefectId + +/*** pull description from 1.0 ***/ +left join +[AlplaPROD_test1].[dbo].[T_BlockingDefects] (nolock) as os +on os.IdGlobalBlockingDefect = df.HumanReadableId + +/*** join in 1.0 defect group ***/ +left join +[AlplaPROD_test1].[dbo].[T_BlockingDefectsGroups] (nolock) as osd +on osd.IdBlockingDefectsGroup = os.IdBlockingDefectsGroup + +left join +[productionControlling].[ProducedLot] (nolock) as lot +on lot.id = bo.ProductionLotId + + +where +bo.[BlockingDate] between getdate() - 2 and getdate() + 3 and +bo.BlockingTrigger = 1 -- so we only get the ir blocking and not coa +--and HumanReadableId NOT IN ([sentBlockingOrders]) +and bo.HumanReadableId > [lastBlocking] \ No newline at end of file diff --git a/backend/utils/mailViews/qualityBlocking.hbs b/backend/utils/mailViews/qualityBlocking.hbs new file mode 100644 index 0000000..5ab1d20 --- /dev/null +++ b/backend/utils/mailViews/qualityBlocking.hbs @@ -0,0 +1,73 @@ + + + + + + {{!-- --}} + + + +
+

All,

+

Please see the new blocking order that was created.

+
+ +
+
+

Remarks:

+

{{items.remark}}

+
+
+ +
+

For further questions please reach out to quality.

+

Thank you,

+

Quality Department

+

+ + + + \ No newline at end of file diff --git a/backend/utils/pgConnectToLst.utils.ts b/backend/utils/pgConnectToLst.utils.ts new file mode 100644 index 0000000..7862a09 --- /dev/null +++ b/backend/utils/pgConnectToLst.utils.ts @@ -0,0 +1,46 @@ +import pkg from "pg"; +const { Client } = pkg; + +const v1client = new Client({ + host: "localhost", + port: 5432, + user: "postgres", + password: "obelix", + database: "lst", +}); + +const v2client = new Client({ + host: "localhost", + port: 5432, + user: "postgres", + password: "obelix", + database: "lst_db", +}); + +export const v1QueryRun= async (query:string)=> { + try { + await v1client.connect(); + + const res = await v1client.query(query); + + console.log("Rows affected:", res.rowCount); + } catch (err) { + console.error("Error running query:", err); + } finally { + await v1client.end(); + } +} + +export const v2QueryRun = async (query:string)=> { + try { + await v2client.connect(); + + const res = await v2client.query(query); + + console.log("Rows affected:", res.rowCount); + } catch (err) { + console.error("Error running query:", err); + } finally { + await v2client.end(); + } +} \ No newline at end of file