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> <Tag key={j.id}>
))} <Link to={`/manage/jobs/${j?.id}`}>{`${j.ro_number || "N/A"}${
<Popover content={popContent} visible={roSearchVisible}> j.clm_no ? ` | ${j.clm_no}` : ""
<Button type="link" onClick={() => setRoSearchVisible(true)}> }${j.status ? ` | ${j.status}` : ""}`}</Link>
<PlusCircleOutlined /> </Tag>
</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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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 }),
}, },
} }