From eccc992cfae1b64fd5d968610bdefe163029f963 Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Thu, 16 Jan 2025 18:28:54 -0800 Subject: [PATCH 1/4] IO-2951 RO Guard Labor Warning Signed-off-by: Allan Carr --- .../labor-allocations-table.component.jsx | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/client/src/components/labor-allocations-table/labor-allocations-table.component.jsx b/client/src/components/labor-allocations-table/labor-allocations-table.component.jsx index 57be0b072..1a6b16a7d 100644 --- a/client/src/components/labor-allocations-table/labor-allocations-table.component.jsx +++ b/client/src/components/labor-allocations-table/labor-allocations-table.component.jsx @@ -1,6 +1,5 @@ import { EditFilled } from "@ant-design/icons"; import { Alert, Card, Col, Row, Space, Table, Typography } from "antd"; -import _ from "lodash"; import React, { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; @@ -9,11 +8,11 @@ import { selectTechnician } from "../../redux/tech/tech.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors"; import CurrencyFormatter from "../../utils/CurrencyFormatter"; import { alphaSort } from "../../utils/sorters"; +import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component"; import LaborAllocationsAdjustmentEdit from "../labor-allocations-adjustment-edit/labor-allocations-adjustment-edit.component"; +import UpsellComponent, { upsellEnum } from "../upsell/upsell.component"; import "./labor-allocations-table.styles.scss"; import { CalculateAllocationsTotals } from "./labor-allocations-table.utility"; -import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component"; -import UpsellComponent, { upsellEnum } from "../upsell/upsell.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, technician: selectTechnician @@ -65,6 +64,7 @@ export function LaborAllocationsTable({ key: "total", sorter: (a, b) => a.total - b.total, sortOrder: state.sortedInfo.columnKey === "total" && state.sortedInfo.order, + align: "right", render: (text, record) => record.total.toFixed(1) }, { @@ -73,6 +73,7 @@ export function LaborAllocationsTable({ key: "hrs_claimed", sorter: (a, b) => a.claimed - b.claimed, sortOrder: state.sortedInfo.columnKey === "claimed" && state.sortedInfo.order, + align: "right", render: (text, record) => record.claimed && record.claimed.toFixed(1) }, { @@ -81,6 +82,7 @@ export function LaborAllocationsTable({ key: "adjustments", sorter: (a, b) => a.adjustments - b.adjustments, sortOrder: state.sortedInfo.columnKey === "adjustments" && state.sortedInfo.order, + align: "right", render: (text, record) => ( {record.adjustments.toFixed(1)} @@ -100,17 +102,17 @@ export function LaborAllocationsTable({ { title: t("jobs.labels.difference"), dataIndex: "difference", - key: "difference", sorter: (a, b) => a.difference - b.difference, sortOrder: state.sortedInfo.columnKey === "difference" && state.sortedInfo.order, + align: "right", render: (text, record) => ( = 0 ? "green" : "red" + color: record.difference.toFixed(1) >= 0 ? "green" : "red" }} > - {_.round(record.difference, 1)} + {(Math.abs(record.difference) < 0.05 ? 0 : record.difference).toFixed(1)} ) } @@ -129,7 +131,6 @@ export function LaborAllocationsTable({ ellipsis: true, render: (text, record) => `${record.op_code_desc || ""}${record.alt_partm ? ` ${record.alt_partm}` : ""}` }, - { title: t("joblines.fields.act_price"), dataIndex: "act_price", @@ -187,7 +188,7 @@ export function LaborAllocationsTable({ { hrs_total: 0, hrs_claimed: 0, adjustments: 0, difference: 0 } ); - if (summary.difference !== 0 && typeof warningCallback === "function") { + if (Math.abs(summary.difference.toFixed(1)) !== 0 && typeof warningCallback === "function") { warningCallback({ key: "labor", warning: t("jobs.labels.outstandinghours") }); } @@ -217,19 +218,21 @@ export function LaborAllocationsTable({ summary={() => ( - {t("general.labels.totals")} + + {t("general.labels.totals")} + - {summary.hrs_total.toFixed(1)} - {summary.hrs_claimed.toFixed(1)} - {summary.adjustments.toFixed(1)} - + {summary.hrs_total.toFixed(1)} + {summary.hrs_claimed.toFixed(1)} + {summary.adjustments.toFixed(1)} + = 0 ? "green" : "red" + color: summary.difference.toFixed(1) >= 0 ? "green" : "red" }} > - {summary.difference.toFixed(1)} + {(Math.abs(summary.difference) < 0.05 ? 0 : summary.difference).toFixed(1)} @@ -261,11 +264,10 @@ export function LaborAllocationsTable({ )} - {showWarning && summary.difference !== 0 && ( + {showWarning && Math.abs(summary.difference.toFixed(1)) !== 0 && ( )} ); } - export default connect(mapStateToProps, null)(LaborAllocationsTable); From 584c2e5de28e727bb638a313a8915ca833e5134a Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Thu, 16 Jan 2025 18:39:41 -0800 Subject: [PATCH 2/4] IO-999 Part Tax Rate Label Signed-off-by: Allan Carr --- .../jobs-detail-rates/jobs-detail-rates.parts.component.jsx | 2 +- client/src/translations/en_us/common.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/components/jobs-detail-rates/jobs-detail-rates.parts.component.jsx b/client/src/components/jobs-detail-rates/jobs-detail-rates.parts.component.jsx index d4d39c6a2..39ac6946a 100644 --- a/client/src/components/jobs-detail-rates/jobs-detail-rates.parts.component.jsx +++ b/client/src/components/jobs-detail-rates/jobs-detail-rates.parts.component.jsx @@ -982,7 +982,7 @@ export function JobsDetailRatesParts({ jobRO, expanded, required = true, form }) - + diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index f153a242a..8c948d1a7 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -2053,7 +2053,7 @@ "parts": "Parts", "parts_lines": "Parts Lines", "parts_received": "Parts Rec.", - "parts_tax_rates": "Parts Tax rates", + "parts_tax_rates": "Profile - Parts", "partsfilter": "Parts Only", "partssubletstotal": "Parts & Sublets Total", "partstotal": "Parts Total (ex. Taxes)", From 23bd6085a845692b205d7069cf62b8be7b0e5dde Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Fri, 17 Jan 2025 09:51:55 -0800 Subject: [PATCH 3/4] feature/IO-3096-Global-Notifications-Preferences - Package Updates --- hasura/migrations/1620771761757_Init/up.sql | 130 +++++++++--------- .../1620771761757_Init/up.sql | 130 +++++++++--------- 2 files changed, 130 insertions(+), 130 deletions(-) diff --git a/hasura/migrations/1620771761757_Init/up.sql b/hasura/migrations/1620771761757_Init/up.sql index 3b608100b..0bdef8e89 100644 --- a/hasura/migrations/1620771761757_Init/up.sql +++ b/hasura/migrations/1620771761757_Init/up.sql @@ -39,50 +39,50 @@ END; $$; CREATE FUNCTION public.assign_ro_number() RETURNS trigger LANGUAGE plpgsql - AS $$ - begin - IF NEW.converted = true and (new.ro_number is null or new.ro_number = '') THEN - UPDATE counters - SET count = count + 1 where shopid=new.shopid AND countertype = 'ronum' - RETURNING concat(prefix,count) into new.ro_number; - END IF; - RETURN NEW; - END; + AS $$ + begin + IF NEW.converted = true and (new.ro_number is null or new.ro_number = '') THEN + UPDATE counters + SET count = count + 1 where shopid=new.shopid AND countertype = 'ronum' + RETURNING concat(prefix,count) into new.ro_number; + END IF; + RETURN NEW; + END; $$; CREATE FUNCTION public.audit_trigger() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER - AS $$ - DECLARE - shopid uuid ; - email text; - BEGIN - select b.id, u.email INTO shopid, email from users u join associations a on u.email = a.useremail join bodyshops b on b.id = a.shopid where u.authid = current_setting('hasura.user', 't')::jsonb->>'x-hasura-user-id' and a.active = true; - IF TG_OP = 'INSERT' - THEN - INSERT INTO public.audit_trail (tabname, schemaname, operation, new_val, recordid, bodyshopid, useremail) - VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(NEW), NEW.id, shopid, email); - RETURN NEW; - ELSIF TG_OP = 'UPDATE' - THEN - INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, new_val, recordid, bodyshopid, useremail) - VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, - json_diff(to_jsonb(OLD), to_jsonb(NEW)) , json_diff(to_jsonb(NEW), to_jsonb(OLD)), OLD.id, shopid, email); - RETURN NEW; - ELSIF TG_OP = 'DELETE' - THEN - INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, recordid, bodyshopid, useremail) - VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(OLD), OLD.ID, shopid, email); - RETURN OLD; - END IF; - END; + AS $$ + DECLARE + shopid uuid ; + email text; + BEGIN + select b.id, u.email INTO shopid, email from users u join associations a on u.email = a.useremail join bodyshops b on b.id = a.shopid where u.authid = current_setting('hasura.user', 't')::jsonb->>'x-hasura-user-id' and a.active = true; + IF TG_OP = 'INSERT' + THEN + INSERT INTO public.audit_trail (tabname, schemaname, operation, new_val, recordid, bodyshopid, useremail) + VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(NEW), NEW.id, shopid, email); + RETURN NEW; + ELSIF TG_OP = 'UPDATE' + THEN + INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, new_val, recordid, bodyshopid, useremail) + VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, + json_diff(to_jsonb(OLD), to_jsonb(NEW)) , json_diff(to_jsonb(NEW), to_jsonb(OLD)), OLD.id, shopid, email); + RETURN NEW; + ELSIF TG_OP = 'DELETE' + THEN + INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, recordid, bodyshopid, useremail) + VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(OLD), OLD.ID, shopid, email); + RETURN OLD; + END IF; + END; $$; CREATE FUNCTION public.json_diff(l jsonb, r jsonb) RETURNS jsonb LANGUAGE sql - AS $$ - SELECT jsonb_object_agg(a.key, a.value) FROM - ( SELECT key, value FROM jsonb_each(l) ) a LEFT OUTER JOIN - ( SELECT key, value FROM jsonb_each(r) ) b ON a.key = b.key - WHERE a.value != b.value OR b.key IS NULL; + AS $$ + SELECT jsonb_object_agg(a.key, a.value) FROM + ( SELECT key, value FROM jsonb_each(l) ) a LEFT OUTER JOIN + ( SELECT key, value FROM jsonb_each(r) ) b ON a.key = b.key + WHERE a.value != b.value OR b.key IS NULL; $$; CREATE TABLE public.bills ( id uuid DEFAULT public.gen_random_uuid() NOT NULL, @@ -211,33 +211,33 @@ CREATE TABLE public.exportlog ( ); CREATE FUNCTION public.search_exportlog(search text) RETURNS SETOF public.exportlog LANGUAGE plpgsql STABLE - AS $$ BEGIN IF search = '' THEN RETURN query -SELECT - * -FROM - exportlog e; - ELSE RETURN query -SELECT - e.* -FROM - exportlog e - LEFT JOIN jobs j on j.id = e.jobid -LEFT JOIN payments p - ON p.id = e.paymentid -LEFT JOIN bills b - ON e.billid = b.id -WHERE - ( - j.ro_number ILIKE '%' || search || '%' - OR b.invoice_number ILIKE '%' || search || '%' - OR p.paymentnum ILIKE '%' || search || '%' - OR e.useremail ILIKE '%' || search || '%' - ) - AND (e.jobid = j.id - or e.paymentid = p.id - or e.billid = b.id) -; -END IF; + AS $$ BEGIN IF search = '' THEN RETURN query +SELECT + * +FROM + exportlog e; + ELSE RETURN query +SELECT + e.* +FROM + exportlog e + LEFT JOIN jobs j on j.id = e.jobid +LEFT JOIN payments p + ON p.id = e.paymentid +LEFT JOIN bills b + ON e.billid = b.id +WHERE + ( + j.ro_number ILIKE '%' || search || '%' + OR b.invoice_number ILIKE '%' || search || '%' + OR p.paymentnum ILIKE '%' || search || '%' + OR e.useremail ILIKE '%' || search || '%' + ) + AND (e.jobid = j.id + or e.paymentid = p.id + or e.billid = b.id) +; +END IF; END $$; CREATE TABLE public.jobs ( id uuid DEFAULT public.gen_random_uuid() NOT NULL, diff --git a/hasura/migrations_backup/1620771761757_Init/up.sql b/hasura/migrations_backup/1620771761757_Init/up.sql index 3b608100b..0bdef8e89 100644 --- a/hasura/migrations_backup/1620771761757_Init/up.sql +++ b/hasura/migrations_backup/1620771761757_Init/up.sql @@ -39,50 +39,50 @@ END; $$; CREATE FUNCTION public.assign_ro_number() RETURNS trigger LANGUAGE plpgsql - AS $$ - begin - IF NEW.converted = true and (new.ro_number is null or new.ro_number = '') THEN - UPDATE counters - SET count = count + 1 where shopid=new.shopid AND countertype = 'ronum' - RETURNING concat(prefix,count) into new.ro_number; - END IF; - RETURN NEW; - END; + AS $$ + begin + IF NEW.converted = true and (new.ro_number is null or new.ro_number = '') THEN + UPDATE counters + SET count = count + 1 where shopid=new.shopid AND countertype = 'ronum' + RETURNING concat(prefix,count) into new.ro_number; + END IF; + RETURN NEW; + END; $$; CREATE FUNCTION public.audit_trigger() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER - AS $$ - DECLARE - shopid uuid ; - email text; - BEGIN - select b.id, u.email INTO shopid, email from users u join associations a on u.email = a.useremail join bodyshops b on b.id = a.shopid where u.authid = current_setting('hasura.user', 't')::jsonb->>'x-hasura-user-id' and a.active = true; - IF TG_OP = 'INSERT' - THEN - INSERT INTO public.audit_trail (tabname, schemaname, operation, new_val, recordid, bodyshopid, useremail) - VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(NEW), NEW.id, shopid, email); - RETURN NEW; - ELSIF TG_OP = 'UPDATE' - THEN - INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, new_val, recordid, bodyshopid, useremail) - VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, - json_diff(to_jsonb(OLD), to_jsonb(NEW)) , json_diff(to_jsonb(NEW), to_jsonb(OLD)), OLD.id, shopid, email); - RETURN NEW; - ELSIF TG_OP = 'DELETE' - THEN - INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, recordid, bodyshopid, useremail) - VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(OLD), OLD.ID, shopid, email); - RETURN OLD; - END IF; - END; + AS $$ + DECLARE + shopid uuid ; + email text; + BEGIN + select b.id, u.email INTO shopid, email from users u join associations a on u.email = a.useremail join bodyshops b on b.id = a.shopid where u.authid = current_setting('hasura.user', 't')::jsonb->>'x-hasura-user-id' and a.active = true; + IF TG_OP = 'INSERT' + THEN + INSERT INTO public.audit_trail (tabname, schemaname, operation, new_val, recordid, bodyshopid, useremail) + VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(NEW), NEW.id, shopid, email); + RETURN NEW; + ELSIF TG_OP = 'UPDATE' + THEN + INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, new_val, recordid, bodyshopid, useremail) + VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, + json_diff(to_jsonb(OLD), to_jsonb(NEW)) , json_diff(to_jsonb(NEW), to_jsonb(OLD)), OLD.id, shopid, email); + RETURN NEW; + ELSIF TG_OP = 'DELETE' + THEN + INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, recordid, bodyshopid, useremail) + VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(OLD), OLD.ID, shopid, email); + RETURN OLD; + END IF; + END; $$; CREATE FUNCTION public.json_diff(l jsonb, r jsonb) RETURNS jsonb LANGUAGE sql - AS $$ - SELECT jsonb_object_agg(a.key, a.value) FROM - ( SELECT key, value FROM jsonb_each(l) ) a LEFT OUTER JOIN - ( SELECT key, value FROM jsonb_each(r) ) b ON a.key = b.key - WHERE a.value != b.value OR b.key IS NULL; + AS $$ + SELECT jsonb_object_agg(a.key, a.value) FROM + ( SELECT key, value FROM jsonb_each(l) ) a LEFT OUTER JOIN + ( SELECT key, value FROM jsonb_each(r) ) b ON a.key = b.key + WHERE a.value != b.value OR b.key IS NULL; $$; CREATE TABLE public.bills ( id uuid DEFAULT public.gen_random_uuid() NOT NULL, @@ -211,33 +211,33 @@ CREATE TABLE public.exportlog ( ); CREATE FUNCTION public.search_exportlog(search text) RETURNS SETOF public.exportlog LANGUAGE plpgsql STABLE - AS $$ BEGIN IF search = '' THEN RETURN query -SELECT - * -FROM - exportlog e; - ELSE RETURN query -SELECT - e.* -FROM - exportlog e - LEFT JOIN jobs j on j.id = e.jobid -LEFT JOIN payments p - ON p.id = e.paymentid -LEFT JOIN bills b - ON e.billid = b.id -WHERE - ( - j.ro_number ILIKE '%' || search || '%' - OR b.invoice_number ILIKE '%' || search || '%' - OR p.paymentnum ILIKE '%' || search || '%' - OR e.useremail ILIKE '%' || search || '%' - ) - AND (e.jobid = j.id - or e.paymentid = p.id - or e.billid = b.id) -; -END IF; + AS $$ BEGIN IF search = '' THEN RETURN query +SELECT + * +FROM + exportlog e; + ELSE RETURN query +SELECT + e.* +FROM + exportlog e + LEFT JOIN jobs j on j.id = e.jobid +LEFT JOIN payments p + ON p.id = e.paymentid +LEFT JOIN bills b + ON e.billid = b.id +WHERE + ( + j.ro_number ILIKE '%' || search || '%' + OR b.invoice_number ILIKE '%' || search || '%' + OR p.paymentnum ILIKE '%' || search || '%' + OR e.useremail ILIKE '%' || search || '%' + ) + AND (e.jobid = j.id + or e.paymentid = p.id + or e.billid = b.id) +; +END IF; END $$; CREATE TABLE public.jobs ( id uuid DEFAULT public.gen_random_uuid() NOT NULL, From fa7e0a107b1be3c8350cf7c0bde591bd399ae95c Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Fri, 17 Jan 2025 10:13:41 -0800 Subject: [PATCH 4/4] hotfix/AdditionalProductFruitsIds - Add additional IDs for product fruits --- .../job-detail-lines/job-lines.component.jsx | 4 +- .../owner-find-modal.container.jsx | 6 +- hasura/migrations/1620771761757_Init/up.sql | 130 +++++++++--------- .../1620771761757_Init/up.sql | 130 +++++++++--------- 4 files changed, 138 insertions(+), 132 deletions(-) diff --git a/client/src/components/job-detail-lines/job-lines.component.jsx b/client/src/components/job-detail-lines/job-lines.component.jsx index a774bac6b..455caefd6 100644 --- a/client/src/components/job-detail-lines/job-lines.component.jsx +++ b/client/src/components/job-detail-lines/job-lines.component.jsx @@ -519,6 +519,7 @@ export function JobLinesComponent({ {selectedLines.length > 0 && ` (${selectedLines.length})`} - +