From d6fbf02092a86a5011fa44f51099288007e5f995 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Thu, 9 Jan 2025 11:22:08 -0800 Subject: [PATCH 01/12] IO-3076 Initial usage reports design. --- server.js | 3 +- server/data/data.js | 1 + server/data/usageReport.js | 84 ++++++++++++++++++++++++++++++++ server/graphql-client/queries.js | 73 +++++++++++++++++++++++++++ server/routes/dataRoutes.js | 3 +- server/tasks/tasks.js | 2 +- 6 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 server/data/usageReport.js diff --git a/server.js b/server.js index 5327d2998..ef480a7ea 100644 --- a/server.js +++ b/server.js @@ -329,7 +329,8 @@ const main = async () => { main().catch((error) => { logger.log(`Main-API-Error: Something was not caught in the application.`, "error", "api", null, { error: error.message, - errorjson: JSON.stringify(error) + errorjson: JSON.stringify(error), + stack: error.stack }); // Note: If we want the app to crash on all uncaught async operations, we would // need to put a `process.exit(1);` here diff --git a/server/data/data.js b/server/data/data.js index 0f6fcd30c..bc79ef9a3 100644 --- a/server/data/data.js +++ b/server/data/data.js @@ -3,3 +3,4 @@ exports.autohouse = require("./autohouse").default; exports.chatter = require("./chatter").default; exports.claimscorp = require("./claimscorp").default; exports.kaizen = require("./kaizen").default; +exports.usageReport = require("./usageReport").default; \ No newline at end of file diff --git a/server/data/usageReport.js b/server/data/usageReport.js new file mode 100644 index 000000000..6c3734826 --- /dev/null +++ b/server/data/usageReport.js @@ -0,0 +1,84 @@ +const path = require("path"); +require("dotenv").config({ + path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`) +}); +const client = require("../graphql-client/graphql-client").client; +const emailer = require("../email/sendemail"); +const moment = require("moment-timezone"); +const converter = require("json-2-csv"); +const logger = require("../utils/logger"); +const queries = require("../graphql-client/queries"); + +exports.default = async (req, res) => { + try { + logger.log("usage-report-email-start", "debug", req?.user?.email, null, {}); + if (process.env.NODE_ENV !== "production") { + res.sendStatus(403); + return; + } + // Only process if the appropriate token is provided. + if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) { + res.sendStatus(401); + logger.log("usage-report-email-forbidden", "warn", req?.user?.email, null, {}); + return; + } + + //Query the elements. + const queryResults = await client.request(queries.STATUS_UPDATE, { + today: moment().startOf("day").subtract(3, "days"), + period: moment().subtract(90, "days").startOf("day") + }); + + //Massage the data. + const shopList = []; + queryResults.bodyshops.forEach((shop) => { + shopList.push({ + "Shop Name": shop.shopname, + "Days Since Creation": moment().diff(moment(shop.created_at), "days"), + "Jobs Created": shop.jobs_created.aggregate.count, + "Jobs Updated": shop.jobs_updated.aggregate.count, + "Owners Created": shop.owners_created.aggregate.count, + "Owners Updated": shop.owners_updated.aggregate.count, + "Vehicles Created": shop.vehicles_created.aggregate.count, + "Vehicles Updated": shop.vehicles_updated.aggregate.count, + "Tasks Created": shop.tasks_created.aggregate.count, + "Tasks Updated": shop.tasks_updated.aggregate.count + }); + }); + + const csv = converter.json2csv(shopList, { emptyFieldValue: "" }); + emailer + .sendTaskEmail({ + to: ["patrick.fic@convenient-brands.com"], + subject: `RO Usage Report - ${moment().format("MM/DD/YYYY")}`, + text: ` +Usage Report for ${moment().format("MM/DD/YYYY")} for Rome Online Customers. + +Notes: + - Days Since Creation: The number of days since the shop was created. Only shops created in the last 90 days are included. + - Updated values should be higher than created values. + - Counts are inclusive of the last 3 days of data. + `, + attachments: [{ filename: `RO Usage Report ${moment().format("MM/DD/YYYY")}.csv`, content: csv }] + }) + .then(() => { + logger.log("usage-report-email-success", "debug", req?.user?.email, null, { + csv + }); + }) + .catch((error) => { + logger.log("usage-report-email-send-error", "ERROR", req?.user?.email, null, { + error: error.message, + stack: error.stack + }); + }); + + return res.sendStatus(200); + } catch (error) { + logger.log("usage-report-email-error", "ERROR", req?.user?.email, null, { + error: error.message, + stack: error.stack + }); + res.status(500).json({ error: error.message, stack: error.stack }); + } +}; diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index d585a4255..e0a1a4981 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -2617,3 +2617,76 @@ exports.CREATE_CONVERSATION = `mutation CREATE_CONVERSATION($conversation: [conv } } `; + + +exports.STATUS_UPDATE = `query STATUS_UPDATE($period: timestamptz!, $today: timestamptz!) { + bodyshops(where: { created_at: { _gte: $period } }) { + shopname + id + created_at + jobs_created: jobs_aggregate(where: { created_at: { _gte: $today } }) { + aggregate { + count + } + } + jobs_updated: jobs_aggregate(where: { updated_at: { _gte: $today } }) { + aggregate { + count + } + } + + owners_created: owners_aggregate(where: { created_at: { _gte: $today } }) { + aggregate { + count + } + } + owners_updated: owners_aggregate(where: { updated_at: { _gte: $today } }) { + aggregate { + count + } + } + + vehicles_created: vehicles_aggregate( + where: { created_at: { _gte: $today } } + ) { + aggregate { + count + } + } + vehicles_updated: vehicles_aggregate( + where: { updated_at: { _gte: $today } } + ) { + aggregate { + count + } + } + + tasks_created: tasks_aggregate(where: { created_at: { _gte: $today } }) { + aggregate { + count + } + } + tasks_updated: tasks_aggregate(where: { updated_at: { _gte: $today } }) { + aggregate { + count + } + } + jobs { + parts_orders_created: parts_orders_aggregate( + where: { created_at: { _gte: $today } } + ) { + aggregate { + count + } + } + parts_orders_updated: parts_orders_aggregate( + where: { updated_at: { _gte: $today } } + ) { + aggregate { + count + } + } + } + } +} +` \ No newline at end of file diff --git a/server/routes/dataRoutes.js b/server/routes/dataRoutes.js index a12563282..788574074 100644 --- a/server/routes/dataRoutes.js +++ b/server/routes/dataRoutes.js @@ -1,10 +1,11 @@ const express = require("express"); const router = express.Router(); -const { autohouse, claimscorp, chatter, kaizen } = require("../data/data"); +const { autohouse, claimscorp, chatter, kaizen, usageReport } = require("../data/data"); router.post("/ah", autohouse); router.post("/cc", claimscorp); router.post("/chatter", chatter); router.post("/kaizen", kaizen); +router.post("/usagereport", usageReport); module.exports = router; diff --git a/server/tasks/tasks.js b/server/tasks/tasks.js index 2ae944e60..f823d13c9 100644 --- a/server/tasks/tasks.js +++ b/server/tasks/tasks.js @@ -41,7 +41,7 @@ exports.taskHandler = async (req, res) => { return res.status(200).send(csv); } catch (error) { - res.status(500).json({ error: error.message, stack: error.stackTrace }); + res.status(500).json({ error: error.message, stack: error.stack }); } }; From e2e5f3f8856ce43089440cc1aceaaee399a243af Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Thu, 9 Jan 2025 13:28:56 -0800 Subject: [PATCH 02/12] feature/IO-3060-Realtime-Notifications-System - Add handlers for Job and Bill Change / Register Routers - Add Tables / Modify Tables / Add permissions. --- hasura/metadata/tables.yaml | 165 ++++++++++++++++++ .../down.sql | 4 + .../up.sql | 2 + .../down.sql | 1 + .../up.sql | 2 + .../down.sql | 3 + .../up.sql | 1 + .../down.sql | 4 + .../up.sql | 2 + .../down.sql | 1 + .../up.sql | 2 + .../eventHandlers/handeJobChange.js | 5 + .../eventHandlers/handleBillChange.js | 5 + server/routes/notificationsRoutes.js | 15 +- 14 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 hasura/migrations/1736452866094_alter_table_public_associations_add_column_notification_settings/down.sql create mode 100644 hasura/migrations/1736452866094_alter_table_public_associations_add_column_notification_settings/up.sql create mode 100644 hasura/migrations/1736455300329_create_table_public_notifications/down.sql create mode 100644 hasura/migrations/1736455300329_create_table_public_notifications/up.sql create mode 100644 hasura/migrations/1736457116827_alter_table_public_notifications_drop_column_scenario/down.sql create mode 100644 hasura/migrations/1736457116827_alter_table_public_notifications_drop_column_scenario/up.sql create mode 100644 hasura/migrations/1736457173242_alter_table_public_notifications_add_column_scenario/down.sql create mode 100644 hasura/migrations/1736457173242_alter_table_public_notifications_add_column_scenario/up.sql create mode 100644 hasura/migrations/1736457615046_create_table_public_job_watchers/down.sql create mode 100644 hasura/migrations/1736457615046_create_table_public_job_watchers/up.sql create mode 100644 server/notifications/eventHandlers/handeJobChange.js create mode 100644 server/notifications/eventHandlers/handleBillChange.js diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index 240b218a4..d7ddeb768 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -207,6 +207,7 @@ - default_prod_list_view - id - kanban_settings + - notification_settings - qbo_realmId - shopid - useremail @@ -222,6 +223,7 @@ - authlevel - default_prod_list_view - kanban_settings + - notification_settings - qbo_realmId filter: user: @@ -2805,6 +2807,80 @@ _eq: X-Hasura-User-Id - active: _eq: true +- table: + name: job_watchers + schema: public + object_relationships: + - name: job + using: + foreign_key_constraint_on: job_id + - name: user + using: + foreign_key_constraint_on: user_email + insert_permissions: + - role: user + permission: + check: + user: + _and: + - associations: + active: + _eq: true + - authid: + _eq: X-Hasura-User-Id + columns: + - user_email + - created_at + - id + - job_id + comment: "" + select_permissions: + - role: user + permission: + columns: + - user_email + - created_at + - id + - job_id + filter: + user: + _and: + - associations: + active: + _eq: true + - authid: + _eq: X-Hasura-User-Id + comment: "" + update_permissions: + - role: user + permission: + columns: + - user_email + - created_at + - id + - job_id + filter: + user: + _and: + - associations: + active: + _eq: true + - authid: + _eq: X-Hasura-User-Id + check: null + comment: "" + delete_permissions: + - role: user + permission: + filter: + user: + _and: + - associations: + active: + _eq: true + - authid: + _eq: X-Hasura-User-Id + comment: "" - table: name: joblines schema: public @@ -4669,6 +4745,95 @@ _eq: X-Hasura-User-Id - active: _eq: true +- table: + name: notifications + schema: public + object_relationships: + - name: association + using: + foreign_key_constraint_on: association_id + - name: job + using: + foreign_key_constraint_on: job_id + insert_permissions: + - role: user + permission: + check: + association: + _and: + - active: + _eq: true + - user: + authid: + _eq: X-Hasura-User-Id + columns: + - association_id + - created_at + - fcm_data + - fcm_message + - fcm_title + - id + - job_id + - meta + - read + - scenario + - ui_translation_meta + - ui_translation_string + - updated_at + comment: "" + select_permissions: + - role: user + permission: + columns: + - association_id + - created_at + - fcm_data + - fcm_message + - fcm_title + - id + - job_id + - meta + - read + - scenario + - ui_translation_meta + - ui_translation_string + - updated_at + filter: + association: + _and: + - active: + _eq: true + - user: + authid: + _eq: X-Hasura-User-Id + comment: "" + update_permissions: + - role: user + permission: + columns: + - association_id + - created_at + - fcm_data + - fcm_message + - fcm_title + - id + - job_id + - meta + - read + - scenario + - ui_translation_meta + - ui_translation_string + - updated_at + filter: + association: + _and: + - active: + _eq: true + - user: + authid: + _eq: X-Hasura-User-Id + check: null + comment: "" - table: name: owners schema: public diff --git a/hasura/migrations/1736452866094_alter_table_public_associations_add_column_notification_settings/down.sql b/hasura/migrations/1736452866094_alter_table_public_associations_add_column_notification_settings/down.sql new file mode 100644 index 000000000..4e2b8ebd3 --- /dev/null +++ b/hasura/migrations/1736452866094_alter_table_public_associations_add_column_notification_settings/down.sql @@ -0,0 +1,4 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- alter table "public"."associations" add column "notification_settings" jsonb +-- null; diff --git a/hasura/migrations/1736452866094_alter_table_public_associations_add_column_notification_settings/up.sql b/hasura/migrations/1736452866094_alter_table_public_associations_add_column_notification_settings/up.sql new file mode 100644 index 000000000..da1bac389 --- /dev/null +++ b/hasura/migrations/1736452866094_alter_table_public_associations_add_column_notification_settings/up.sql @@ -0,0 +1,2 @@ +alter table "public"."associations" add column "notification_settings" jsonb + null; diff --git a/hasura/migrations/1736455300329_create_table_public_notifications/down.sql b/hasura/migrations/1736455300329_create_table_public_notifications/down.sql new file mode 100644 index 000000000..6fc4e3068 --- /dev/null +++ b/hasura/migrations/1736455300329_create_table_public_notifications/down.sql @@ -0,0 +1 @@ +DROP TABLE "public"."notifications"; diff --git a/hasura/migrations/1736455300329_create_table_public_notifications/up.sql b/hasura/migrations/1736455300329_create_table_public_notifications/up.sql new file mode 100644 index 000000000..20c46499b --- /dev/null +++ b/hasura/migrations/1736455300329_create_table_public_notifications/up.sql @@ -0,0 +1,2 @@ +CREATE TABLE "public"."notifications" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "job_id" uuid NOT NULL, "association_id" uuid NOT NULL, "ui_translation_string" text NOT NULL, "ui_translation_meta" jsonb, "fcm_title" text NOT NULL, "fcm_message" text NOT NULL, "fcm_data" jsonb, "read" timestamptz, "meta" jsonb, "scenario" Integer NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("job_id") REFERENCES "public"."jobs"("id") ON UPDATE restrict ON DELETE restrict, FOREIGN KEY ("association_id") REFERENCES "public"."associations"("id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"));COMMENT ON TABLE "public"."notifications" IS E'Real Time Notifications System'; +CREATE EXTENSION IF NOT EXISTS pgcrypto; diff --git a/hasura/migrations/1736457116827_alter_table_public_notifications_drop_column_scenario/down.sql b/hasura/migrations/1736457116827_alter_table_public_notifications_drop_column_scenario/down.sql new file mode 100644 index 000000000..38a88b4df --- /dev/null +++ b/hasura/migrations/1736457116827_alter_table_public_notifications_drop_column_scenario/down.sql @@ -0,0 +1,3 @@ +comment on column "public"."notifications"."scenario" is E'Real Time Notifications System'; +alter table "public"."notifications" alter column "scenario" drop not null; +alter table "public"."notifications" add column "scenario" int4; diff --git a/hasura/migrations/1736457116827_alter_table_public_notifications_drop_column_scenario/up.sql b/hasura/migrations/1736457116827_alter_table_public_notifications_drop_column_scenario/up.sql new file mode 100644 index 000000000..19d298dc1 --- /dev/null +++ b/hasura/migrations/1736457116827_alter_table_public_notifications_drop_column_scenario/up.sql @@ -0,0 +1 @@ +alter table "public"."notifications" drop column "scenario" cascade; diff --git a/hasura/migrations/1736457173242_alter_table_public_notifications_add_column_scenario/down.sql b/hasura/migrations/1736457173242_alter_table_public_notifications_add_column_scenario/down.sql new file mode 100644 index 000000000..88746e6fc --- /dev/null +++ b/hasura/migrations/1736457173242_alter_table_public_notifications_add_column_scenario/down.sql @@ -0,0 +1,4 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- alter table "public"."notifications" add column "scenario" text +-- not null; diff --git a/hasura/migrations/1736457173242_alter_table_public_notifications_add_column_scenario/up.sql b/hasura/migrations/1736457173242_alter_table_public_notifications_add_column_scenario/up.sql new file mode 100644 index 000000000..7f26cc332 --- /dev/null +++ b/hasura/migrations/1736457173242_alter_table_public_notifications_add_column_scenario/up.sql @@ -0,0 +1,2 @@ +alter table "public"."notifications" add column "scenario" text + not null; diff --git a/hasura/migrations/1736457615046_create_table_public_job_watchers/down.sql b/hasura/migrations/1736457615046_create_table_public_job_watchers/down.sql new file mode 100644 index 000000000..b718e9f8f --- /dev/null +++ b/hasura/migrations/1736457615046_create_table_public_job_watchers/down.sql @@ -0,0 +1 @@ +DROP TABLE "public"."job_watchers"; diff --git a/hasura/migrations/1736457615046_create_table_public_job_watchers/up.sql b/hasura/migrations/1736457615046_create_table_public_job_watchers/up.sql new file mode 100644 index 000000000..1debac3c0 --- /dev/null +++ b/hasura/migrations/1736457615046_create_table_public_job_watchers/up.sql @@ -0,0 +1,2 @@ +CREATE TABLE "public"."job_watchers" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "job_id" uuid NOT NULL, "user_email" text NOT NULL, "created_at" timestamptz NOT NULL DEFAULT now(), PRIMARY KEY ("id") , FOREIGN KEY ("user_email") REFERENCES "public"."users"("email") ON UPDATE restrict ON DELETE restrict, FOREIGN KEY ("job_id") REFERENCES "public"."jobs"("id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"));COMMENT ON TABLE "public"."job_watchers" IS E'Job Watchers'; +CREATE EXTENSION IF NOT EXISTS pgcrypto; diff --git a/server/notifications/eventHandlers/handeJobChange.js b/server/notifications/eventHandlers/handeJobChange.js new file mode 100644 index 000000000..db38d5e87 --- /dev/null +++ b/server/notifications/eventHandlers/handeJobChange.js @@ -0,0 +1,5 @@ +const handleJobChange = (req, res) => { + return res.status(200).json({ message: "Job change handled." }); +}; + +module.exports = handleJobChange; diff --git a/server/notifications/eventHandlers/handleBillChange.js b/server/notifications/eventHandlers/handleBillChange.js new file mode 100644 index 000000000..fcabafe85 --- /dev/null +++ b/server/notifications/eventHandlers/handleBillChange.js @@ -0,0 +1,5 @@ +const handleBillChange = (req, res) => { + return res.status(200).json({ message: "Bill change handled." }); +}; + +module.exports = handleBillChange; diff --git a/server/routes/notificationsRoutes.js b/server/routes/notificationsRoutes.js index cd31b7172..235d71e7f 100644 --- a/server/routes/notificationsRoutes.js +++ b/server/routes/notificationsRoutes.js @@ -1,12 +1,19 @@ const express = require("express"); const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware"); const { subscribe, unsubscribe, sendNotification } = require("../firebase/firebase-handler"); +const eventAuthorizationMiddleware = require("../middleware/eventAuthorizationMIddleware"); +const handleJobChange = require("../notifications/eventHandlers/handeJobChange"); +const handleBillChange = require("../notifications/eventHandlers/handleBillChange"); + const router = express.Router(); -router.use(validateFirebaseIdTokenMiddleware); +// These are FCM handlers +router.post("/subscribe", validateFirebaseIdTokenMiddleware, subscribe); +router.post("/unsubscribe", validateFirebaseIdTokenMiddleware, unsubscribe); +router.post("/sendtestnotification", validateFirebaseIdTokenMiddleware, sendNotification); -router.post("/subscribe", subscribe); -router.post("/unsubscribe", unsubscribe); -router.post("/sendtestnotification", sendNotification); +// Hasura Entry points for creating notifications +router.post("/events/handleJobChange", eventAuthorizationMiddleware, handleJobChange); +router.post("/events/handleBillChange", eventAuthorizationMiddleware, handleBillChange); module.exports = router; From 80379cdd791241450f438fd6d1c467fdb5f8db0d Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Thu, 9 Jan 2025 17:37:30 -0800 Subject: [PATCH 03/12] IO-3082 Additional IO Basic Lock Out Reports Signed-off-by: Allan Carr --- client/src/utils/TemplateConstants.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/client/src/utils/TemplateConstants.js b/client/src/utils/TemplateConstants.js index 9c282dc6c..42241d152 100644 --- a/client/src/utils/TemplateConstants.js +++ b/client/src/utils/TemplateConstants.js @@ -1444,7 +1444,8 @@ export const TemplateList = (type, context) => { object: i18n.t("reportcenter.labels.objects.jobs"), field: i18n.t("jobs.fields.date_exported") }, - group: "sales" + group: "sales", + featureNameRestricted: "export" }, gsr_by_estimator: { title: i18n.t("reportcenter.templates.gsr_by_estimator"), @@ -1865,7 +1866,8 @@ export const TemplateList = (type, context) => { object: i18n.t("reportcenter.labels.objects.jobs"), field: i18n.t("jobs.fields.date_open") }, - group: "jobs" + group: "jobs", + featureNameRestricted: "bills" }, psr_by_make: { title: i18n.t("reportcenter.templates.psr_by_make"), @@ -1901,7 +1903,8 @@ export const TemplateList = (type, context) => { object: i18n.t("reportcenter.labels.objects.parts_orders"), field: i18n.t("parts_orders.fields.order_date") }, - group: "jobs" + group: "jobs", + featureNameRestricted: "bills" }, returns_grouped_by_vendor_detailed: { title: i18n.t("reportcenter.templates.returns_grouped_by_vendor_detailed"), @@ -1913,7 +1916,8 @@ export const TemplateList = (type, context) => { object: i18n.t("reportcenter.labels.objects.parts_orders"), field: i18n.t("parts_orders.fields.order_date") }, - group: "jobs" + group: "jobs", + featureNameRestricted: "bills" }, scheduled_parts_list: { title: i18n.t("reportcenter.templates.scheduled_parts_list"), From c1b3df9c3bd04b1e0506f63a7da22a41aae069e5 Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Thu, 9 Jan 2025 17:43:43 -0800 Subject: [PATCH 04/12] IO-3080 Restrict Claimable Hours Label Signed-off-by: Allan Carr --- client/src/translations/en_us/common.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index f153a242a..7f82f69f1 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -636,7 +636,7 @@ "target_touchtime": "Target Touch Time", "timezone": "Timezone", "tt_allow_post_to_invoiced": "Allow Time Tickets to be posted to Invoiced & Exported Jobs", - "tt_enforce_hours_for_tech_console": "Restrict Claimable hours from Tech Console", + "tt_enforce_hours_for_tech_console": "Restrict Claimable Hours", "use_fippa": "Conceal Customer Information on Generated Documents?", "use_paint_scale_data": "Use Paint Scale Data for Job Costing?", "uselocalmediaserver": "Use Local Media Server?", From 779cc7d9e8e363262b68a8fcc310da5c53314609 Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Thu, 9 Jan 2025 18:57:21 -0800 Subject: [PATCH 05/12] IO-3078 Jobs Presets Company Setup Markup Discount Signed-off-by: Allan Carr --- client/src/components/shop-info/shop-info.general.component.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/shop-info/shop-info.general.component.jsx b/client/src/components/shop-info/shop-info.general.component.jsx index 90a17b4b1..19f1ecb9e 100644 --- a/client/src/components/shop-info/shop-info.general.component.jsx +++ b/client/src/components/shop-info/shop-info.general.component.jsx @@ -1261,7 +1261,7 @@ export function ShopInfoGeneral({ form, bodyshop }) { key={`${index}prt_dsmk_p`} name={[field.name, "prt_dsmk_p"]} > - + Date: Thu, 9 Jan 2025 19:16:45 -0800 Subject: [PATCH 06/12] IO-3022 Export Payments - Payment Method Signed-off-by: Allan Carr --- .../accounting-payments-table.component.jsx | 11 +++++++++++ client/src/graphql/accounting.queries.js | 1 + 2 files changed, 12 insertions(+) diff --git a/client/src/components/accounting-payments-table/accounting-payments-table.component.jsx b/client/src/components/accounting-payments-table/accounting-payments-table.component.jsx index f738cd0ea..edaa05187 100644 --- a/client/src/components/accounting-payments-table/accounting-payments-table.component.jsx +++ b/client/src/components/accounting-payments-table/accounting-payments-table.component.jsx @@ -85,6 +85,17 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, payments, sortOrder: state.sortedInfo.columnKey === "amount" && state.sortedInfo.order, render: (text, record) => {record.amount} }, + { + title: t("payments.fields.type"), + dataIndex: "type", + key: "type", + sorter: (a, b) => a.type.localeCompare(b.type), + sortOrder: state.sortedInfo.columnKey === "type" && state.sortedInfo.order, + filters: bodyshop.md_payment_types.map((s) => { + return { text: s, value: [s] }; + }), + onFilter: (value, record) => value.includes(record.type) + }, { title: t("payments.fields.memo"), dataIndex: "memo", diff --git a/client/src/graphql/accounting.queries.js b/client/src/graphql/accounting.queries.js index 930df6cf1..88aacedbe 100644 --- a/client/src/graphql/accounting.queries.js +++ b/client/src/graphql/accounting.queries.js @@ -73,6 +73,7 @@ export const QUERY_PAYMENTS_FOR_EXPORT = gql` transactionid paymentnum date + type exportlogs { id successful From c7f293cecac8fd5f18ddbfbb4dfe1ac6041e8b35 Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Thu, 9 Jan 2025 19:28:28 -0800 Subject: [PATCH 07/12] IO-1927 Export Button as Primary Signed-off-by: Allan Carr --- .../jobs-close-export-button.component.jsx | 4 ++-- .../jobs-export-all-button.component.jsx | 10 +++++----- .../payable-export-all-button.component.jsx | 12 ++++++------ .../payable-export-button.component.jsx | 10 +++++----- .../payment-export-button.component.jsx | 10 +++++----- .../payments-export-all-button.component.jsx | 12 ++++++------ 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/client/src/components/jobs-close-export-button/jobs-close-export-button.component.jsx b/client/src/components/jobs-close-export-button/jobs-close-export-button.component.jsx index e84b31595..f22da1eab 100644 --- a/client/src/components/jobs-close-export-button/jobs-close-export-button.component.jsx +++ b/client/src/components/jobs-close-export-button/jobs-close-export-button.component.jsx @@ -173,7 +173,7 @@ export function JobsCloseExportButton({ } }); - if (!!!jobUpdateResponse.errors) { + if (!jobUpdateResponse.errors) { notification.open({ type: "success", key: "jobsuccessexport", @@ -222,7 +222,7 @@ export function JobsCloseExportButton({ }; return ( - ); diff --git a/client/src/components/jobs-export-all-button/jobs-export-all-button.component.jsx b/client/src/components/jobs-export-all-button/jobs-export-all-button.component.jsx index e149f39f0..f9670dc77 100644 --- a/client/src/components/jobs-export-all-button/jobs-export-all-button.component.jsx +++ b/client/src/components/jobs-export-all-button/jobs-export-all-button.component.jsx @@ -10,8 +10,8 @@ import { auth, logImEXEvent } from "../../firebase/firebase.utils"; import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; import { UPDATE_JOBS } from "../../graphql/jobs.queries"; import { insertAuditTrail } from "../../redux/application/application.actions"; -import AuditTrailMapping from "../../utils/AuditTrailMappings"; import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors"; +import AuditTrailMapping from "../../utils/AuditTrailMappings"; import client from "../../utils/GraphQLClient"; const mapStateToProps = createStructuredSelector({ @@ -165,7 +165,7 @@ export function JobsExportAllButton({ } }); - if (!!!jobUpdateResponse.errors) { + if (!jobUpdateResponse.errors) { notification.open({ type: "success", key: "jobsuccessexport", @@ -213,13 +213,13 @@ export function JobsExportAllButton({ }) ); - if (!!completedCallback) completedCallback([]); - if (!!loadingCallback) loadingCallback(false); + if (completedCallback) completedCallback([]); + if (loadingCallback) loadingCallback(false); setLoading(false); }; return ( - ); diff --git a/client/src/components/payable-export-all-button/payable-export-all-button.component.jsx b/client/src/components/payable-export-all-button/payable-export-all-button.component.jsx index dc8316be8..6e2d2b5ed 100644 --- a/client/src/components/payable-export-all-button/payable-export-all-button.component.jsx +++ b/client/src/components/payable-export-all-button/payable-export-all-button.component.jsx @@ -48,7 +48,7 @@ export function PayableExportAll({ let PartnerResponse; setLoading(true); - if (!!loadingCallback) loadingCallback(true); + if (loadingCallback) loadingCallback(true); if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) { PartnerResponse = await axios.post(`/qbo/payables`, { bills: billids, @@ -85,7 +85,7 @@ export function PayableExportAll({ notification["error"]({ message: t("bills.errors.exporting-partner") }); - if (!!loadingCallback) loadingCallback(false); + if (loadingCallback) loadingCallback(false); setLoading(false); return; } @@ -152,7 +152,7 @@ export function PayableExportAll({ } } }); - if (!!!billUpdateResponse.errors) { + if (!billUpdateResponse.errors) { notification.open({ type: "success", key: "billsuccessexport", @@ -187,8 +187,8 @@ export function PayableExportAll({ }); await Promise.all(proms); - if (!!completedCallback) completedCallback([]); - if (!!loadingCallback) loadingCallback(false); + if (completedCallback) completedCallback([]); + if (loadingCallback) loadingCallback(false); setLoading(false); }; @@ -200,7 +200,7 @@ export function PayableExportAll({ ); return ( - ); diff --git a/client/src/components/payable-export-button/payable-export-button.component.jsx b/client/src/components/payable-export-button/payable-export-button.component.jsx index 3249da15d..f369b031c 100644 --- a/client/src/components/payable-export-button/payable-export-button.component.jsx +++ b/client/src/components/payable-export-button/payable-export-button.component.jsx @@ -46,7 +46,7 @@ export function PayableExportButton({ logImEXEvent("accounting_export_payable"); setLoading(true); - if (!!loadingCallback) loadingCallback(true); + if (loadingCallback) loadingCallback(true); //Check if it's a QBO Setup. let PartnerResponse; @@ -88,7 +88,7 @@ export function PayableExportButton({ notification["error"]({ message: t("bills.errors.exporting-partner") }); - if (!!loadingCallback) loadingCallback(false); + if (loadingCallback) loadingCallback(false); setLoading(false); return; } @@ -149,7 +149,7 @@ export function PayableExportButton({ } } }); - if (!!!billUpdateResponse.errors) { + if (!billUpdateResponse.errors) { notification.open({ type: "success", key: "billsuccessexport", @@ -186,7 +186,7 @@ export function PayableExportButton({ } } - if (!!loadingCallback) loadingCallback(false); + if (loadingCallback) loadingCallback(false); setLoading(false); }; @@ -198,7 +198,7 @@ export function PayableExportButton({ ); return ( - ); diff --git a/client/src/components/payment-export-button/payment-export-button.component.jsx b/client/src/components/payment-export-button/payment-export-button.component.jsx index 876dc5af7..35937da58 100644 --- a/client/src/components/payment-export-button/payment-export-button.component.jsx +++ b/client/src/components/payment-export-button/payment-export-button.component.jsx @@ -55,7 +55,7 @@ export function PaymentExportButton({ } else { //Default is QBD - if (!!loadingCallback) loadingCallback(true); + if (loadingCallback) loadingCallback(true); let QbXmlResponse; try { @@ -88,7 +88,7 @@ export function PaymentExportButton({ notification["error"]({ message: t("payments.errors.exporting-partner") }); - if (!!loadingCallback) loadingCallback(false); + if (loadingCallback) loadingCallback(false); setLoading(false); return; } @@ -148,7 +148,7 @@ export function PaymentExportButton({ } } }); - if (!!!paymentUpdateResponse.errors) { + if (!paymentUpdateResponse.errors) { notification.open({ type: "success", key: "paymentsuccessexport", @@ -184,12 +184,12 @@ export function PaymentExportButton({ ) ]); } - if (!!loadingCallback) loadingCallback(false); + if (loadingCallback) loadingCallback(false); setLoading(false); }; return ( - ); diff --git a/client/src/components/payments-export-all-button/payments-export-all-button.component.jsx b/client/src/components/payments-export-all-button/payments-export-all-button.component.jsx index 5c4246890..46131daae 100644 --- a/client/src/components/payments-export-all-button/payments-export-all-button.component.jsx +++ b/client/src/components/payments-export-all-button/payments-export-all-button.component.jsx @@ -44,7 +44,7 @@ export function PaymentsExportAllButton({ const handleQbxml = async () => { setLoading(true); - if (!!loadingCallback) loadingCallback(true); + if (loadingCallback) loadingCallback(true); let PartnerResponse; if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) { PartnerResponse = await axios.post(`/qbo/payments`, { @@ -76,7 +76,7 @@ export function PaymentsExportAllButton({ notification["error"]({ message: t("payments.errors.exporting-partner") }); - if (!!loadingCallback) loadingCallback(false); + if (loadingCallback) loadingCallback(false); setLoading(false); return; } @@ -140,7 +140,7 @@ export function PaymentsExportAllButton({ } } }); - if (!!!paymentUpdateResponse.errors) { + if (!paymentUpdateResponse.errors) { notification.open({ type: "success", key: "paymentsuccessexport", @@ -174,13 +174,13 @@ export function PaymentsExportAllButton({ ); }); await Promise.all(proms); - if (!!completedCallback) completedCallback([]); - if (!!loadingCallback) loadingCallback(false); + if (completedCallback) completedCallback([]); + if (loadingCallback) loadingCallback(false); setLoading(false); }; return ( - ); From 3bc6504ae68cb5cd6b67cacb10a8b5877c76424d Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Fri, 10 Jan 2025 09:33:47 -0800 Subject: [PATCH 08/12] feature/IO-3060-Realtime-Notifications-Progress Update --- hasura/metadata/tables.yaml | 25 ++++++++++++++++--- .../down.sql | 3 +++ .../up.sql | 1 + .../handlePartsDispatchChange.js | 5 ++++ .../eventHandlers/handlePartsOrderChange.js | 5 ++++ .../eventHandlers/handleTasksChange.js | 5 ++++ .../eventHandlers/handleTimeTicketsChange.js | 5 ++++ server/routes/notificationsRoutes.js | 7 ++++++ 8 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 hasura/migrations/1736522640938_alter_table_public_notifications_drop_column_scenario/down.sql create mode 100644 hasura/migrations/1736522640938_alter_table_public_notifications_drop_column_scenario/up.sql create mode 100644 server/notifications/eventHandlers/handlePartsDispatchChange.js create mode 100644 server/notifications/eventHandlers/handlePartsOrderChange.js create mode 100644 server/notifications/eventHandlers/handleTasksChange.js create mode 100644 server/notifications/eventHandlers/handleTimeTicketsChange.js diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index d7ddeb768..c9b1922ca 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -4776,7 +4776,6 @@ - job_id - meta - read - - scenario - ui_translation_meta - ui_translation_string - updated_at @@ -4794,7 +4793,6 @@ - job_id - meta - read - - scenario - ui_translation_meta - ui_translation_string - updated_at @@ -4820,7 +4818,6 @@ - job_id - meta - read - - scenario - ui_translation_meta - ui_translation_string - updated_at @@ -6200,6 +6197,28 @@ _eq: X-Hasura-User-Id - active: _eq: true + event_triggers: + - name: notifications_time_tickets + definition: + enable_manual: false + insert: + columns: '*' + update: + columns: '*' + retry_conf: + interval_sec: 10 + num_retries: 0 + timeout_sec: 60 + webhook_from_env: HASURA_API_URL + headers: + - name: event-secret + value_from_env: EVENT_SECRET + request_transform: + method: POST + query_params: {} + template_engine: Kriti + url: '{{$base_url}}/events/handleTimeTicketsChange' + version: 2 - table: name: transitions schema: public diff --git a/hasura/migrations/1736522640938_alter_table_public_notifications_drop_column_scenario/down.sql b/hasura/migrations/1736522640938_alter_table_public_notifications_drop_column_scenario/down.sql new file mode 100644 index 000000000..7ef1b7017 --- /dev/null +++ b/hasura/migrations/1736522640938_alter_table_public_notifications_drop_column_scenario/down.sql @@ -0,0 +1,3 @@ +comment on column "public"."notifications"."scenario" is E'Real Time Notifications System'; +alter table "public"."notifications" alter column "scenario" drop not null; +alter table "public"."notifications" add column "scenario" text; diff --git a/hasura/migrations/1736522640938_alter_table_public_notifications_drop_column_scenario/up.sql b/hasura/migrations/1736522640938_alter_table_public_notifications_drop_column_scenario/up.sql new file mode 100644 index 000000000..19d298dc1 --- /dev/null +++ b/hasura/migrations/1736522640938_alter_table_public_notifications_drop_column_scenario/up.sql @@ -0,0 +1 @@ +alter table "public"."notifications" drop column "scenario" cascade; diff --git a/server/notifications/eventHandlers/handlePartsDispatchChange.js b/server/notifications/eventHandlers/handlePartsDispatchChange.js new file mode 100644 index 000000000..fd3f78b97 --- /dev/null +++ b/server/notifications/eventHandlers/handlePartsDispatchChange.js @@ -0,0 +1,5 @@ +const handlePartsDispatchChange = (req, res) => { + return res.status(200).json({ message: "Parts Dispatch change handled." }); +}; + +module.exports = handlePartsDispatchChange; diff --git a/server/notifications/eventHandlers/handlePartsOrderChange.js b/server/notifications/eventHandlers/handlePartsOrderChange.js new file mode 100644 index 000000000..16c09ef2f --- /dev/null +++ b/server/notifications/eventHandlers/handlePartsOrderChange.js @@ -0,0 +1,5 @@ +const handlePartsOrderChange = (req, res) => { + return res.status(200).json({ message: "Parts Order change handled." }); +}; + +module.exports = handlePartsOrderChange; diff --git a/server/notifications/eventHandlers/handleTasksChange.js b/server/notifications/eventHandlers/handleTasksChange.js new file mode 100644 index 000000000..5a6e7f344 --- /dev/null +++ b/server/notifications/eventHandlers/handleTasksChange.js @@ -0,0 +1,5 @@ +const handleTasksChange = (req, res) => { + return res.status(200).json({ message: "Tasks change handled." }); +}; + +module.exports = handleTasksChange; diff --git a/server/notifications/eventHandlers/handleTimeTicketsChange.js b/server/notifications/eventHandlers/handleTimeTicketsChange.js new file mode 100644 index 000000000..403fa4060 --- /dev/null +++ b/server/notifications/eventHandlers/handleTimeTicketsChange.js @@ -0,0 +1,5 @@ +const handleTimeTicketsChange = (req, res) => { + return res.status(200).json({ message: "Time Tickets change handled." }); +}; + +module.exports = handleTimeTicketsChange; diff --git a/server/routes/notificationsRoutes.js b/server/routes/notificationsRoutes.js index 235d71e7f..a459b9a53 100644 --- a/server/routes/notificationsRoutes.js +++ b/server/routes/notificationsRoutes.js @@ -4,6 +4,9 @@ const { subscribe, unsubscribe, sendNotification } = require("../firebase/fireba const eventAuthorizationMiddleware = require("../middleware/eventAuthorizationMIddleware"); const handleJobChange = require("../notifications/eventHandlers/handeJobChange"); const handleBillChange = require("../notifications/eventHandlers/handleBillChange"); +const handlePartsOrderChange = require("../notifications/eventHandlers/handlePartsOrderChange"); +const handlePartsDispatchChange = require("../notifications/eventHandlers/handlePartsDispatchChange"); +const handleTasksChange = require("../notifications/eventHandlers/handleTasksChange"); const router = express.Router(); @@ -15,5 +18,9 @@ router.post("/sendtestnotification", validateFirebaseIdTokenMiddleware, sendNoti // Hasura Entry points for creating notifications router.post("/events/handleJobChange", eventAuthorizationMiddleware, handleJobChange); router.post("/events/handleBillChange", eventAuthorizationMiddleware, handleBillChange); +router.post("/events/handlePartsOrderChange", eventAuthorizationMiddleware, handlePartsOrderChange); +router.post("/events/handlePartsDispatchChange", eventAuthorizationMiddleware, handlePartsDispatchChange); +router.post("/events/handleTasksChange", eventAuthorizationMiddleware, handleTasksChange); +router.post("/events/handleTimeTicketsChange", eventAuthorizationMiddleware, handleTasksChange); module.exports = router; From 2f267a9f2cb9eda3aedd797fb876a50523b5352a Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Fri, 10 Jan 2025 11:39:18 -0800 Subject: [PATCH 09/12] IO-3076 updates to usage report. --- server/data/usageReport.js | 46 +++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/server/data/usageReport.js b/server/data/usageReport.js index 6c3734826..dd7bb4d42 100644 --- a/server/data/usageReport.js +++ b/server/data/usageReport.js @@ -8,48 +8,54 @@ const moment = require("moment-timezone"); const converter = require("json-2-csv"); const logger = require("../utils/logger"); const queries = require("../graphql-client/queries"); +const InstanceMgr = require("../utils/instanceMgr").default; exports.default = async (req, res) => { try { logger.log("usage-report-email-start", "debug", req?.user?.email, null, {}); + + if (InstanceMgr({ rome: false, imex: true })) { + //Disable for ImEX at the moment. + res.sendStatus(403); + logger.log("usage-report-email-forbidden", "warn", req?.user?.email, null, {}); + return; + } + if (process.env.NODE_ENV !== "production") { res.sendStatus(403); return; } - // Only process if the appropriate token is provided. + // Validate using autohouse token header. if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) { res.sendStatus(401); logger.log("usage-report-email-forbidden", "warn", req?.user?.email, null, {}); return; } - //Query the elements. + //Query the usage data. const queryResults = await client.request(queries.STATUS_UPDATE, { today: moment().startOf("day").subtract(3, "days"), period: moment().subtract(90, "days").startOf("day") }); //Massage the data. - const shopList = []; - queryResults.bodyshops.forEach((shop) => { - shopList.push({ - "Shop Name": shop.shopname, - "Days Since Creation": moment().diff(moment(shop.created_at), "days"), - "Jobs Created": shop.jobs_created.aggregate.count, - "Jobs Updated": shop.jobs_updated.aggregate.count, - "Owners Created": shop.owners_created.aggregate.count, - "Owners Updated": shop.owners_updated.aggregate.count, - "Vehicles Created": shop.vehicles_created.aggregate.count, - "Vehicles Updated": shop.vehicles_updated.aggregate.count, - "Tasks Created": shop.tasks_created.aggregate.count, - "Tasks Updated": shop.tasks_updated.aggregate.count - }); - }); + const shopList = queryResults.bodyshops.map((shop) => ({ + "Shop Name": shop.shopname, + "Days Since Creation": moment().diff(moment(shop.created_at), "days"), + "Jobs Created": shop.jobs_created.aggregate.count, + "Jobs Updated": shop.jobs_updated.aggregate.count, + "Owners Created": shop.owners_created.aggregate.count, + "Owners Updated": shop.owners_updated.aggregate.count, + "Vehicles Created": shop.vehicles_created.aggregate.count, + "Vehicles Updated": shop.vehicles_updated.aggregate.count, + "Tasks Created": shop.tasks_created.aggregate.count, + "Tasks Updated": shop.tasks_updated.aggregate.count + })); const csv = converter.json2csv(shopList, { emptyFieldValue: "" }); emailer .sendTaskEmail({ - to: ["patrick.fic@convenient-brands.com"], + to: ["patrick.fic@convenient-brands.com", "bradley.rhoades@convenient-brands.com"], subject: `RO Usage Report - ${moment().format("MM/DD/YYYY")}`, text: ` Usage Report for ${moment().format("MM/DD/YYYY")} for Rome Online Customers. @@ -72,8 +78,8 @@ Notes: stack: error.stack }); }); - - return res.sendStatus(200); + res.sendStatus(200); + return; } catch (error) { logger.log("usage-report-email-error", "ERROR", req?.user?.email, null, { error: error.message, From 8a0916a47f1e421a5dba0bc7f8f5901e9ecb2b78 Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Mon, 13 Jan 2025 07:27:10 -0800 Subject: [PATCH 10/12] feature/IO-3060-Realtime-Notifications- Checkpoint --- hasura/metadata/tables.yaml | 166 +++++++++++++----- .../down.sql | 3 + .../up.sql | 1 + .../down.sql | 1 + .../up.sql | 1 + .../down.sql | 5 + .../up.sql | 3 + .../down.sql | 2 + .../up.sql | 2 + .../down.sql | 2 + .../up.sql | 2 + .../down.sql | 2 + .../up.sql | 2 + .../down.sql | 1 + .../up.sql | 1 + .../down.sql | 1 + .../up.sql | 1 + .../down.sql | 1 + .../up.sql | 1 + server/email/tasksEmails.js | 27 ++- .../eventHandlers/handeJobChange.js | 5 - .../eventHandlers/handeJobsChange.js | 5 + .../eventHandlers/handleBillChange.js | 5 - .../eventHandlers/handleBillsChange.js | 5 + server/routes/notificationsRoutes.js | 12 +- 25 files changed, 188 insertions(+), 69 deletions(-) create mode 100644 hasura/migrations/1736532531529_notification_settings_to_null/down.sql create mode 100644 hasura/migrations/1736532531529_notification_settings_to_null/up.sql create mode 100644 hasura/migrations/1736532558976_alter_table_public_associations_alter_column_notification_settings/down.sql create mode 100644 hasura/migrations/1736532558976_alter_table_public_associations_alter_column_notification_settings/up.sql create mode 100644 hasura/migrations/1736532890968_notifications_json_empty/down.sql create mode 100644 hasura/migrations/1736532890968_notifications_json_empty/up.sql create mode 100644 hasura/migrations/1736532914109_alter_table_public_notifications_alter_column_meta/down.sql create mode 100644 hasura/migrations/1736532914109_alter_table_public_notifications_alter_column_meta/up.sql create mode 100644 hasura/migrations/1736532933220_alter_table_public_notifications_alter_column_fcm_data/down.sql create mode 100644 hasura/migrations/1736532933220_alter_table_public_notifications_alter_column_fcm_data/up.sql create mode 100644 hasura/migrations/1736532952372_alter_table_public_notifications_alter_column_ui_translation_meta/down.sql create mode 100644 hasura/migrations/1736532952372_alter_table_public_notifications_alter_column_ui_translation_meta/up.sql create mode 100644 hasura/migrations/1736781880394_alter_table_public_notifications_alter_column_job_id/down.sql create mode 100644 hasura/migrations/1736781880394_alter_table_public_notifications_alter_column_job_id/up.sql create mode 100644 hasura/migrations/1736781913160_alter_table_public_notifications_alter_column_association_id/down.sql create mode 100644 hasura/migrations/1736781913160_alter_table_public_notifications_alter_column_association_id/up.sql create mode 100644 hasura/migrations/1736781949826_alter_table_public_job_watchers_alter_column_job_id/down.sql create mode 100644 hasura/migrations/1736781949826_alter_table_public_job_watchers_alter_column_job_id/up.sql delete mode 100644 server/notifications/eventHandlers/handeJobChange.js create mode 100644 server/notifications/eventHandlers/handeJobsChange.js delete mode 100644 server/notifications/eventHandlers/handleBillChange.js create mode 100644 server/notifications/eventHandlers/handleBillsChange.js diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index c9b1922ca..68dab7c39 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -683,6 +683,31 @@ - exported: _eq: false event_triggers: + - name: notifications_bills + definition: + enable_manual: false + insert: + columns: '*' + retry_conf: + interval_sec: 10 + num_retries: 0 + timeout_sec: 60 + webhook_from_env: HASURA_API_URL + headers: + - name: event-secret + value_from_env: EVENT_SECRET + request_transform: + body: + action: transform + template: |- + { + "success": true + } + method: POST + query_params: {} + template_engine: Kriti + url: '{{$base_url}}/job/events/handleBillsChange' + version: 2 - name: os_bills definition: delete: @@ -2813,7 +2838,7 @@ object_relationships: - name: job using: - foreign_key_constraint_on: job_id + foreign_key_constraint_on: jobid - name: user using: foreign_key_constraint_on: user_email @@ -2832,7 +2857,7 @@ - user_email - created_at - id - - job_id + - jobid comment: "" select_permissions: - role: user @@ -2841,7 +2866,7 @@ - user_email - created_at - id - - job_id + - jobid filter: user: _and: @@ -2858,7 +2883,7 @@ - user_email - created_at - id - - job_id + - jobid filter: user: _and: @@ -4402,6 +4427,58 @@ template_engine: Kriti url: '{{$base_url}}/record-handler/arms' version: 2 + - name: notifications_jobs + definition: + enable_manual: false + insert: + columns: '*' + update: + columns: + - queued_for_parts + - employee_prep + - clm_total + - towin + - employee_body + - converted + - scheduled_in + - scheduled_completion + - scheduled_delivery + - actual_delivery + - actual_completion + - alt_transport + - date_exported + - status + - employee_csr + - actual_in + - deliverchecklist + - comment + - job_totals + - employee_refinish + - inproduction + - production_vars + - intakechecklist + - cieca_ttl + - date_invoiced + retry_conf: + interval_sec: 10 + num_retries: 0 + timeout_sec: 60 + webhook_from_env: HASURA_API_URL + headers: + - name: event-secret + value_from_env: EVENT_SECRET + request_transform: + body: + action: transform + template: |- + { + "success": true + } + method: POST + query_params: {} + template_engine: Kriti + url: '{{$base_url}}/job/events/handleJobsChange' + version: 2 - name: os_jobs definition: delete: @@ -4751,46 +4828,21 @@ object_relationships: - name: association using: - foreign_key_constraint_on: association_id + foreign_key_constraint_on: associationid - name: job using: - foreign_key_constraint_on: job_id - insert_permissions: - - role: user - permission: - check: - association: - _and: - - active: - _eq: true - - user: - authid: - _eq: X-Hasura-User-Id - columns: - - association_id - - created_at - - fcm_data - - fcm_message - - fcm_title - - id - - job_id - - meta - - read - - ui_translation_meta - - ui_translation_string - - updated_at - comment: "" + foreign_key_constraint_on: jobid select_permissions: - role: user permission: columns: - - association_id + - associationid - created_at - fcm_data - fcm_message - fcm_title - id - - job_id + - jobid - meta - read - ui_translation_meta @@ -4809,18 +4861,8 @@ - role: user permission: columns: - - association_id - - created_at - - fcm_data - - fcm_message - - fcm_title - - id - - job_id - meta - read - - ui_translation_meta - - ui_translation_string - - updated_at filter: association: _and: @@ -5071,6 +5113,32 @@ - active: _eq: true check: null + event_triggers: + - name: notifications_parts_dispatch + definition: + enable_manual: false + insert: + columns: '*' + retry_conf: + interval_sec: 10 + num_retries: 0 + timeout_sec: 60 + webhook_from_env: HASURA_API_URL + headers: + - name: event-secret + value_from_env: EVENT_SECRET + request_transform: + body: + action: transform + template: |- + { + "success": true + } + method: POST + query_params: {} + template_engine: Kriti + url: '{{$base_url}}/job/events/handlePartsDispatchChange' + version: 2 - table: name: parts_dispatch_lines schema: public @@ -6049,6 +6117,8 @@ update: columns: - assigned_to + - completed + - description retry_conf: interval_sec: 10 num_retries: 3 @@ -6203,8 +6273,6 @@ enable_manual: false insert: columns: '*' - update: - columns: '*' retry_conf: interval_sec: 10 num_retries: 0 @@ -6214,10 +6282,16 @@ - name: event-secret value_from_env: EVENT_SECRET request_transform: + body: + action: transform + template: |- + { + "success": true + } method: POST query_params: {} template_engine: Kriti - url: '{{$base_url}}/events/handleTimeTicketsChange' + url: '{{$base_url}}/job/events/handleTimeTicketsChange' version: 2 - table: name: transitions diff --git a/hasura/migrations/1736532531529_notification_settings_to_null/down.sql b/hasura/migrations/1736532531529_notification_settings_to_null/down.sql new file mode 100644 index 000000000..9b79fc484 --- /dev/null +++ b/hasura/migrations/1736532531529_notification_settings_to_null/down.sql @@ -0,0 +1,3 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- update associations set notification_settings = '{}'; diff --git a/hasura/migrations/1736532531529_notification_settings_to_null/up.sql b/hasura/migrations/1736532531529_notification_settings_to_null/up.sql new file mode 100644 index 000000000..0badc8e0d --- /dev/null +++ b/hasura/migrations/1736532531529_notification_settings_to_null/up.sql @@ -0,0 +1 @@ +update associations set notification_settings = '{}'; diff --git a/hasura/migrations/1736532558976_alter_table_public_associations_alter_column_notification_settings/down.sql b/hasura/migrations/1736532558976_alter_table_public_associations_alter_column_notification_settings/down.sql new file mode 100644 index 000000000..a57d44af7 --- /dev/null +++ b/hasura/migrations/1736532558976_alter_table_public_associations_alter_column_notification_settings/down.sql @@ -0,0 +1 @@ +alter table "public"."associations" alter column "notification_settings" drop not null; diff --git a/hasura/migrations/1736532558976_alter_table_public_associations_alter_column_notification_settings/up.sql b/hasura/migrations/1736532558976_alter_table_public_associations_alter_column_notification_settings/up.sql new file mode 100644 index 000000000..ff8b9e509 --- /dev/null +++ b/hasura/migrations/1736532558976_alter_table_public_associations_alter_column_notification_settings/up.sql @@ -0,0 +1 @@ +alter table "public"."associations" alter column "notification_settings" set not null; diff --git a/hasura/migrations/1736532890968_notifications_json_empty/down.sql b/hasura/migrations/1736532890968_notifications_json_empty/down.sql new file mode 100644 index 000000000..8996c9f3e --- /dev/null +++ b/hasura/migrations/1736532890968_notifications_json_empty/down.sql @@ -0,0 +1,5 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- update notifications set meta = '{}'; +-- update notifications set fcm_data = '{}'; +-- update notifications set ui_translation_meta = '{}'; diff --git a/hasura/migrations/1736532890968_notifications_json_empty/up.sql b/hasura/migrations/1736532890968_notifications_json_empty/up.sql new file mode 100644 index 000000000..3300e7e9e --- /dev/null +++ b/hasura/migrations/1736532890968_notifications_json_empty/up.sql @@ -0,0 +1,3 @@ +update notifications set meta = '{}'; +update notifications set fcm_data = '{}'; +update notifications set ui_translation_meta = '{}'; diff --git a/hasura/migrations/1736532914109_alter_table_public_notifications_alter_column_meta/down.sql b/hasura/migrations/1736532914109_alter_table_public_notifications_alter_column_meta/down.sql new file mode 100644 index 000000000..86844a82c --- /dev/null +++ b/hasura/migrations/1736532914109_alter_table_public_notifications_alter_column_meta/down.sql @@ -0,0 +1,2 @@ +alter table "public"."notifications" alter column "meta" drop not null; +ALTER TABLE "public"."notifications" ALTER COLUMN "meta" drop default; diff --git a/hasura/migrations/1736532914109_alter_table_public_notifications_alter_column_meta/up.sql b/hasura/migrations/1736532914109_alter_table_public_notifications_alter_column_meta/up.sql new file mode 100644 index 000000000..05e6a5b23 --- /dev/null +++ b/hasura/migrations/1736532914109_alter_table_public_notifications_alter_column_meta/up.sql @@ -0,0 +1,2 @@ +alter table "public"."notifications" alter column "meta" set default jsonb_build_object(); +alter table "public"."notifications" alter column "meta" set not null; diff --git a/hasura/migrations/1736532933220_alter_table_public_notifications_alter_column_fcm_data/down.sql b/hasura/migrations/1736532933220_alter_table_public_notifications_alter_column_fcm_data/down.sql new file mode 100644 index 000000000..f33b7161e --- /dev/null +++ b/hasura/migrations/1736532933220_alter_table_public_notifications_alter_column_fcm_data/down.sql @@ -0,0 +1,2 @@ +alter table "public"."notifications" alter column "fcm_data" drop not null; +ALTER TABLE "public"."notifications" ALTER COLUMN "fcm_data" drop default; diff --git a/hasura/migrations/1736532933220_alter_table_public_notifications_alter_column_fcm_data/up.sql b/hasura/migrations/1736532933220_alter_table_public_notifications_alter_column_fcm_data/up.sql new file mode 100644 index 000000000..5c0d753d9 --- /dev/null +++ b/hasura/migrations/1736532933220_alter_table_public_notifications_alter_column_fcm_data/up.sql @@ -0,0 +1,2 @@ +alter table "public"."notifications" alter column "fcm_data" set default jsonb_build_object(); +alter table "public"."notifications" alter column "fcm_data" set not null; diff --git a/hasura/migrations/1736532952372_alter_table_public_notifications_alter_column_ui_translation_meta/down.sql b/hasura/migrations/1736532952372_alter_table_public_notifications_alter_column_ui_translation_meta/down.sql new file mode 100644 index 000000000..d4a7ca3ef --- /dev/null +++ b/hasura/migrations/1736532952372_alter_table_public_notifications_alter_column_ui_translation_meta/down.sql @@ -0,0 +1,2 @@ +alter table "public"."notifications" alter column "ui_translation_meta" drop not null; +ALTER TABLE "public"."notifications" ALTER COLUMN "ui_translation_meta" drop default; diff --git a/hasura/migrations/1736532952372_alter_table_public_notifications_alter_column_ui_translation_meta/up.sql b/hasura/migrations/1736532952372_alter_table_public_notifications_alter_column_ui_translation_meta/up.sql new file mode 100644 index 000000000..221860724 --- /dev/null +++ b/hasura/migrations/1736532952372_alter_table_public_notifications_alter_column_ui_translation_meta/up.sql @@ -0,0 +1,2 @@ +alter table "public"."notifications" alter column "ui_translation_meta" set default jsonb_build_object(); +alter table "public"."notifications" alter column "ui_translation_meta" set not null; diff --git a/hasura/migrations/1736781880394_alter_table_public_notifications_alter_column_job_id/down.sql b/hasura/migrations/1736781880394_alter_table_public_notifications_alter_column_job_id/down.sql new file mode 100644 index 000000000..701336411 --- /dev/null +++ b/hasura/migrations/1736781880394_alter_table_public_notifications_alter_column_job_id/down.sql @@ -0,0 +1 @@ +alter table "public"."notifications" rename column "jobid" to "job_id"; diff --git a/hasura/migrations/1736781880394_alter_table_public_notifications_alter_column_job_id/up.sql b/hasura/migrations/1736781880394_alter_table_public_notifications_alter_column_job_id/up.sql new file mode 100644 index 000000000..ee6f4ba02 --- /dev/null +++ b/hasura/migrations/1736781880394_alter_table_public_notifications_alter_column_job_id/up.sql @@ -0,0 +1 @@ +alter table "public"."notifications" rename column "job_id" to "jobid"; diff --git a/hasura/migrations/1736781913160_alter_table_public_notifications_alter_column_association_id/down.sql b/hasura/migrations/1736781913160_alter_table_public_notifications_alter_column_association_id/down.sql new file mode 100644 index 000000000..ddf7bf683 --- /dev/null +++ b/hasura/migrations/1736781913160_alter_table_public_notifications_alter_column_association_id/down.sql @@ -0,0 +1 @@ +alter table "public"."notifications" rename column "associationid" to "association_id"; diff --git a/hasura/migrations/1736781913160_alter_table_public_notifications_alter_column_association_id/up.sql b/hasura/migrations/1736781913160_alter_table_public_notifications_alter_column_association_id/up.sql new file mode 100644 index 000000000..5238af59a --- /dev/null +++ b/hasura/migrations/1736781913160_alter_table_public_notifications_alter_column_association_id/up.sql @@ -0,0 +1 @@ +alter table "public"."notifications" rename column "association_id" to "associationid"; diff --git a/hasura/migrations/1736781949826_alter_table_public_job_watchers_alter_column_job_id/down.sql b/hasura/migrations/1736781949826_alter_table_public_job_watchers_alter_column_job_id/down.sql new file mode 100644 index 000000000..ff9c47c35 --- /dev/null +++ b/hasura/migrations/1736781949826_alter_table_public_job_watchers_alter_column_job_id/down.sql @@ -0,0 +1 @@ +alter table "public"."job_watchers" rename column "jobid" to "job_id"; diff --git a/hasura/migrations/1736781949826_alter_table_public_job_watchers_alter_column_job_id/up.sql b/hasura/migrations/1736781949826_alter_table_public_job_watchers_alter_column_job_id/up.sql new file mode 100644 index 000000000..af25f283a --- /dev/null +++ b/hasura/migrations/1736781949826_alter_table_public_job_watchers_alter_column_job_id/up.sql @@ -0,0 +1 @@ +alter table "public"."job_watchers" rename column "job_id" to "jobid"; diff --git a/server/email/tasksEmails.js b/server/email/tasksEmails.js index 6d787811a..7a05bcb05 100644 --- a/server/email/tasksEmails.js +++ b/server/email/tasksEmails.js @@ -142,27 +142,38 @@ const sendMail = (type, to, subject, html, taskIds, successCallback, requestInst * @returns {Promise<*>} */ const taskAssignedEmail = async (req, res) => { - // We have no event Data, bail + // 1. Check if we have new task data in the event body. if (!req?.body?.event?.data?.new) { return res.status(400).json({ message: "No data in the event body" }); } - const { new: newTask } = req.body.event.data; + const { new: newTask, old: oldTask } = req.body.event.data; - // This is not a new task, but a reassignment. - const dirty = req.body.event.data?.old && req.body.event.data?.old?.assigned_to; + // TODO: THIS IS HERE BECAUSE THE HANDLER NOW DOES 3 FIELDS, WILL NEED TO BE BUILT ON FOR NOTIFICATIONS + if (oldTask && oldTask.assigned_to === newTask.assigned_to) { + return res.status(200).json({ success: true, message: "assigned_to not changed" }); + } - //Query to get the employee assigned currently. + // 3. If we made it here, assigned_to changed (or oldTask is missing), + // so we continue with the rest of the logic. + + // Query to get the task with bodyshop data, assigned employee data, etc. const { tasks_by_pk } = await client.request(queries.QUERY_TASK_BY_ID, { id: newTask.id }); + // Format date/time with the correct timezone const dateLine = moment().tz(tasks_by_pk.bodyshop.timezone).format("M/DD/YYYY @ hh:mm a"); + // This determines if it was re-assigned (old task exists and had an assigned_to). + const dirty = oldTask && oldTask.assigned_to; + sendMail( "assigned", tasks_by_pk.assigned_to_employee.user_email, - `A ${formatPriority(newTask.priority)} priority task has been ${dirty ? "reassigned to" : "created for"} you - ${newTask.title}`, + `A ${formatPriority(newTask.priority)} priority task has been ${ + dirty ? "reassigned" : "created" + } for you - ${newTask.title}`, generateEmailTemplate( generateTemplateArgs( newTask.title, @@ -181,8 +192,8 @@ const taskAssignedEmail = async (req, res) => { tasks_by_pk.bodyshop.convenient_company ); - // We return success regardless because we don't want to block the event trigger. - res.status(200).json({ success: true }); + // Return success so we don't block the event trigger. + return res.status(200).json({ success: true }); }; /** diff --git a/server/notifications/eventHandlers/handeJobChange.js b/server/notifications/eventHandlers/handeJobChange.js deleted file mode 100644 index db38d5e87..000000000 --- a/server/notifications/eventHandlers/handeJobChange.js +++ /dev/null @@ -1,5 +0,0 @@ -const handleJobChange = (req, res) => { - return res.status(200).json({ message: "Job change handled." }); -}; - -module.exports = handleJobChange; diff --git a/server/notifications/eventHandlers/handeJobsChange.js b/server/notifications/eventHandlers/handeJobsChange.js new file mode 100644 index 000000000..9449f23c4 --- /dev/null +++ b/server/notifications/eventHandlers/handeJobsChange.js @@ -0,0 +1,5 @@ +const handleJobsChange = (req, res) => { + return res.status(200).json({ message: "Jobs change handled." }); +}; + +module.exports = handleJobsChange; diff --git a/server/notifications/eventHandlers/handleBillChange.js b/server/notifications/eventHandlers/handleBillChange.js deleted file mode 100644 index fcabafe85..000000000 --- a/server/notifications/eventHandlers/handleBillChange.js +++ /dev/null @@ -1,5 +0,0 @@ -const handleBillChange = (req, res) => { - return res.status(200).json({ message: "Bill change handled." }); -}; - -module.exports = handleBillChange; diff --git a/server/notifications/eventHandlers/handleBillsChange.js b/server/notifications/eventHandlers/handleBillsChange.js new file mode 100644 index 000000000..d444848f2 --- /dev/null +++ b/server/notifications/eventHandlers/handleBillsChange.js @@ -0,0 +1,5 @@ +const handleBillsChange = (req, res) => { + return res.status(200).json({ message: "Bills change handled." }); +}; + +module.exports = handleBillsChange; diff --git a/server/routes/notificationsRoutes.js b/server/routes/notificationsRoutes.js index a459b9a53..64228382b 100644 --- a/server/routes/notificationsRoutes.js +++ b/server/routes/notificationsRoutes.js @@ -2,11 +2,13 @@ const express = require("express"); const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware"); const { subscribe, unsubscribe, sendNotification } = require("../firebase/firebase-handler"); const eventAuthorizationMiddleware = require("../middleware/eventAuthorizationMIddleware"); -const handleJobChange = require("../notifications/eventHandlers/handeJobChange"); -const handleBillChange = require("../notifications/eventHandlers/handleBillChange"); + const handlePartsOrderChange = require("../notifications/eventHandlers/handlePartsOrderChange"); const handlePartsDispatchChange = require("../notifications/eventHandlers/handlePartsDispatchChange"); const handleTasksChange = require("../notifications/eventHandlers/handleTasksChange"); +const handleTimeTicketsChange = require("../notifications/eventHandlers/handleTimeTicketsChange"); +const handleJobsChange = require("../notifications/eventHandlers/handeJobsChange"); +const handleBillsChange = require("../notifications/eventHandlers/handleBillsChange"); const router = express.Router(); @@ -16,11 +18,11 @@ router.post("/unsubscribe", validateFirebaseIdTokenMiddleware, unsubscribe); router.post("/sendtestnotification", validateFirebaseIdTokenMiddleware, sendNotification); // Hasura Entry points for creating notifications -router.post("/events/handleJobChange", eventAuthorizationMiddleware, handleJobChange); -router.post("/events/handleBillChange", eventAuthorizationMiddleware, handleBillChange); +router.post("/events/handleJobsChange", eventAuthorizationMiddleware, handleJobsChange); +router.post("/events/handleBillsChange", eventAuthorizationMiddleware, handleBillsChange); router.post("/events/handlePartsOrderChange", eventAuthorizationMiddleware, handlePartsOrderChange); router.post("/events/handlePartsDispatchChange", eventAuthorizationMiddleware, handlePartsDispatchChange); router.post("/events/handleTasksChange", eventAuthorizationMiddleware, handleTasksChange); -router.post("/events/handleTimeTicketsChange", eventAuthorizationMiddleware, handleTasksChange); +router.post("/events/handleTimeTicketsChange", eventAuthorizationMiddleware, handleTimeTicketsChange); module.exports = router; From a06d3c9365583b8b8000a0b7f30f9029f3816035 Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Tue, 14 Jan 2025 09:00:04 -0800 Subject: [PATCH 11/12] feature/IO-3060-Realtime-Notifications- Checkpoint --- hasura/metadata/tables.yaml | 38 ++++++++++++++++++++++++++++++++----- server/email/tasksEmails.js | 27 ++++++++------------------ 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index 68dab7c39..d7a010473 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -706,7 +706,7 @@ method: POST query_params: {} template_engine: Kriti - url: '{{$base_url}}/job/events/handleBillsChange' + url: '{{$base_url}}/notifications/events/handleBillsChange' version: 2 - name: os_bills definition: @@ -4477,7 +4477,7 @@ method: POST query_params: {} template_engine: Kriti - url: '{{$base_url}}/job/events/handleJobsChange' + url: '{{$base_url}}/notifications/events/handleJobsChange' version: 2 - name: os_jobs definition: @@ -5137,7 +5137,7 @@ method: POST query_params: {} template_engine: Kriti - url: '{{$base_url}}/job/events/handlePartsDispatchChange' + url: '{{$base_url}}/notifications/events/handlePartsDispatchChange' version: 2 - table: name: parts_dispatch_lines @@ -6109,7 +6109,7 @@ _eq: true check: null event_triggers: - - name: tasks_assigned_changed + - name: notifications_tasks definition: enable_manual: false insert: @@ -6119,6 +6119,34 @@ - assigned_to - completed - description + retry_conf: + interval_sec: 10 + num_retries: 0 + timeout_sec: 60 + webhook_from_env: HASURA_API_URL + headers: + - name: event-secret + value_from_env: EVENT_SECRET + request_transform: + body: + action: transform + template: |- + { + "success": true + } + method: POST + query_params: {} + template_engine: Kriti + url: '{{$base_url}}/notifications/events/handleTasksChange' + version: 2 + - name: tasks_assigned_changed + definition: + enable_manual: false + insert: + columns: '*' + update: + columns: + - assigned_to retry_conf: interval_sec: 10 num_retries: 3 @@ -6291,7 +6319,7 @@ method: POST query_params: {} template_engine: Kriti - url: '{{$base_url}}/job/events/handleTimeTicketsChange' + url: '{{$base_url}}/notifications/events/handleTimeTicketsChange' version: 2 - table: name: transitions diff --git a/server/email/tasksEmails.js b/server/email/tasksEmails.js index 7a05bcb05..6d787811a 100644 --- a/server/email/tasksEmails.js +++ b/server/email/tasksEmails.js @@ -142,38 +142,27 @@ const sendMail = (type, to, subject, html, taskIds, successCallback, requestInst * @returns {Promise<*>} */ const taskAssignedEmail = async (req, res) => { - // 1. Check if we have new task data in the event body. + // We have no event Data, bail if (!req?.body?.event?.data?.new) { return res.status(400).json({ message: "No data in the event body" }); } - const { new: newTask, old: oldTask } = req.body.event.data; + const { new: newTask } = req.body.event.data; - // TODO: THIS IS HERE BECAUSE THE HANDLER NOW DOES 3 FIELDS, WILL NEED TO BE BUILT ON FOR NOTIFICATIONS - if (oldTask && oldTask.assigned_to === newTask.assigned_to) { - return res.status(200).json({ success: true, message: "assigned_to not changed" }); - } + // This is not a new task, but a reassignment. + const dirty = req.body.event.data?.old && req.body.event.data?.old?.assigned_to; - // 3. If we made it here, assigned_to changed (or oldTask is missing), - // so we continue with the rest of the logic. - - // Query to get the task with bodyshop data, assigned employee data, etc. + //Query to get the employee assigned currently. const { tasks_by_pk } = await client.request(queries.QUERY_TASK_BY_ID, { id: newTask.id }); - // Format date/time with the correct timezone const dateLine = moment().tz(tasks_by_pk.bodyshop.timezone).format("M/DD/YYYY @ hh:mm a"); - // This determines if it was re-assigned (old task exists and had an assigned_to). - const dirty = oldTask && oldTask.assigned_to; - sendMail( "assigned", tasks_by_pk.assigned_to_employee.user_email, - `A ${formatPriority(newTask.priority)} priority task has been ${ - dirty ? "reassigned" : "created" - } for you - ${newTask.title}`, + `A ${formatPriority(newTask.priority)} priority task has been ${dirty ? "reassigned to" : "created for"} you - ${newTask.title}`, generateEmailTemplate( generateTemplateArgs( newTask.title, @@ -192,8 +181,8 @@ const taskAssignedEmail = async (req, res) => { tasks_by_pk.bodyshop.convenient_company ); - // Return success so we don't block the event trigger. - return res.status(200).json({ success: true }); + // We return success regardless because we don't want to block the event trigger. + res.status(200).json({ success: true }); }; /** From a8ef681cf111ba77c607a24122ec0b0283dfcd1a Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Tue, 14 Jan 2025 13:13:09 -0800 Subject: [PATCH 12/12] IO-3076 Adjust daily usage report window. --- server/data/usageReport.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/data/usageReport.js b/server/data/usageReport.js index dd7bb4d42..42a8b5451 100644 --- a/server/data/usageReport.js +++ b/server/data/usageReport.js @@ -34,7 +34,7 @@ exports.default = async (req, res) => { //Query the usage data. const queryResults = await client.request(queries.STATUS_UPDATE, { - today: moment().startOf("day").subtract(3, "days"), + today: moment().startOf("day").subtract(7, "days"), period: moment().subtract(90, "days").startOf("day") }); @@ -63,7 +63,7 @@ Usage Report for ${moment().format("MM/DD/YYYY")} for Rome Online Customers. Notes: - Days Since Creation: The number of days since the shop was created. Only shops created in the last 90 days are included. - Updated values should be higher than created values. - - Counts are inclusive of the last 3 days of data. + - Counts are inclusive of the last 7 days of data. `, attachments: [{ filename: `RO Usage Report ${moment().format("MM/DD/YYYY")}.csv`, content: csv }] })