From 7a383aaec98a4315914820960cb65351330edfa1 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Thu, 28 Aug 2025 15:07:18 -0700 Subject: [PATCH 1/7] IO-3340 Resolve unenforced content-type --- .../documents-upload-imgproxy.utility.js | 5 ++++- server/media/imgproxy-media.js | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/client/src/components/documents-upload-imgproxy/documents-upload-imgproxy.utility.js b/client/src/components/documents-upload-imgproxy/documents-upload-imgproxy.utility.js index 7ecbd50bb..000871efe 100644 --- a/client/src/components/documents-upload-imgproxy/documents-upload-imgproxy.utility.js +++ b/client/src/components/documents-upload-imgproxy/documents-upload-imgproxy.utility.js @@ -67,11 +67,14 @@ export const uploadToS3 = async ( } //Key should be same as we provided to maintain backwards compatibility. - const { presignedUrl: preSignedUploadUrlToS3, key: s3Key } = signedURLResponse.data.signedUrls[0]; + const { presignedUrl: preSignedUploadUrlToS3, key: s3Key, contentType } = signedURLResponse.data.signedUrls[0]; const options = { onUploadProgress: (e) => { if (onProgress) onProgress({ percent: (e.loaded / e.total) * 100 }); + }, + headers: { + ...contentType ? { "Content-Type": fileType } : {} } }; diff --git a/server/media/imgproxy-media.js b/server/media/imgproxy-media.js index 7a2b1ebfb..85bc3393f 100644 --- a/server/media/imgproxy-media.js +++ b/server/media/imgproxy-media.js @@ -58,8 +58,20 @@ const generateSignedUploadUrls = async (req, res) => { } const command = new PutObjectCommand(commandParams); - const presignedUrl = await getSignedUrl(client, command, { expiresIn: 360 }); - signedUrls.push({ filename, presignedUrl, key }); + + // For PDFs, we need to add conditions to the presigned URL to enforce content type + const presignedUrlOptions = { expiresIn: 360 }; + if (isPdf) { + presignedUrlOptions.signableHeaders = new Set(['content-type']); + } + + const presignedUrl = await getSignedUrl(client, command, presignedUrlOptions); + signedUrls.push({ + filename, + presignedUrl, + key, + ...(isPdf && { contentType: "application/pdf" }) + }); } logger.log("imgproxy-upload-success", "DEBUG", req.user?.email, jobid, { signedUrls }); From 0fbf63dec8737a89a0762e7816617d5aa37931cb Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Sun, 31 Aug 2025 22:42:24 -0700 Subject: [PATCH 2/7] IO-3352 Arrived Color on Schedule Signed-off-by: Allan Carr --- .../scheduler-calendar-wrapper.component.jsx | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/client/src/components/schedule-calendar-wrapper/scheduler-calendar-wrapper.component.jsx b/client/src/components/schedule-calendar-wrapper/scheduler-calendar-wrapper.component.jsx index e9e411b41..b2d355434 100644 --- a/client/src/components/schedule-calendar-wrapper/scheduler-calendar-wrapper.component.jsx +++ b/client/src/components/schedule-calendar-wrapper/scheduler-calendar-wrapper.component.jsx @@ -40,27 +40,26 @@ export function ScheduleCalendarWrapperComponent({ const currentView = search.view || defaultView || "week"; const handleEventPropStyles = (event) => { - const hasColor = Boolean(event?.color?.hex || event?.color); + const { color, block, arrived } = event ?? {}; + const hasColor = Boolean(color?.hex || color); const useBg = currentView !== "agenda"; // Prioritize explicit blocked-day background to ensure red in all themes let bg; if (useBg) { - if (event?.block) { - bg = "var(--event-block-bg)"; - } else if (hasColor) { - bg = event?.color?.hex ?? event?.color; - } else { - bg = "var(--event-bg-fallback)"; - } + bg = block + ? "var(--event-block-bg)" + : arrived + ? "var(--event-arrived-bg)" + : (color?.hex ?? color ?? "var(--event-bg-fallback)"); } - const usedFallback = !hasColor && !event?.block; // only mark as fallback when not blocked + const usedFallback = !hasColor && !block && !arrived; // only mark as fallback when not blocked or arrived const classes = [ "imex-event", - event.arrived && "imex-event-arrived", - event.block && "imex-event-block", + arrived && "imex-event-arrived", + block && "imex-event-block", usedFallback && "imex-event-fallback" ] .filter(Boolean) From eb18130e5169cd518f43a19aee5d802d85236c28 Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Mon, 1 Sep 2025 20:29:39 -0700 Subject: [PATCH 3/7] IO-3310 Shop Info Data Preservation Correction to preserve hidden data Signed-off-by: Allan Carr --- .../shop-info/shop-info.container.jsx | 28 ++++-- .../shop-info/useFormDataPreservation.js | 88 ++++++++----------- 2 files changed, 57 insertions(+), 59 deletions(-) diff --git a/client/src/components/shop-info/shop-info.container.jsx b/client/src/components/shop-info/shop-info.container.jsx index afcd7968d..a820dce69 100644 --- a/client/src/components/shop-info/shop-info.container.jsx +++ b/client/src/components/shop-info/shop-info.container.jsx @@ -23,13 +23,24 @@ export default function ShopInfoContainer() { }); const notification = useNotification(); - const combinedFeatureConfig = { - ...FEATURE_CONFIGS.general, - ...FEATURE_CONFIGS.responsibilitycenters - }; + const combineFeatureConfigs = (...configs) => + (configs || []) + .filter(Boolean) + .flatMap((cfg) => Object.entries(cfg)) + .reduce((acc, [featureName, fieldPaths]) => { + if (!Array.isArray(fieldPaths)) return acc; + acc[featureName] = [...(acc[featureName] ?? []), ...fieldPaths]; + return acc; + }, {}); + + const combinedFeatureConfig = combineFeatureConfigs(FEATURE_CONFIGS.general, FEATURE_CONFIGS.responsibilitycenters); // Use form data preservation for all shop-info features - const { createSubmissionHandler } = useFormDataPreservation(form, data?.bodyshops[0], combinedFeatureConfig); + const { createSubmissionHandler, preserveHiddenFormData } = useFormDataPreservation( + form, + data?.bodyshops[0], + combinedFeatureConfig + ); const handleFinish = createSubmissionHandler((values) => { setSaveLoading(true); @@ -51,8 +62,11 @@ export default function ShopInfoContainer() { }); useEffect(() => { - if (data) form.resetFields(); - }, [form, data]); + if (!data) return; + form.resetFields(); + // After reset, re-apply hidden field preservation so values aren't wiped + preserveHiddenFormData(); + }, [data, form, preserveHiddenFormData]); if (error) return ; if (loading) return ; diff --git a/client/src/components/shop-info/useFormDataPreservation.js b/client/src/components/shop-info/useFormDataPreservation.js index 310ff0202..93978962e 100644 --- a/client/src/components/shop-info/useFormDataPreservation.js +++ b/client/src/components/shop-info/useFormDataPreservation.js @@ -1,4 +1,4 @@ -import { useCallback, useEffect } from "react"; +import { useCallback, useEffect, useMemo } from "react"; import { HasFeatureAccess } from "./../feature-wrapper/feature-wrapper.component"; /** @@ -8,73 +8,57 @@ import { HasFeatureAccess } from "./../feature-wrapper/feature-wrapper.component * @param {Object} featureConfig - Configuration object defining which features and their associated fields to preserve */ export const useFormDataPreservation = (form, bodyshop, featureConfig) => { - const getNestedValue = (obj, path) => { - return path.reduce((current, key) => current?.[key], obj); - }; - + // Safe nested getters/setters using path arrays + const getNestedValue = (obj, path) => path?.reduce((acc, key) => acc?.[key], obj); const setNestedValue = (obj, path, value) => { const lastKey = path[path.length - 1]; - const parentPath = path.slice(0, -1); - - const parent = parentPath.reduce((current, key) => { - if (!current[key]) current[key] = {}; - return current[key]; + const parent = path.slice(0, -1).reduce((curr, key) => { + if (!curr[key] || typeof curr[key] !== "object") curr[key] = {}; + return curr[key]; }, obj); - parent[lastKey] = value; }; - const preserveHiddenFormData = useCallback(() => { - const preservationData = {}; - let hasDataToPreserve = false; - + // Paths for features that are NOT accessible + const disabledPaths = useMemo(() => { + const result = []; + if (!featureConfig) return result; Object.entries(featureConfig).forEach(([featureName, fieldPaths]) => { const hasAccess = HasFeatureAccess({ featureName, bodyshop }); + if (hasAccess || !Array.isArray(fieldPaths)) return; + fieldPaths.forEach((p) => Array.isArray(p) && p.length && result.push(p)); + }); + return result; + }, [featureConfig, bodyshop]); - if (!hasAccess) { - fieldPaths.forEach((fieldPath) => { - const currentValues = form.getFieldsValue(); - let value = getNestedValue(currentValues, fieldPath); + const preserveHiddenFormData = useCallback(() => { + const currentValues = form.getFieldsValue(); + const preservationData = {}; + let hasAny = false; - if (value === undefined || value === null) { - value = getNestedValue(bodyshop, fieldPath); - } - - if (value !== undefined && value !== null) { - setNestedValue(preservationData, fieldPath, value); - hasDataToPreserve = true; - } - }); + disabledPaths.forEach((path) => { + let value = getNestedValue(currentValues, path); + if (value == null) value = getNestedValue(bodyshop, path); + if (value != null) { + setNestedValue(preservationData, path, value); + hasAny = true; } }); - if (hasDataToPreserve) { - form.setFieldsValue(preservationData); - } - }, [form, featureConfig, bodyshop]); + if (hasAny) form.setFieldsValue(preservationData); + }, [form, bodyshop, disabledPaths]); const getCompleteFormValues = () => { - const currentFormValues = form.getFieldsValue(); - const completeValues = { ...currentFormValues }; + const currentValues = form.getFieldsValue(); + const complete = { ...currentValues }; - Object.entries(featureConfig).forEach(([featureName, fieldPaths]) => { - const hasAccess = HasFeatureAccess({ featureName, bodyshop }); - - if (!hasAccess) { - fieldPaths.forEach((fieldPath) => { - let value = getNestedValue(currentFormValues, fieldPath); - if (value === undefined || value === null) { - value = getNestedValue(bodyshop, fieldPath); - } - - if (value !== undefined && value !== null) { - setNestedValue(completeValues, fieldPath, value); - } - }); - } + disabledPaths.forEach((path) => { + let value = getNestedValue(currentValues, path); + if (value == null) value = getNestedValue(bodyshop, path); + if (value != null) setNestedValue(complete, path, value); }); - return completeValues; + return complete; }; const createSubmissionHandler = (originalHandler) => { @@ -103,8 +87,8 @@ export const FEATURE_CONFIGS = { ["md_responsibility_centers", "profits"], ["md_responsibility_centers", "defaults"], ["md_responsibility_centers", "dms_defaults"], - ["md_responsibility_centers", "taxes", "itemexemptcode"], - ["md_responsibility_centers", "taxes", "invoiceexemptcode"], + ["md_responsibility_centers", "taxes"], + ["md_responsibility_centers", "cieca_pfl"], ["md_responsibility_centers", "ar"], ["md_responsibility_centers", "refund"], ["md_responsibility_centers", "sales_tax_codes"], From edaeb5d77aacb3edad3c4c585ca8b672e4a8f9cf Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Thu, 4 Sep 2025 15:16:48 -0700 Subject: [PATCH 4/7] IO-3355 Job Cash Discounting Signed-off-by: Allan Carr --- ...totals.cash-discount-display.component.jsx | 46 +++++-------- server/intellipay/intellipay.js | 69 +++++-------------- 2 files changed, 37 insertions(+), 78 deletions(-) diff --git a/client/src/components/job-totals-table/jobs-totals.cash-discount-display.component.jsx b/client/src/components/job-totals-table/jobs-totals.cash-discount-display.component.jsx index e5a89e23c..409e2e5dc 100644 --- a/client/src/components/job-totals-table/jobs-totals.cash-discount-display.component.jsx +++ b/client/src/components/job-totals-table/jobs-totals.cash-discount-display.component.jsx @@ -20,35 +20,27 @@ export function JobTotalsCashDiscount({ bodyshop, amountDinero }) { const notification = useNotification(); const fetchData = useCallback(async () => { - if (amountDinero && bodyshop) { - setLoading(true); - let response; - try { - response = await axios.post("/intellipay/checkfee", { - bodyshop: { id: bodyshop.id, imexshopid: bodyshop.imexshopid, state: bodyshop.state }, - amount: Dinero(amountDinero).toFormat("0.00") - }); + if (!amountDinero || !bodyshop) return; - if (response?.data?.error) { - notification.open({ - type: "error", - message: - response.data?.error || - "Error encountered when contacting IntelliPay service to determine cash discounted price." - }); - } else { - setFee(response.data?.fee || 0); - } - } catch (error) { - notification.open({ - type: "error", - message: - error.response?.data?.error || - "Error encountered when contacting IntelliPay service to determine cash discounted price." - }); - } finally { - setLoading(false); + setLoading(true); + const errorMessage = "Error encountered when contacting IntelliPay service to determine cash discounted price."; + + try { + const { id, imexshopid, state } = bodyshop; + const { data } = await axios.post("/intellipay/checkfee", { + bodyshop: { id, imexshopid, state }, + amount: Dinero(amountDinero).toUnit() + }); + + if (data?.error) { + notification.open({ type: "error", message: data.error || errorMessage }); + } else { + setFee(data?.fee ?? 0); } + } catch (error) { + notification.open({ type: "error", message: error.response?.data?.error || errorMessage }); + } finally { + setLoading(false); } }, [amountDinero, bodyshop, notification]); diff --git a/server/intellipay/intellipay.js b/server/intellipay/intellipay.js index faa725faf..bc6c0763f 100644 --- a/server/intellipay/intellipay.js +++ b/server/intellipay/intellipay.js @@ -252,35 +252,27 @@ const generatePaymentUrl = async (req, res) => { * @returns {Promise} */ const checkFee = async (req, res) => { - const logResponseMeta = { - bodyshop: { - id: req.body?.bodyshop?.id, - imexshopid: req.body?.bodyshop?.imexshopid, - name: req.body?.bodyshop?.shopname, - state: req.body?.bodyshop?.state - }, - amount: req.body?.amount - }; + const { bodyshop = {}, amount } = req.body || {}; + const { id, imexshopid, shopname, state } = bodyshop; + const logResponseMeta = { bodyshop: { id, imexshopid, name: shopname, state }, amount }; logger.log("intellipay-checkfee-request-received", "DEBUG", req.user?.email, null, logResponseMeta); - if (!isNumber(req.body?.amount) || req.body?.amount <= 0) { + if (!isNumber(amount) || amount <= 0) { logger.log("intellipay-checkfee-skip", "DEBUG", req.user?.email, null, { message: "Amount is zero or undefined, skipping fee check.", ...logResponseMeta }); - return res.json({ fee: 0 }); } - const shopCredentials = await getShopCredentials(req.body.bodyshop); + const shopCredentials = await getShopCredentials(bodyshop); if (shopCredentials?.error) { logger.log("intellipay-checkfee-credentials-error", "ERROR", req.user?.email, null, { message: shopCredentials.error?.message, ...logResponseMeta }); - return res.status(400).json({ error: shopCredentials.error?.message, ...logResponseMeta }); } @@ -292,13 +284,10 @@ const checkFee = async (req, res) => { { method: "fee", ...shopCredentials, - amount: req.body.amount, - paymenttype: `CC`, + amount: String(amount), // Type cast to string as required by API + paymenttype: "CC", cardnum: "4111111111111111", // Required for compatibility with API - state: - req.body.bodyshop?.state && req.body.bodyshop.state.length === 2 - ? req.body.bodyshop.state.toUpperCase() - : "ZZ" + state: state?.toUpperCase() || "ZZ" }, { sort: false } // Ensure query string order is preserved ), @@ -310,46 +299,24 @@ const checkFee = async (req, res) => { ...logResponseMeta }); - const response = await axios(options); + const { data } = await axios(options); - if (response.data?.error) { - logger.log("intellipay-checkfee-api-error", "ERROR", req.user?.email, null, { - message: response.data?.error, - ...logResponseMeta - }); - - return res.status(400).json({ - error: response.data?.error, - type: "intellipay-checkfee-api-error", - ...logResponseMeta - }); + if (data?.error || data < 0) { + const errorType = data?.error ? "intellipay-checkfee-api-error" : "intellipay-checkfee-negative-fee"; + const errorMessage = data?.error + ? data?.error + : "Fee amount negative. Check API credentials & account configuration."; + logger.log(errorType, "ERROR", req.user?.email, null, { message: errorMessage, data, ...logResponseMeta }); + return res.status(400).json({ error: errorMessage, type: errorType, data, ...logResponseMeta }); } - if (response.data < 0) { - logger.log("intellipay-checkfee-negative-fee", "ERROR", req.user?.email, null, { - message: "Fee amount returned is negative.", - ...logResponseMeta - }); - - return res.json({ - error: "Fee amount negative. Check API credentials & account configuration.", - ...logResponseMeta, - type: "intellipay-checkfee-negative-fee" - }); - } - - logger.log("intellipay-checkfee-success", "DEBUG", req.user?.email, null, { - fee: response.data, - ...logResponseMeta - }); - - return res.json({ fee: response.data, ...logResponseMeta }); + logger.log("intellipay-checkfee-success", "DEBUG", req.user?.email, null, { fee: data, ...logResponseMeta }); + return res.json({ fee: data, ...logResponseMeta }); } catch (error) { logger.log("intellipay-checkfee-error", "ERROR", req.user?.email, null, { message: error?.message, ...logResponseMeta }); - return res.status(500).json({ error: error?.message, logResponseMeta }); } }; From d9f59fcad4047dba8d5945b06242fb95b124e371 Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Thu, 4 Sep 2025 15:27:06 -0700 Subject: [PATCH 5/7] IO-3355 Correct translation Signed-off-by: Allan Carr --- client/src/components/vendors-form/vendors-form.component.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/vendors-form/vendors-form.component.jsx b/client/src/components/vendors-form/vendors-form.component.jsx index 20aa07d9c..7d2cd1468 100644 --- a/client/src/components/vendors-form/vendors-form.component.jsx +++ b/client/src/components/vendors-form/vendors-form.component.jsx @@ -173,7 +173,7 @@ export function VendorsFormComponent({ bodyshop, form, formLoading, handleDelete Date: Tue, 9 Sep 2025 11:59:08 -0400 Subject: [PATCH 6/7] feature/Reynolds-and-Reynolds-DMS-API-Integration - DB Modifications --- hasura/metadata/tables.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index 952bc7c25..65ed49949 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -959,6 +959,7 @@ - enforce_referral - entegral_configuration - entegral_id + - external_shop_id - features - federal_tax_id - id @@ -1012,6 +1013,8 @@ - prodtargethrs - production_config - region_config + - rr_configuration + - rr_dealerid - schedule_end_time - schedule_start_time - scoreboard_target @@ -1035,7 +1038,6 @@ - use_fippa - use_paint_scale_data - uselocalmediaserver - - external_shop_id - website - workingdays - zip_post @@ -1068,6 +1070,7 @@ - enforce_conversion_category - enforce_conversion_csr - enforce_referral + - external_shop_id - federal_tax_id - id - inhousevendorid @@ -1113,6 +1116,7 @@ - phone - prodtargethrs - production_config + - rr_configuration - schedule_end_time - schedule_start_time - scoreboard_target @@ -1131,7 +1135,6 @@ - use_fippa - use_paint_scale_data - uselocalmediaserver - - external_shop_id - website - workingdays - zip_post From ac9fac458c8e7c9edfde99fabe44c161ea88c4c1 Mon Sep 17 00:00:00 2001 From: Dave Date: Tue, 9 Sep 2025 11:59:18 -0400 Subject: [PATCH 7/7] feature/Reynolds-and-Reynolds-DMS-API-Integration - DB Modifications --- .../down.sql | 4 ++++ .../up.sql | 2 ++ .../down.sql | 4 ++++ .../up.sql | 2 ++ .../down.sql | 1 + .../up.sql | 1 + 6 files changed, 14 insertions(+) create mode 100644 hasura/migrations/1757432557001_alter_table_public_bodyshops_add_column_rr_configuration/down.sql create mode 100644 hasura/migrations/1757432557001_alter_table_public_bodyshops_add_column_rr_configuration/up.sql create mode 100644 hasura/migrations/1757432588201_alter_table_public_bodyshops_add_column_rr_dealierid/down.sql create mode 100644 hasura/migrations/1757432588201_alter_table_public_bodyshops_add_column_rr_dealierid/up.sql create mode 100644 hasura/migrations/1757432608667_alter_table_public_bodyshops_alter_column_rr_dealierid/down.sql create mode 100644 hasura/migrations/1757432608667_alter_table_public_bodyshops_alter_column_rr_dealierid/up.sql diff --git a/hasura/migrations/1757432557001_alter_table_public_bodyshops_add_column_rr_configuration/down.sql b/hasura/migrations/1757432557001_alter_table_public_bodyshops_add_column_rr_configuration/down.sql new file mode 100644 index 000000000..7c166f19a --- /dev/null +++ b/hasura/migrations/1757432557001_alter_table_public_bodyshops_add_column_rr_configuration/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"."bodyshops" add column "rr_configuration" jsonb +-- null default jsonb_build_object(); diff --git a/hasura/migrations/1757432557001_alter_table_public_bodyshops_add_column_rr_configuration/up.sql b/hasura/migrations/1757432557001_alter_table_public_bodyshops_add_column_rr_configuration/up.sql new file mode 100644 index 000000000..07e9ffed7 --- /dev/null +++ b/hasura/migrations/1757432557001_alter_table_public_bodyshops_add_column_rr_configuration/up.sql @@ -0,0 +1,2 @@ +alter table "public"."bodyshops" add column "rr_configuration" jsonb + null default jsonb_build_object(); diff --git a/hasura/migrations/1757432588201_alter_table_public_bodyshops_add_column_rr_dealierid/down.sql b/hasura/migrations/1757432588201_alter_table_public_bodyshops_add_column_rr_dealierid/down.sql new file mode 100644 index 000000000..010c196e7 --- /dev/null +++ b/hasura/migrations/1757432588201_alter_table_public_bodyshops_add_column_rr_dealierid/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"."bodyshops" add column "rr_dealierid" text +-- null; diff --git a/hasura/migrations/1757432588201_alter_table_public_bodyshops_add_column_rr_dealierid/up.sql b/hasura/migrations/1757432588201_alter_table_public_bodyshops_add_column_rr_dealierid/up.sql new file mode 100644 index 000000000..95c11f63d --- /dev/null +++ b/hasura/migrations/1757432588201_alter_table_public_bodyshops_add_column_rr_dealierid/up.sql @@ -0,0 +1,2 @@ +alter table "public"."bodyshops" add column "rr_dealierid" text + null; diff --git a/hasura/migrations/1757432608667_alter_table_public_bodyshops_alter_column_rr_dealierid/down.sql b/hasura/migrations/1757432608667_alter_table_public_bodyshops_alter_column_rr_dealierid/down.sql new file mode 100644 index 000000000..10c07cdcb --- /dev/null +++ b/hasura/migrations/1757432608667_alter_table_public_bodyshops_alter_column_rr_dealierid/down.sql @@ -0,0 +1 @@ +alter table "public"."bodyshops" rename column "rr_dealerid" to "rr_dealierid"; diff --git a/hasura/migrations/1757432608667_alter_table_public_bodyshops_alter_column_rr_dealierid/up.sql b/hasura/migrations/1757432608667_alter_table_public_bodyshops_alter_column_rr_dealierid/up.sql new file mode 100644 index 000000000..60a636aa8 --- /dev/null +++ b/hasura/migrations/1757432608667_alter_table_public_bodyshops_alter_column_rr_dealierid/up.sql @@ -0,0 +1 @@ +alter table "public"."bodyshops" rename column "rr_dealierid" to "rr_dealerid";