Merge branch 'release/2021-09-10' into feature/cdk-cert

This commit is contained in:
Patrick Fic
2021-09-13 10:08:15 -07:00
10 changed files with 7944 additions and 7511 deletions

View File

@@ -5819,6 +5819,27 @@
<folder_node> <folder_node>
<name>responsibilitycenters</name> <name>responsibilitycenters</name>
<children> <children>
<concept_node>
<name>LA4</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>ap</name> <name>ap</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -5903,6 +5924,69 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>la1</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>la2</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>la3</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>lab</name> <name>lab</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -6344,6 +6428,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>pasl</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>refund</name> <name>refund</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>

View File

@@ -81,11 +81,7 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
<span style={{ margin: "0rem .5rem" }}>/</span> <span style={{ margin: "0rem .5rem" }}>/</span>
<CurrencyFormatter>{job.owner_owing}</CurrencyFormatter> <CurrencyFormatter>{job.owner_owing}</CurrencyFormatter>
</DataLabel> </DataLabel>
{job.converted && (
<DataLabel label={t("jobs.labels.relatedros")}>
<JobsRelatedRos jobid={job.id} />
</DataLabel>
)}
<DataLabel label={t("jobs.fields.alt_transport")}> <DataLabel label={t("jobs.fields.alt_transport")}>
{job.alt_transport} {job.alt_transport}
<JobAltTransportChange job={job} /> <JobAltTransportChange job={job} />
@@ -183,6 +179,9 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
<DataLabel key="4" label={t("vehicles.fields.v_vin")}> <DataLabel key="4" label={t("vehicles.fields.v_vin")}>
{`${job.v_vin || t("general.labels.na")}`} {`${job.v_vin || t("general.labels.na")}`}
</DataLabel> </DataLabel>
<DataLabel label={t("jobs.labels.relatedros")}>
<JobsRelatedRos jobid={job.id} job={job} />
</DataLabel>
</div> </div>
</Card> </Card>
</Col> </Col>

View File

@@ -1,112 +1,19 @@
import React, { useState } from "react"; import { Space, Tag } from "antd";
import { useQuery, useMutation } from "@apollo/client"; import React from "react";
import { Tag, Space, Button, Popover, Card, Form } from "antd";
import {
DELETE_RELATED_RO,
INSERT_RELATED_ROS,
QUERY_RELATED_ROS,
} from "../../graphql/jobs.queries";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import AlertComponent from "../alert/alert.component";
import { PlusCircleOutlined } from "@ant-design/icons";
import JobSearchSelectComponent from "../job-search-select/job-search-select.component";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
export default function JobsRelatedRos({ jobid }) { export default function JobsRelatedRos({ jobid, job }) {
const [roSearchVisible, setRoSearchVisible] = useState(false);
const [saveLoading, setSaveLoading] = useState(false);
const [insertRelationship] = useMutation(INSERT_RELATED_ROS);
const [deleteRelationship] = useMutation(DELETE_RELATED_RO);
const { loading, error, data } = useQuery(QUERY_RELATED_ROS, {
variables: { jobid },
skip: !jobid,
});
const { t } = useTranslation();
if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type="error" />;
const relatedJobs = data.relatedjobs.map((r) => {
if (r.parentjob === jobid) {
return { relationshipid: r.id, ...r.childjob_rel };
}
return { relationshipid: r.id, ...r.parentjob_rel };
});
const handleAddRo = async ({ relatedjobid }) => {
setSaveLoading(true);
await insertRelationship({
variables: { relationship: { parentjob: jobid, childjob: relatedjobid } },
update(cache, { data }) {
cache.modify({
fields: {
relatedjobs(rj, { readField }) {
return [rj, data.insert_relatedjobs_one];
},
},
});
},
});
setSaveLoading(false);
setRoSearchVisible(false);
};
const handleDelete = async (id) => {
setSaveLoading(true);
await deleteRelationship({
variables: {
relationshipid: id,
},
update(cache, { data }) {
cache.modify({
fields: {
relatedjobs(rj, { readField }) {
return rj.filter((r) => r.id !== id);
},
},
});
},
});
setSaveLoading(false);
setRoSearchVisible(false);
};
const popContent = (
<Card style={{ minWidth: "25rem" }}>
<Form layout="vertical" onFinish={handleAddRo}>
<Form.Item
name="relatedjobid"
label={t("jobs.fields.ro_number")}
rules={[{ required: true }]}
>
<JobSearchSelectComponent convertedOnly />
</Form.Item>
<Space>
<Button loading={saveLoading} htmlType="submit">
{t("general.actions.add")}
</Button>
<Button onClick={() => setRoSearchVisible(false)}>
{t("general.actions.cancel")}
</Button>
</Space>
</Form>
</Card>
);
return ( return (
<Space wrap> <Space wrap>
{relatedJobs.map((r) => ( {job.vehicle.jobs
<Tag key={r.id} closable onClose={() => handleDelete(r.relationshipid)}> .filter((j) => j.id !== job.id)
<Link to={`/manage/jobs/${r?.id}`}>{r.ro_number}</Link> .map((j) => (
<Tag key={j.id}>
<Link to={`/manage/jobs/${j?.id}`}>{`${j.ro_number || "N/A"}${
j.clm_no ? ` | ${j.clm_no}` : ""
}${j.status ? ` | ${j.status}` : ""}`}</Link>
</Tag> </Tag>
))} ))}
<Popover content={popContent} visible={roSearchVisible}>
<Button type="link" onClick={() => setRoSearchVisible(true)}>
<PlusCircleOutlined />
</Button>
</Popover>
</Space> </Space>
); );
} }

