Refactored job closing to be line based instead of totals based. BOD-383

This commit is contained in:
Patrick Fic
2020-09-14 13:54:11 -07:00
parent e3f108c567
commit eff49e3d25
34 changed files with 1030 additions and 822 deletions

View File

@@ -1,146 +1,102 @@
import React, { useState } from "react";
import { Button, Form, Space, notification, Popconfirm } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import JobsCloseLaborMaterialAllocation from "../../components/jobs-close-labmat-allocation/jobs-close-labmat-allocation.component";
import { selectBodyshop } from "../../redux/user/user.selectors";
import JobsClosePartsAllocation from "../../components/jobs-close-parts-allocation/jobs-close-parts-allocation.component";
import Dinero from "dinero.js";
import JobsCloseTotals from "../../components/jobs-close-totals/jobs-close-totals.component";
import JobsCloseAutoAllocate from "../../components/jobs-close-auto-allocate/jobs-close-auto-allocate.component";
import JobsCloseSaveButton from "../../components/jobs-close-save-button/jobs-close-save-button.component";
import JobsCloseExportButton from "../../components/jobs-close-export-button/jobs-close-export-button.component";
import FormsFieldChanged from "../../components/form-fields-changed-alert/form-fields-changed-alert.component";
import JobsScoreboardAdd from "../../components/job-scoreboard-add-button/job-scoreboard-add-button.component";
import JobsCloseAutoAllocate from "../../components/jobs-close-auto-allocate/jobs-close-auto-allocate.component";
import JobsCloseExportButton from "../../components/jobs-close-export-button/jobs-close-export-button.component";
import JobsCloseLines from "../../components/jobs-close-lines/jobs-close-lines.component";
import JobsCloseTotals from "../../components/jobs-close-totals/jobs-close-totals.component";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { useApolloClient, useMutation } from "react-apollo";
import { generateJobLinesUpdatesForInvoicing } from "../../graphql/jobs-lines.queries";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { useHistory } from "react-router-dom";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
export function JobsCloseComponent({ job, bodyshop, jobTotals }) {
const [invoiced, setInvoiced] = useState(!!job.invoice_allocation);
const [labmatAllocations, setLabmatAllocations] = useState(
!!job.invoice_allocation && !!job.invoice_allocation.labMatAllocations
? Object.keys(job.invoice_allocation.labMatAllocations).reduce(
(acc, val) => {
if (val.includes("subtotal")) {
acc[val] = Dinero(job.invoice_allocation.labMatAllocations[val]);
} else {
acc[val] = {
...job.invoice_allocation.labMatAllocations[val],
total: Dinero(
job.invoice_allocation.labMatAllocations[val].total
),
allocations: job.invoice_allocation.labMatAllocations[
val
].allocations.map((item) => {
return { ...item, amount: Dinero(item.amount) };
}),
};
}
export function JobsCloseComponent({ job, bodyshop }) {
const { t } = useTranslation();
const [form] = Form.useForm();
const client = useApolloClient();
const history = useHistory();
const [closeJob] = useMutation(UPDATE_JOB);
// useEffect(() => {
// //if (job && form) form.setFields({ joblines: job.joblines });
// }, [job, form]);
return acc;
},
{}
)
: Object.keys(jobTotals.rates).reduce((acc, val) => {
acc[val] = jobTotals.rates[val];
if (val.includes("subtotal")) return acc;
//Not a subtotal - therefore can be allocated.
acc[val].allocations = [];
return acc;
}, {})
);
const handleFinish = async (values) => {
console.log(values);
const [partsAllocations, setPartsAllocations] = useState(
!!job.invoice_allocation && !!job.invoice_allocation.partsAllocations
? Object.keys(job.invoice_allocation.partsAllocations).reduce(
(acc, val) => {
acc[val] = {
...job.invoice_allocation.partsAllocations[val],
total: Dinero(job.invoice_allocation.partsAllocations[val].total),
allocations: job.invoice_allocation.partsAllocations[
val
].allocations.map((item) => {
return { ...item, amount: Dinero(item.amount) };
}),
};
return acc;
},
{}
)
: {
...Object.keys(jobTotals.parts.parts.list).reduce((acc, val) => {
acc[val] = { ...jobTotals.parts.parts.list[val], allocations: [] };
const result = await client.mutate({
mutation: generateJobLinesUpdatesForInvoicing(values.joblines),
});
console.log("result.data", result.data);
form.resetFields();
form.resetFields();
};
return acc;
}, {}),
pas: {
...jobTotals.parts.sublets,
allocations: [],
},
}
);
const handleClose = async () => {
const result = await closeJob({
variables: {
jobId: job.id,
job: {
status: bodyshop.md_ro_statuses.default_invoiced || "",
date_invoiced: new Date(),
},
},
});
const labmatAllocatedTotalsArray = Object.keys(labmatAllocations)
.filter((i) => !i.includes("subtotal"))
.map((i) => labmatAllocations[i].allocations)
.flat();
const labmatAllocatedTotal = Dinero({
amount: labmatAllocatedTotalsArray.reduce((acc, val) => {
return (acc = acc + Dinero(val.amount).getAmount());
}, 0),
});
const partsAllocatedTotalsArray = Object.keys(partsAllocations)
.map((i) => partsAllocations[i].allocations)
.flat();
const partsAllocatedTotal = Dinero({
amount: partsAllocatedTotalsArray.reduce((acc, val) => {
return (acc = acc + Dinero(val.amount).getAmount());
}, 0),
});
if (!!!result.errors) {
notification["success"]({ message: t("job.successes.closed") });
history.push(`/manage/jobs/${job.id}`);
} else {
notification["error"]({
message: t("job.errors.closing", {
error: JSON.stringify(result.errors),
}),
});
}
};
return (
<div>
<JobsCloseSaveButton
jobId={job.id}
invoiced={invoiced}
setInvoicedState={setInvoiced}
partsAllocations={partsAllocations}
labMatAllocations={labmatAllocations}
disabled={!!job.date_exported}
suspenseAmount={Dinero(jobTotals.totals.subtotal)
.subtract(labmatAllocatedTotal)
.subtract(partsAllocatedTotal)
.getAmount()}
/>
<JobsScoreboardAdd job={job} disabled={!invoiced} />
<JobsCloseExportButton jobId={job.id} disabled={!invoiced} />
<JobsCloseTotals
jobTotals={jobTotals}
labMatTotal={labmatAllocatedTotal}
partsTotal={partsAllocatedTotal}
/>
<JobsCloseAutoAllocate
labmatAllocations={labmatAllocations}
setLabmatAllocations={setLabmatAllocations}
partsAllocations={partsAllocations}
setPartsAllocations={setPartsAllocations}
disabled={!!job.date_exported}
/>
<JobsCloseLaborMaterialAllocation
labmatAllocations={labmatAllocations}
setLabmatAllocations={setLabmatAllocations}
labMatTotalAllocation={labmatAllocatedTotal}
invoiced={!!job.date_exported}
/>
<JobsClosePartsAllocation
partsAllocations={partsAllocations}
setPartsAllocations={setPartsAllocations}
partsAllocatedTotal={partsAllocatedTotal}
invoiced={!!job.date_exported}
/>
<Form
layout="vertical"
form={form}
onFinish={handleFinish}
initialValues={{ joblines: job.joblines }}
>
<Space>
<JobsCloseAutoAllocate
joblines={job.joblines}
form={form}
disabled={!!job.date_exported}
/>
<Button onClick={() => form.submit()}>
{t("general.actions.save")}
</Button>
<Popconfirm
onConfirm={handleClose}
okText={t("general.labels.yes")}
cancelText={t("general.labels.no")}
title={t("jobs.labels.closeconfirm")}
>
<Button type="danger">{t("general.actions.close")}</Button>
</Popconfirm>
<JobsScoreboardAdd job={job} disabled={false} />
<JobsCloseExportButton jobId={job.id} disabled={false} />
</Space>
<FormsFieldChanged form={form} />
<JobsCloseTotals jobTotals={job.job_totals} form={form} />
<JobsCloseLines joblines={job.joblines} />
</Form>
</div>
);
}

View File

@@ -61,13 +61,11 @@ export function JobsCloseContainer({ setBreadcrumbs, setSelectedHeader }) {
extra={<JobCalculateTotals job={data.jobs_by_pk} />}
/>
);
return (
<RbacWrapper action="jobs:close">
<div>
<JobsCloseComponent
job={data ? data.jobs_by_pk : {}}
jobTotals={data.jobs_by_pk.job_totals}
/>
<JobsCloseComponent job={data ? data.jobs_by_pk : {}} />
</div>
</RbacWrapper>
);