Compare commits

...

7 Commits

Author SHA1 Message Date
Dave
ca1a456312 feature/IO-3402-Import-Add-Notifiers - Fix Normalize 2025-12-19 14:10:15 -05:00
Dave
c010665ea9 feature/IO-3402-Import-Add-Notifiers - Fix 2025-12-18 18:26:02 -05:00
Dave
d6fba12cd9 feature/IO-3402-Import-Add-Notifiers - Fix Auto Notifiers 2025-12-18 13:27:24 -05:00
Allan Carr
6ea1c291e6 Merged in release/2025-12-19 (pull request #2703)
Release/2025 12 19
2025-12-12 02:36:57 +00:00
Allan Carr
05d5c96491 Merged in feature/IO-3462-Project-Mexico-Mod (pull request #2701)
IO-3462 Project Mexico Mod

Approved-by: Dave Richer
2025-12-10 20:18:14 +00:00
Dave Richer
f12e40e4c6 Merged in feature/IO-3461-Fix-EULA (pull request #2698)
feature/IO-3461-Fix-Eula
2025-12-09 22:15:22 +00:00
Dave
bb4e671c83 feature/IO-3461-Fix-Eula 2025-12-09 17:13:59 -05:00
7 changed files with 37 additions and 21 deletions

View File

@@ -138,7 +138,7 @@ export function App({
);
}
if (currentEula && !currentUser.eulaIsAccepted) {
if (!isPartsEntry && currentEula && !currentUser.eulaIsAccepted) {
return <Eula />;
}

View File

@@ -55,7 +55,8 @@ const Eula = ({ currentEula, currentUser, acceptEula }) => {
const useremail = currentUser.email;
try {
const { ...otherFormValues } = formValues;
// eslint-disable-next-line no-unused-vars
const { accepted_terms, ...otherFormValues } = formValues;
// Trim the values of the fields before submitting
const trimmedFormValues = Object.entries(otherFormValues).reduce((acc, [key, value]) => {

View File

@@ -16,6 +16,7 @@ export default function ShopInfoNotificationsAutoadd({ bodyshop }) {
<Text type="secondary">{t("bodyshop.labels.notifications.followers")}</Text>
{employeeOptions.length > 0 ? (
<Form.Item
normalize={(value) => (value || []).filter((id) => typeof id === "string" && id.trim() !== "")}
name="notification_followers"
rules={[
{
@@ -42,11 +43,6 @@ export default function ShopInfoNotificationsAutoadd({ bodyshop }) {
options={employeeOptions}
placeholder={t("bodyshop.fields.notifications.placeholder")}
showEmail={true}
onChange={(value) => {
// Filter out null or invalid values before passing to Form
const cleanedValue = value?.filter((id) => id != null && typeof id === "string" && id.trim() !== "");
return cleanedValue;
}}
/>
</Form.Item>
) : (

View File

@@ -1156,7 +1156,11 @@
enable_manual: false
update:
columns:
- imexshopid
- timezone
- shopname
- notification_followers
- state
- md_order_statuses
retry_conf:
interval_sec: 10
@@ -3698,6 +3702,7 @@
- deliverchecklist
- depreciation_taxes
- dms_allocation
- dms_id
- driveable
- employee_body
- employee_csr
@@ -3975,6 +3980,7 @@
- deliverchecklist
- depreciation_taxes
- dms_allocation
- dms_id
- driveable
- employee_body
- employee_csr
@@ -4264,6 +4270,7 @@
- deliverchecklist
- depreciation_taxes
- dms_allocation
- dms_id
- driveable
- employee_body
- employee_csr

View File

@@ -2926,6 +2926,15 @@ exports.GET_BODYSHOP_BY_ID = `
}
`;
exports.GET_BODYSHOP_WATCHERS_BY_ID = `
query GET_BODYSHOP_BY_ID($id: uuid!) {
bodyshops_by_pk(id: $id) {
id
notification_followers
}
}
`;
exports.GET_DOCUMENTS_BY_JOB = `
query GET_DOCUMENTS_BY_JOB($jobId: uuid!) {
jobs_by_pk(id: $jobId) {

View File

@@ -241,6 +241,8 @@ const partsManagementProvisioning = async (req, res) => {
"phone",
"userEmail"
]);
// TODO add in check for early access
await ensureExternalIdUnique(body.external_shop_id);
logger.log("admin-create-shop-user", "debug", body.userEmail, null, {

View File

@@ -4,11 +4,14 @@
* This module handles automatically adding watchers to new jobs based on the notifications_autoadd
* boolean field in the associations table and the notification_followers JSON field in the bodyshops table.
* It ensures users are not added twice and logs the process.
*
* NOTE: Bodyshop notification_followers is fetched directly from the DB (Hasura) to avoid stale Redis cache.
*/
const { client: gqlClient } = require("../graphql-client/graphql-client");
const { isEmpty } = require("lodash");
const {
GET_BODYSHOP_WATCHERS_BY_ID,
GET_JOB_WATCHERS_MINIMAL,
GET_NOTIFICATION_WATCHERS,
INSERT_JOB_WATCHERS
@@ -26,10 +29,7 @@ const FILTER_SELF_FROM_WATCHERS = process.env?.FILTER_SELF_FROM_WATCHERS !== "fa
*/
const autoAddWatchers = async (req) => {
const { event, trigger } = req.body;
const {
logger,
sessionUtils: { getBodyshopFromRedis }
} = req;
const { logger } = req;
// Validate that this is an INSERT event, bail
if (trigger?.name !== "notifications_jobs_autoadd" || event.op !== "INSERT" || event.data.old) {
@@ -48,20 +48,20 @@ const autoAddWatchers = async (req) => {
const hasuraUserId = event?.session_variables?.["x-hasura-user-id"];
try {
// Fetch bodyshop data from Redis
const bodyshopData = await getBodyshopFromRedis(shopId);
let notificationFollowers = bodyshopData?.notification_followers;
// Fetch bodyshop data directly from DB (avoid Redis staleness)
const bodyshopResponse = await gqlClient.request(GET_BODYSHOP_WATCHERS_BY_ID, { id: shopId });
const bodyshopData = bodyshopResponse?.bodyshops_by_pk;
// Bail if notification_followers is missing or not an array
if (!notificationFollowers || !Array.isArray(notificationFollowers)) {
return;
}
const notificationFollowersRaw = bodyshopData?.notification_followers;
const notificationFollowers = Array.isArray(notificationFollowersRaw)
? [...new Set(notificationFollowersRaw.filter((id) => id))] // de-dupe + remove falsy
: [];
// Execute queries in parallel
const [notificationData, existingWatchersData] = await Promise.all([
gqlClient.request(GET_NOTIFICATION_WATCHERS, {
shopId,
employeeIds: notificationFollowers.filter((id) => id)
employeeIds: notificationFollowers
}),
gqlClient.request(GET_JOB_WATCHERS_MINIMAL, { jobid: jobId })
]);
@@ -73,7 +73,7 @@ const autoAddWatchers = async (req) => {
associationId: assoc.id
})) || [];
// Get users from notification_followers
// Get users from notification_followers (employee IDs -> employee emails)
const followerEmails =
notificationData?.employees
?.filter((e) => e.user_email)
@@ -84,7 +84,7 @@ const autoAddWatchers = async (req) => {
// Combine and deduplicate emails (use email as the unique key)
const usersToAdd = [...autoAddUsers, ...followerEmails].reduce((acc, user) => {
if (!acc.some((u) => u.email === user.email)) {
if (user?.email && !acc.some((u) => u.email === user.email)) {
acc.push(user);
}
return acc;
@@ -123,6 +123,7 @@ const autoAddWatchers = async (req) => {
message: error?.message,
stack: error?.stack,
jobId,
shopId,
roNumber
});
throw error; // Re-throw to ensure the error is logged in the handler