View File

@@ -698,6 +698,90 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
))} ))}
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenters.la1"
)}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
key={`${index}costs-LA1`}
name={[field.name, "costs", "LA1"]}
>
<Select>
{costOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenters.la2"
)}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
key={`${index}costs-LA2`}
name={[field.name, "costs", "LA2"]}
>
<Select>
{costOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenters.la3"
)}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
key={`${index}costs-LA3`}
name={[field.name, "costs", "LA3"]}
>
<Select>
{costOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenters.la4"
)}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
key={`${index}costs-LA4`}
name={[field.name, "costs", "LA4"]}
>
<Select>
{costOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item <Form.Item
label={t( label={t(
"bodyshop.fields.responsibilitycenters.paa" "bodyshop.fields.responsibilitycenters.paa"
@@ -887,6 +971,27 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
))} ))}
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenters.pasl"
)}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
key={`${index}costs-PASL`}
name={[field.name, "costs", "PASL"]}
>
<Select>
{costOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item <Form.Item
label={t( label={t(
"bodyshop.fields.responsibilitycenters.tow" "bodyshop.fields.responsibilitycenters.tow"
@@ -1164,6 +1269,90 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
))} ))}
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenters.la1"
)}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
key={`${index}profits-LA1`}
name={[field.name, "profits", "LA1"]}
>
<Select>
{profitOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenters.la2"
)}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
key={`${index}profits-LA2`}
name={[field.name, "profits", "LA2"]}
>
<Select>
{profitOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenters.la3"
)}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
key={`${index}profits-LA3`}
name={[field.name, "profits", "LA3"]}
>
<Select>
{profitOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenters.la4"
)}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
key={`${index}profits-LA4`}
name={[field.name, "profits", "LA4"]}
>
<Select>
{profitOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item <Form.Item
label={t( label={t(
"bodyshop.fields.responsibilitycenters.paa" "bodyshop.fields.responsibilitycenters.paa"
@@ -1353,6 +1542,27 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
))} ))}
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenters.pasl"
)}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
key={`${index}profits-PASL`}
name={[field.name, "profits", "PASL"]}
>
<Select>
{profitOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item <Form.Item
label={t( label={t(
"bodyshop.fields.responsibilitycenters.tow" "bodyshop.fields.responsibilitycenters.tow"
@@ -1625,6 +1835,78 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
))} ))}
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.la1")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "defaults", "costs", "LA1"]}
>
<Select>
{costOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.la2")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "defaults", "costs", "LA2"]}
>
<Select>
{costOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.la3")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "defaults", "costs", "LA3"]}
>
<Select>
{costOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.la4")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "defaults", "costs", "LA4"]}
>
<Select>
{costOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.responsibilitycenters.paa")} label={t("bodyshop.fields.responsibilitycenters.paa")}
rules={[ rules={[
@@ -1787,6 +2069,24 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
))} ))}
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.pasl")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "defaults", "costs", "PASL"]}
>
<Select>
{costOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.responsibilitycenters.tow")} label={t("bodyshop.fields.responsibilitycenters.tow")}
rules={[ rules={[
@@ -2023,6 +2323,78 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
))} ))}
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.la1")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "defaults", "profits", "LA1"]}
>
<Select>
{profitOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.la2")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "defaults", "profits", "LA2"]}
>
<Select>
{profitOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.la3")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "defaults", "profits", "LA3"]}
>
<Select>
{profitOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.la4")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "defaults", "profits", "LA4"]}
>
<Select>
{profitOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.responsibilitycenters.paa")} label={t("bodyshop.fields.responsibilitycenters.paa")}
rules={[ rules={[
@@ -2185,6 +2557,24 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
))} ))}
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.pasl")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "defaults", "profits", "PASL"]}
>
<Select>
{profitOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.responsibilitycenters.tow")} label={t("bodyshop.fields.responsibilitycenters.tow")}
rules={[ rules={[

View File

@@ -384,6 +384,12 @@ export const GET_JOB_BY_PK = gql`
v_model_desc v_model_desc
v_make_desc v_make_desc
v_color v_color
jobs {
id
ro_number
status
clm_no
}
} }
available_jobs { available_jobs {
id id
@@ -700,6 +706,11 @@ export const QUERY_JOB_CARD_DETAILS = gql`
v_model_desc v_model_desc
v_color v_color
plate_no plate_no
jobs {
id
clm_no
ro_number
}
} }
actual_completion actual_completion
actual_delivery actual_delivery

View File

@@ -381,10 +381,14 @@
"responsibilitycenter_accountnumber": "Account Number", "responsibilitycenter_accountnumber": "Account Number",
"responsibilitycenter_rate": "Rate", "responsibilitycenter_rate": "Rate",
"responsibilitycenters": { "responsibilitycenters": {
"LA4": "LA4",
"ap": "Accounts Payable", "ap": "Accounts Payable",
"ar": "Accounts Receivable", "ar": "Accounts Receivable",
"ats": "ATS", "ats": "ATS",
"federal_tax": "Federal Tax", "federal_tax": "Federal Tax",
"la1": "LA1",
"la2": "LA2",
"la3": "LA3",
"lab": "Body", "lab": "Body",
"lad": "Diagnostic", "lad": "Diagnostic",
"lae": "Electrical", "lae": "Electrical",
@@ -406,6 +410,7 @@
"pap": "OEM Partial", "pap": "OEM Partial",
"par": "Recored", "par": "Recored",
"pas": "Sublet", "pas": "Sublet",
"pasl": "Sublet (L)",
"refund": "Refund", "refund": "Refund",
"sales_tax_codes": { "sales_tax_codes": {
"code": "Code", "code": "Code",

View File

@@ -381,10 +381,14 @@
"responsibilitycenter_accountnumber": "", "responsibilitycenter_accountnumber": "",
"responsibilitycenter_rate": "", "responsibilitycenter_rate": "",
"responsibilitycenters": { "responsibilitycenters": {
"LA4": "",
"ap": "", "ap": "",
"ar": "", "ar": "",
"ats": "", "ats": "",
"federal_tax": "", "federal_tax": "",
"la1": "",
"la2": "",
"la3": "",
"lab": "", "lab": "",
"lad": "", "lad": "",
"lae": "", "lae": "",
@@ -406,6 +410,7 @@
"pap": "", "pap": "",
"par": "", "par": "",
"pas": "", "pas": "",
"pasl": "",
"refund": "", "refund": "",
"sales_tax_codes": { "sales_tax_codes": {
"code": "", "code": "",

View File

@@ -381,10 +381,14 @@
"responsibilitycenter_accountnumber": "", "responsibilitycenter_accountnumber": "",
"responsibilitycenter_rate": "", "responsibilitycenter_rate": "",
"responsibilitycenters": { "responsibilitycenters": {
"LA4": "",
"ap": "", "ap": "",
"ar": "", "ar": "",
"ats": "", "ats": "",
"federal_tax": "", "federal_tax": "",
"la1": "",
"la2": "",
"la3": "",
"lab": "", "lab": "",
"lad": "", "lad": "",
"lae": "", "lae": "",
@@ -406,6 +410,7 @@
"pap": "", "pap": "",
"par": "", "par": "",
"pas": "", "pas": "",
"pasl": "",
"refund": "", "refund": "",
"sales_tax_codes": { "sales_tax_codes": {
"code": "", "code": "",

View File

@@ -294,11 +294,7 @@ const generateInvoiceQbxml = (
} }
} }
// Labor Lines // Labor Lines
if ( if (jobline.profitcenter_labor && jobline.mod_lb_hrs) {
jobline.profitcenter_labor &&
jobline.mod_lb_hrs &&
jobline.mod_lb_hrs > 0
) {
const DineroAmount = Dinero({ const DineroAmount = Dinero({
amount: Math.round( amount: Math.round(
jobs_by_pk[`rate_${jobline.mod_lbr_ty.toLowerCase()}`] * 100 jobs_by_pk[`rate_${jobline.mod_lbr_ty.toLowerCase()}`] * 100

View File

@@ -242,7 +242,16 @@ function CalculatePartsTotals(jobLines) {
subtotal: acc.sublets.subtotal.add( subtotal: acc.sublets.subtotal.add(
Dinero({ Dinero({
amount: Math.round(value.act_price * 100), amount: Math.round(value.act_price * 100),
}).multiply(value.part_qty || 0) })
.multiply(value.part_qty || 0)
.add(
Dinero({
amount: Math.round(value.act_price * 100),
})
.multiply(value.part_qty || 0)
.percentage(Math.abs(value.prt_dsmk_p || 0))
.multiply(value.prt_dsmk_p > 0 ? 1 : -1)
)
), ),
}, },
}; };
@@ -306,6 +315,7 @@ function CalculatePartsTotals(jobLines) {
}, },
sublets: { sublets: {
subtotal: Dinero({ amount: 0 }), subtotal: Dinero({ amount: 0 }),
total: Dinero({ amount: 0 }), total: Dinero({ amount: 0 }),
}, },
} }