From a17787e85217f1fa4a5e5389e29c33ec09c286c5 Mon Sep 17 00:00:00 2001 From: Blake Matthes Date: Mon, 6 Apr 2026 16:01:06 -0500 Subject: [PATCH] feat(notification): reprint added --- .../notification.reprintLabels.ts | 108 +++++++++++++++++- backend/notification/notifications.master.ts | 2 +- backend/prodSql/queries/reprintLabels.sql | 28 +++++ backend/utils/mailViews/reprintLabels.hbs | 47 ++++++++ backend/utils/returnHelper.utils.ts | 3 +- frontend/src/routes/admin/notifications.tsx | 22 ++-- frontend/src/routes/admin/settings.tsx | 2 +- 7 files changed, 198 insertions(+), 14 deletions(-) create mode 100644 backend/prodSql/queries/reprintLabels.sql create mode 100644 backend/utils/mailViews/reprintLabels.hbs diff --git a/backend/notification/notification.reprintLabels.ts b/backend/notification/notification.reprintLabels.ts index 930e092..65bdf62 100644 --- a/backend/notification/notification.reprintLabels.ts +++ b/backend/notification/notification.reprintLabels.ts @@ -1,8 +1,108 @@ -const reprint = (data: any, emails: string) => { - // TODO: do the actual logic for the notification. - console.log(data); - console.log(emails); +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 { returnFunc } from "../utils/returnHelper.utils.js"; +import { sendEmail } from "../utils/sendEmail.utils.js"; +import { tryCatch } from "../utils/trycatch.utils.js"; +const reprint = async (data: any, emails: string) => { + // TODO: do the actual logic for the notification. + 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], + notify: true, + }); + } + + // search the query db for the query by name + const sqlQuery = sqlQuerySelector(`${data.name}`) as SqlQuery; + // create the ignore audit logs ids + const ignoreIds = l[0].options[0]?.auditId + ? `${l[0].options[0]?.auditId}` + : "0"; + + // run the check + const { data: queryRun, error } = await tryCatch( + prodQuery( + sqlQuery.query + .replace("[intervalCheck]", l[0].interval) + .replace("[ignoreList]", ignoreIds), + `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], + notify: true, + }); + } + + if (queryRun.data.length > 0) { + // update the latest audit id + const { error: dbe } = await tryCatch( + db + .update(notifications) + .set({ options: [{ auditId: `${queryRun.data[0].id}` }] }) + .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], + notify: true, + }); + } + + // send the email + + const sentEmail = await sendEmail({ + email: emails, + subject: "Alert! Label Reprinted", + template: "reprintLabels", + context: { + items: queryRun.data, + }, + }); + + if (!sentEmail?.success) { + return returnFunc({ + success: false, + level: "error", + module: "email", + subModule: "notification", + message: `${l[0].name} failed to send the email`, + data: [sentEmail], + notify: true, + }); + } + } else { + console.log("doing nothing as there is nothing to do."); + } // TODO send the error to systemAdmin users so they do not always need to be on the notifications. // these errors are defined per notification. }; diff --git a/backend/notification/notifications.master.ts b/backend/notification/notifications.master.ts index 4590417..048c802 100644 --- a/backend/notification/notifications.master.ts +++ b/backend/notification/notifications.master.ts @@ -14,7 +14,7 @@ const note: NewNotification[] = [ "Monitors the labels that are printed and returns a there data, if one falls withing the time frame.", active: false, interval: "10", - options: [{ prodID: 1 }], + options: [{ auditId: [0] }], }, ]; diff --git a/backend/prodSql/queries/reprintLabels.sql b/backend/prodSql/queries/reprintLabels.sql new file mode 100644 index 0000000..5d52095 --- /dev/null +++ b/backend/prodSql/queries/reprintLabels.sql @@ -0,0 +1,28 @@ +use [test1_AlplaPROD2.0_Read] + +SELECT + --JSON_VALUE(content, '$.EntityId') as labelId + a.id + ,ActorName + ,FORMAT(PrintDate, 'yyyy-MM-dd HH:mm') as printDate + ,FORMAT(CreatedDateTime, 'yyyy-MM-dd HH:mm') createdDateTime + ,l.ArticleHumanReadableId as av + ,l.ArticleDescription as alias + ,PrintedCopies + ,p.name as printerName + ,RunningNumber + --,* +FROM [support].[AuditLog] (nolock) as a + +left join + [labelling].[InternalLabel] (nolock) as l on + l.id = JSON_VALUE(content, '$.EntityId') + +left join + [masterData].[printer] (nolock) as p on + p.id = l.PrinterId + +where message like '%reprint%' + and CreatedDateTime > DATEADD(minute, -[intervalCheck], SYSDATETIMEOFFSET()) + and a.id > [ignoreList] +order by CreatedDateTime desc \ No newline at end of file diff --git a/backend/utils/mailViews/reprintLabels.hbs b/backend/utils/mailViews/reprintLabels.hbs new file mode 100644 index 0000000..891baf6 --- /dev/null +++ b/backend/utils/mailViews/reprintLabels.hbs @@ -0,0 +1,47 @@ + + + + + + {{!-- --}} + {{> styles}} + + +

All,

+

The below labels have been reprinted.

+ + + + + + + + + + + + + + + + {{#each items}} + + + + + + + + + + + {{/each}} + +
AVDescriptionLabel NumberDate AddedDate ReprintedWho printed/UpdatedWhat printer it came from
{{av}}{{alias}}{{RunningNumber}}{{printDate}}{{createdDateTime}}{{ActorName}}{{printerName}}
+ +
+

Thank you,

+

LST Team

+
+ + \ No newline at end of file diff --git a/backend/utils/returnHelper.utils.ts b/backend/utils/returnHelper.utils.ts index 51a80fd..194ba84 100644 --- a/backend/utils/returnHelper.utils.ts +++ b/backend/utils/returnHelper.utils.ts @@ -10,7 +10,8 @@ interface Data { | "datamart" | "utils" | "opendock" - | "notification"; + | "notification" + | "email"; subModule: | "db" | "labeling" diff --git a/frontend/src/routes/admin/notifications.tsx b/frontend/src/routes/admin/notifications.tsx index 8758112..8f0c69f 100644 --- a/frontend/src/routes/admin/notifications.tsx +++ b/frontend/src/routes/admin/notifications.tsx @@ -80,7 +80,7 @@ export const Route = createFileRoute("/admin/notifications")({ component: RouteComponent, }); -function RouteComponent() { +const NotificationTable = () => { const { data, refetch } = useSuspenseQuery(notifications()); const { data: subs, refetch: subRefetch } = useSuspenseQuery( notificationSubs(), @@ -281,6 +281,19 @@ function RouteComponent() { }), ]; + return ( + <> + + + + + + + + ); +}; + +function RouteComponent() { return (
@@ -301,12 +314,7 @@ function RouteComponent() { Subscriptions }> - - - - - - + diff --git a/frontend/src/routes/admin/settings.tsx b/frontend/src/routes/admin/settings.tsx index 92d4eb0..d6745a9 100644 --- a/frontend/src/routes/admin/settings.tsx +++ b/frontend/src/routes/admin/settings.tsx @@ -46,7 +46,7 @@ const updateSettings = async ( id: string, data: Record, ) => { - console.log(id, data); + //console.log(id, data); try { const res = await axios.patch(`/lst/api/settings/${id}`, data, { withCredentials: true,