diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 22b12f42a..f69761e00 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -1085,6 +1085,48 @@ + + responsibilitycenter_accountname + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + responsibilitycenter_accountnumber + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + responsibilitycenters @@ -13662,6 +13704,27 @@ + + jobs-close + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + jobs-detail false @@ -14105,6 +14168,27 @@ + + jobs-close + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + jobs-create false diff --git a/client/src/components/invoice-form/invoice-form.lines.component.jsx b/client/src/components/invoice-form/invoice-form.lines.component.jsx index 1efc76ff7..21fed7a99 100644 --- a/client/src/components/invoice-form/invoice-form.lines.component.jsx +++ b/client/src/components/invoice-form/invoice-form.lines.component.jsx @@ -147,7 +147,7 @@ export default function InvoiceEnterModalLinesComponent({ > diff --git a/client/src/components/jobs-close-labmat-allocation/jobs-close-labmat-allocation.component.jsx b/client/src/components/jobs-close-labmat-allocation/jobs-close-labmat-allocation.component.jsx new file mode 100644 index 000000000..954ab2480 --- /dev/null +++ b/client/src/components/jobs-close-labmat-allocation/jobs-close-labmat-allocation.component.jsx @@ -0,0 +1,35 @@ +import React from "react"; + +export default function JobCloseLabMatAllocation({ + labmatAllocations, + setLabmatAllocations, +}) { + console.log( + "JobCloseLabMatAllocation -> labmatAllocations", + labmatAllocations + ); + return ( +
+ + + + + + + + + {Object.keys(labmatAllocations).map((alloc) => ( + + + + + ))} + +
RateAvailable
{alloc} + { + //labmatAllocations[alloc].total.toFormat() + } +
+
+ ); +} diff --git a/client/src/components/jobs-list-paginated/jobs-list-paginated.component.jsx b/client/src/components/jobs-list-paginated/jobs-list-paginated.component.jsx index ca487df19..95d048b36 100644 --- a/client/src/components/jobs-list-paginated/jobs-list-paginated.component.jsx +++ b/client/src/components/jobs-list-paginated/jobs-list-paginated.component.jsx @@ -64,10 +64,10 @@ export default function JobsList({ render: (text, record) => { return record.owner ? ( - {`${record.ownr_fn} ${record.ownr_ln}`} + {`${record.ownr_fn || ""} ${record.ownr_ln || ""}`} ) : ( - {`${record.ownr_fn} ${record.ownr_ln}`} + {`${record.ownr_fn || ""} ${record.ownr_ln || ""}`} ); }, }, diff --git a/client/src/components/jobs-list/jobs-list.component.jsx b/client/src/components/jobs-list/jobs-list.component.jsx index 4960d26d0..865af88e0 100644 --- a/client/src/components/jobs-list/jobs-list.component.jsx +++ b/client/src/components/jobs-list/jobs-list.component.jsx @@ -32,37 +32,41 @@ export default withRouter(function JobsList({ title: t("jobs.fields.ro_number"), dataIndex: "ro_number", key: "ro_number", - width: "8%", - sorter: (a, b) => - alphaSort( - a.ro_number ? a.ro_number : a.est_number, - b.ro_number ? b.ro_number : b.est_number - ), + sorter: (a, b) => alphaSort(a.ro_number, b.ro_number), sortOrder: state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order, render: (text, record) => ( - - {record.ro_number ? record.ro_number : record.est_number} - + {record.ro_number} ), }, + { + title: t("jobs.fields.est_number"), + dataIndex: "est_number", + key: "est_number", + sorter: (a, b) => a.est_number - b.est_number, + sortOrder: + state.sortedInfo.columnKey === "est_number" && state.sortedInfo.order, + + render: (text, record) => ( + {record.est_number} + ), + }, + { title: t("jobs.fields.owner"), dataIndex: "owner", key: "owner", - ellipsis: true, sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln), - width: "25%", sortOrder: state.sortedInfo.columnKey === "owner" && state.sortedInfo.order, render: (text, record) => { return record.owner ? ( - {`${record.ownr_fn} ${record.ownr_ln}`} + {`${record.ownr_fn || ""} ${record.ownr_ln || ""}`} ) : ( - {`${record.ownr_fn} ${record.ownr_ln}`} + {`${record.ownr_fn || ""} ${record.ownr_ln || ""}`} ); }, }, @@ -70,7 +74,6 @@ export default withRouter(function JobsList({ title: t("jobs.fields.ownr_ph1"), dataIndex: "ownr_ph1", key: "ownr_ph1", - width: "12%", ellipsis: true, render: (text, record) => { return record.ownr_ph1 ? ( @@ -85,8 +88,6 @@ export default withRouter(function JobsList({ title: t("jobs.fields.status"), dataIndex: "status", key: "status", - width: "10%", - ellipsis: true, sorter: (a, b) => alphaSort(a.status, b.status), sortOrder: state.sortedInfo.columnKey === "status" && state.sortedInfo.order, @@ -99,7 +100,6 @@ export default withRouter(function JobsList({ title: t("jobs.fields.vehicle"), dataIndex: "vehicle", key: "vehicle", - width: "15%", ellipsis: true, render: (text, record) => { return record.vehicleid ? ( @@ -119,8 +119,6 @@ export default withRouter(function JobsList({ title: t("vehicles.fields.plate_no"), dataIndex: "plate_no", key: "plate_no", - width: "8%", - ellipsis: true, sorter: (a, b) => alphaSort(a.plate_no, b.plate_no), sortOrder: state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order, @@ -132,7 +130,6 @@ export default withRouter(function JobsList({ title: t("jobs.fields.clm_no"), dataIndex: "clm_no", key: "clm_no", - width: "12%", ellipsis: true, sorter: (a, b) => alphaSort(a.clm_no, b.clm_no), sortOrder: @@ -149,7 +146,6 @@ export default withRouter(function JobsList({ title: t("jobs.fields.clm_total"), dataIndex: "clm_total", key: "clm_total", - width: "10%", sorter: (a, b) => a.clm_total - b.clm_total, sortOrder: state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order, @@ -165,7 +161,6 @@ export default withRouter(function JobsList({ title: t("jobs.fields.owner_owing"), dataIndex: "owner_owing", key: "owner_owing", - width: "8%", render: (text, record) => ( {record.owner_owing} ), @@ -210,10 +205,10 @@ export default withRouter(function JobsList({ ); }} - size="small" + size='small' pagination={{ position: "top" }} columns={columns.map((item) => ({ ...item }))} - rowKey="id" + rowKey='id' dataSource={jobs} rowSelection={{ onSelect: (record) => { diff --git a/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx b/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx index 9dea09c1c..6b18d26af 100644 --- a/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx +++ b/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx @@ -15,433 +15,468 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) { const [options, setOptions] = useState( [ - ...(form.getFieldValue(["md_responsibility_centers", "costs"]) || []), - ...(form.getFieldValue(["md_responsibility_centers", "profits"]) || []) + ...(form + .getFieldValue(["md_responsibility_centers", "costs"]) + .map((i) => i.name) || []), + ...(form + .getFieldValue(["md_responsibility_centers", "profits"]) + .map((i) => i.name) || []), ] || [] ); const handleBlur = () => { setOptions([ - ...(form.getFieldValue(["md_responsibility_centers", "costs"]) || []), - ...(form.getFieldValue(["md_responsibility_centers", "profits"]) || []) + ...(form + .getFieldValue(["md_responsibility_centers", "costs"]) + .map((i) => i.name) || []), + ...(form + .getFieldValue(["md_responsibility_centers", "profits"]) + .map((i) => i.name) || []), ]); }; return (
- {t("bodyshop.labels.responsibilitycenters.title")} - - - {t("bodyshop.labels.responsibilitycenters.costs")} - - {(fields, { add, remove }) => { - return ( -
- {fields.map((field, index) => ( - -
- - - - { - remove(field.name); - }} - /> -
-
- ))} - - + {t("bodyshop.labels.responsibilitycenters.title")}= +
+ {t("bodyshop.labels.responsibilitycenters.costs")} + + {(fields, { add, remove }) => { + return ( +
+ {fields.map((field, index) => ( + +
+ + + + + + + + + + { + remove(field.name); + }} + /> +
-
- ); - }} -
- - - - {t("bodyshop.labels.responsibilitycenters.profits")} - - {(fields, { add, remove }) => { - return ( -
- {fields.map((field, index) => ( - -
- - - - { - remove(field.name); - }} - /> -
-
- ))} - - + ))} + + + +
+ ); + }} +
+
+
+ {t("bodyshop.labels.responsibilitycenters.profits")} + + {(fields, { add, remove }) => { + return ( +
+ {fields.map((field, index) => ( + +
+ + + + + + + + + + { + remove(field.name); + }} + /> +
-
- ); - }} -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + +
+ ); + }} + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
); } diff --git a/client/src/components/time-ticket-modal/time-ticket-modal.component.jsx b/client/src/components/time-ticket-modal/time-ticket-modal.component.jsx index 3af0d4e96..443b01705 100644 --- a/client/src/components/time-ticket-modal/time-ticket-modal.component.jsx +++ b/client/src/components/time-ticket-modal/time-ticket-modal.component.jsx @@ -107,7 +107,7 @@ export default function TimeTicketModalComponent({ }} > {responsibilityCenters.costs.map((item) => ( - {item} + {item.name} ))} diff --git a/client/src/components/vendors-form/vendors-form.component.jsx b/client/src/components/vendors-form/vendors-form.component.jsx index 0823b661a..b3c96cf56 100644 --- a/client/src/components/vendors-form/vendors-form.component.jsx +++ b/client/src/components/vendors-form/vendors-form.component.jsx @@ -151,7 +151,7 @@ export default function VendorsFormComponent({ > diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js index d516c2025..fcd7ed13c 100644 --- a/client/src/graphql/jobs.queries.js +++ b/client/src/graphql/jobs.queries.js @@ -750,3 +750,141 @@ export const QUERY_ALL_JOBS_PAGINATED = gql` } } `; + +export const QUERY_JOB_CLOSE_DETAILS = gql` + query QUERY_JOB_CLOSE_DETAILS($id: uuid!) { + jobs_by_pk(id: $id) { + po_number + special_coverage_policy + scheduled_delivery + converted + est_number + ro_number + clm_total + inproduction + plate_no + v_vin + v_model_yr + v_model_desc + v_make_desc + v_color + ins_co_id + policy_no + loss_date + clm_no + area_of_damage + ins_co_nm + ins_addr1 + ins_city + ins_ct_ln + ins_ct_fn + ins_ea + ins_ph1 + est_co_nm + est_ct_fn + est_ct_ln + pay_date + est_ph1 + est_ea + regie_number + scheduled_completion + id + ded_amt + ded_status + depreciation_taxes + federal_tax_payable + other_amount_payable + towing_payable + storage_payable + adjustment_bottom_line + federal_tax_rate + state_tax_rate + local_tax_rate + tax_tow_rt + tax_str_rt + tax_paint_mat_rt + tax_sub_rt + tax_lbr_rt + tax_levies_rt + parts_tax_rates + job_totals + ownr_fn + ownr_ln + ownr_ea + ownr_addr1 + ownr_addr2 + ownr_city + ownr_st + ownr_zip + ownr_ctry + ownr_ph1 + owner { + id + ownr_fn + ownr_ln + ownr_ea + ownr_addr1 + ownr_addr2 + ownr_city + ownr_st + ownr_zip + ownr_ctry + ownr_ph1 + } + labor_rate_desc + rate_atp + rate_la1 + rate_la2 + rate_la3 + rate_la4 + rate_laa + rate_lab + rate_lad + rate_lae + rate_laf + rate_lag + rate_lam + rate_lar + rate_las + rate_lau + rate_ma2s + rate_ma2t + rate_ma3s + rate_mabl + rate_macs + rate_mahw + rate_mapa + rate_mash + rate_matd + actual_completion + scheduled_delivery + actual_delivery + date_invoiced + date_closed + date_exported + status + owner_owing + joblines { + id + unq_seq + line_ind + tax_part + line_desc + prt_dsmk_p + prt_dsmk_m + part_type + oem_partno + db_price + act_price + part_qty + mod_lbr_ty + db_hrs + mod_lb_hrs + lbr_op + lbr_amt + op_code_desc + } + cieca_ttl + } + } +`; diff --git a/client/src/pages/jobs-close/jobs-close.component.jsx b/client/src/pages/jobs-close/jobs-close.component.jsx new file mode 100644 index 000000000..c487af91a --- /dev/null +++ b/client/src/pages/jobs-close/jobs-close.component.jsx @@ -0,0 +1,31 @@ +import React, { useCallback, useState } from "react"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { CalculateJob } from "../../components/job-totals-table/job-totals.utility"; +import JobsCloseLaborMaterialAllocation from "../../components/jobs-close-labmat-allocation/jobs-close-labmat-allocation.component"; +import { selectBodyshop } from "../../redux/user/user.selectors"; +const mapStateToProps = createStructuredSelector({ + //currentUser: selectCurrentUser + bodyshop: selectBodyshop, +}); + +export function JobsCloseComponent({ job, bodyshop }) { + const CalcJobMemoized = useCallback( + () => CalculateJob(job, bodyshop.shoprates), + [job, bodyshop.shoprates] + ); + + const jobTotals = CalcJobMemoized(); + + const [labmatAllocations, setLabmatAllocations] = useState(jobTotals.rates); + + return ( +
+ +
+ ); +} +export default connect(mapStateToProps, null)(JobsCloseComponent); diff --git a/client/src/pages/jobs-close/jobs-close.container.jsx b/client/src/pages/jobs-close/jobs-close.container.jsx new file mode 100644 index 000000000..bf85ce792 --- /dev/null +++ b/client/src/pages/jobs-close/jobs-close.container.jsx @@ -0,0 +1,53 @@ +import { useQuery } from "@apollo/react-hooks"; +import React, { useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { connect } from "react-redux"; +import { useParams } from "react-router-dom"; +import AlertComponent from "../../components/alert/alert.component"; +import { QUERY_JOB_CLOSE_DETAILS } from "../../graphql/jobs.queries"; +import { setBreadcrumbs } from "../../redux/application/application.actions"; +import JobsCloseComponent from "./jobs-close.component"; +import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component"; + +const mapDispatchToProps = (dispatch) => ({ + setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)), +}); + +export function JobsCloseContainer({ setBreadcrumbs }) { + const { jobId } = useParams(); + const { loading, error, data } = useQuery(QUERY_JOB_CLOSE_DETAILS, { + variables: { id: jobId }, + }); + const { t } = useTranslation(); + useEffect(() => { + document.title = t("titles.jobs-close", { + number: data ? data.jobs_by_pk.ro_number : null, + }); + + setBreadcrumbs([ + { + link: `/manage/jobs/${jobId}/`, + label: t("titles.bc.jobs"), + }, + { + link: `/manage/jobs/${jobId}/`, + label: t("titles.bc.jobs-detail", { + number: data ? data.jobs_by_pk.ro_number : null, + }), + }, + { + link: `/manage/jobs/${jobId}/close`, + label: t("titles.bc.jobs-close"), + }, + ]); + }, [setBreadcrumbs, t, jobId, data]); + + if (loading) return ; + if (error) return ; + return ( +
+ +
+ ); +} +export default connect(null, mapDispatchToProps)(JobsCloseContainer); diff --git a/client/src/pages/manage/manage.page.component.jsx b/client/src/pages/manage/manage.page.component.jsx index 25e7e7bfb..e304a3ba6 100644 --- a/client/src/pages/manage/manage.page.component.jsx +++ b/client/src/pages/manage/manage.page.component.jsx @@ -87,6 +87,7 @@ const JobIntake = lazy(() => import("../jobs-intake/jobs-intake.page.container") ); const AllJobs = lazy(() => import("../jobs-all/jobs-all.container")); +const JobsClose = lazy(() => import("../jobs-close/jobs-close.container")); const { Header, Content, Footer } = Layout; @@ -125,7 +126,12 @@ export default function Manage({ match }) { exact path={`${match.path}/jobs/:jobId/intake`} component={JobIntake} - />{" "} + /> +