CC Conversion Corrections IO-463

This commit is contained in:
Patrick Fic
2021-02-02 09:32:49 -08:00
parent 95c06ea25a
commit 8fcb488b3b
8 changed files with 123 additions and 49 deletions

View File

@@ -1,5 +1,13 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Button, notification, Popover, Radio, Form, InputNumber } from "antd"; import {
Button,
notification,
Popover,
Radio,
Form,
InputNumber,
Space,
} from "antd";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useMutation } from "react-apollo"; import { useMutation } from "react-apollo";
import { INSERT_NEW_JOB } from "../../graphql/jobs.queries"; import { INSERT_NEW_JOB } from "../../graphql/jobs.queries";
@@ -11,6 +19,8 @@ import {
selectCurrentUser, selectCurrentUser,
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import axios from "axios";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -20,7 +30,12 @@ const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language)) //setUserLanguage: language => dispatch(setUserLanguage(language))
}); });
export function ContractConvertToRo({ bodyshop, currentUser, contract }) { export function ContractConvertToRo({
bodyshop,
currentUser,
contract,
disabled,
}) {
const { t } = useTranslation(); const { t } = useTranslation();
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@@ -46,6 +61,7 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract }) {
part_type: "CCDR", part_type: "CCDR",
tax_part: true, tax_part: true,
mod_lb_hrs: 0, mod_lb_hrs: 0,
db_ref: "io-ccdr",
// mod_lbr_ty: "PAL", // mod_lbr_ty: "PAL",
}, },
]; ];
@@ -63,6 +79,7 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract }) {
part_type: "CCM", part_type: "CCM",
part_qty: mileageDiff, part_qty: mileageDiff,
tax_part: true, tax_part: true,
db_ref: "io-ccm",
mod_lb_hrs: 0, mod_lb_hrs: 0,
}); });
} }
@@ -78,6 +95,7 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract }) {
part_qty: values.refuelqty, part_qty: values.refuelqty,
part_type: "CCF", part_type: "CCF",
tax_part: true, tax_part: true,
db_ref: "io-ccf",
mod_lb_hrs: 0, mod_lb_hrs: 0,
}); });
} }
@@ -92,6 +110,7 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract }) {
part_qty: 1, part_qty: 1,
part_type: "CCC", part_type: "CCC",
tax_part: true, tax_part: true,
db_ref: "io-ccc",
mod_lb_hrs: 0, mod_lb_hrs: 0,
}); });
} }
@@ -107,6 +126,7 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract }) {
part_type: "CCD", part_type: "CCD",
part_qty: 1, part_qty: 1,
tax_part: true, tax_part: true,
db_ref: "io-ccd",
mod_lb_hrs: 0, mod_lb_hrs: 0,
}); });
} }
@@ -119,8 +139,8 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract }) {
federal_tax_rate: bodyshop.bill_tax_rates.federal_tax_rate / 100, federal_tax_rate: bodyshop.bill_tax_rates.federal_tax_rate / 100,
state_tax_rate: bodyshop.bill_tax_rates.state_tax_rate / 100, state_tax_rate: bodyshop.bill_tax_rates.state_tax_rate / 100,
local_tax_rate: bodyshop.bill_tax_rates.local_tax_rate / 100, local_tax_rate: bodyshop.bill_tax_rates.local_tax_rate / 100,
converted: true,
clm_no: `${contract.job.clm_no}-CC`, clm_no: `${contract.job.clm_no}-CC`,
clm_total: 1234,
ownr_fn: contract.job.owner.ownr_fn, ownr_fn: contract.job.owner.ownr_fn,
ownr_ln: contract.job.owner.ownr_ln, ownr_ln: contract.job.owner.ownr_ln,
ownr_co_nm: contract.job.owner.ownr_co_nm, ownr_co_nm: contract.job.owner.ownr_co_nm,
@@ -151,7 +171,7 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract }) {
prt_mktyp: false, prt_mktyp: false,
prt_mkupp: 0, prt_mkupp: 0,
prt_tax_in: true, prt_tax_in: true,
prt_tax_rt: 0.07, prt_tax_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
}, },
CCF: { CCF: {
prt_type: "CCF", prt_type: "CCF",
@@ -159,7 +179,7 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract }) {
prt_mktyp: false, prt_mktyp: false,
prt_mkupp: 0, prt_mkupp: 0,
prt_tax_in: true, prt_tax_in: true,
prt_tax_rt: 0.07, prt_tax_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
}, },
CCM: { CCM: {
prt_type: "CCM", prt_type: "CCM",
@@ -167,7 +187,7 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract }) {
prt_mktyp: false, prt_mktyp: false,
prt_mkupp: 0, prt_mkupp: 0,
prt_tax_in: true, prt_tax_in: true,
prt_tax_rt: 0.07, prt_tax_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
}, },
CCC: { CCC: {
prt_type: "CCC", prt_type: "CCC",
@@ -175,7 +195,7 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract }) {
prt_mktyp: false, prt_mktyp: false,
prt_mkupp: 0, prt_mkupp: 0,
prt_tax_in: true, prt_tax_in: true,
prt_tax_rt: 0.07, prt_tax_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
}, },
CCD: { CCD: {
prt_type: "CCD", prt_type: "CCD",
@@ -183,10 +203,22 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract }) {
prt_mktyp: false, prt_mktyp: false,
prt_mkupp: 0, prt_mkupp: 0,
prt_tax_in: true, prt_tax_in: true,
prt_tax_rt: 0.07, prt_tax_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
}, },
}, },
}; };
//Calcualte the new job totals.
const newTotals = (
await axios.post("/job/totals", {
job: { ...newJob, joblines: billingLines },
})
).data;
newJob.clm_total = newTotals.totals.total_repairs.amount / 100;
newJob.job_totals = newTotals;
const result = await insertJob({ const result = await insertJob({
variables: { job: [newJob] }, variables: { job: [newJob] },
// refetchQueries: ["GET_JOB_BY_PK"], // refetchQueries: ["GET_JOB_BY_PK"],
@@ -244,12 +276,14 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract }) {
> >
<InputNumber precision={0} min={0} /> <InputNumber precision={0} min={0} />
</Form.Item> </Form.Item>
<Button type="primary" htmlType="submit"> <Space>
{t("contracts.actions.convertoro")} <Button type="primary" htmlType="submit" loading={loading}>
</Button> {t("contracts.actions.convertoro")}
<Button onClick={() => setVisible(false)}> </Button>
{t("general.actions.close")} <Button onClick={() => setVisible(false)}>
</Button> {t("general.actions.close")}
</Button>
</Space>
</Form> </Form>
</div> </div>
); );
@@ -257,7 +291,11 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract }) {
return ( return (
<div> <div>
<Popover content={popContent} visible={visible}> <Popover content={popContent} visible={visible}>
<Button onClick={() => setVisible(true)} loading={loading}> <Button
onClick={() => setVisible(true)}
loading={loading}
disabled={!contract.dailyrate || disabled}
>
{t("contracts.actions.convertoro")} {t("contracts.actions.convertoro")}
</Button> </Button>
</Popover> </Popover>

View File

@@ -253,13 +253,13 @@
// total: acc.parts.list[value.part_type].total.add( // total: acc.parts.list[value.part_type].total.add(
// Dinero({ // Dinero({
// amount: Math.round((value.act_price || 0) * 100), // amount: Math.round((value.act_price || 0) * 100),
// }).multiply(value.part_qty || 1) // }).multiply(value.part_qty || 0)
// ), // ),
// } // }
// : { // : {
// total: Dinero({ // total: Dinero({
// amount: Math.round((value.act_price || 0) * 100), // amount: Math.round((value.act_price || 0) * 100),
// }).multiply(value.part_qty || 1), // }).multiply(value.part_qty || 0),
// }, // },
// }, // },
// subtotal: acc.parts.subtotal.add( // subtotal: acc.parts.subtotal.add(

View File

@@ -1,12 +1,12 @@
import { Button, Typography, Row, Col } from "antd"; import { Button, Col, Row, Space, Typography, Form } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import ContractConvertToRo from "../../components/contract-convert-to-ro/contract-convert-to-ro.component";
import ContractCourtesyCarBlock from "../../components/contract-courtesy-car-block/contract-courtesy-car-block.component"; import ContractCourtesyCarBlock from "../../components/contract-courtesy-car-block/contract-courtesy-car-block.component";
import ContractFormComponent from "../../components/contract-form/contract-form.component"; import ContractFormComponent from "../../components/contract-form/contract-form.component";
import ContractJobBlock from "../../components/contract-job-block/contract-job-block.component"; import ContractJobBlock from "../../components/contract-job-block/contract-job-block.component";
import { setModalContext } from "../../redux/modals/modals.actions"; import { setModalContext } from "../../redux/modals/modals.actions";
import ContractConvertToRo from "../../components/contract-convert-to-ro/contract-convert-to-ro.component";
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setCourtesyCarReturnModalContext: (context) => setCourtesyCarReturnModalContext: (context) =>
@@ -20,6 +20,7 @@ export function ContractDetailPage({
setCourtesyCarReturnModalContext, setCourtesyCarReturnModalContext,
refetch, refetch,
form, form,
saveLoading,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
@@ -28,27 +29,6 @@ export function ContractDetailPage({
<Typography.Title>{`Agreement ${ <Typography.Title>{`Agreement ${
(contract && contract.agreementnumber) || "" (contract && contract.agreementnumber) || ""
} - ${t((contract && contract.status) || "")}`}</Typography.Title> } - ${t((contract && contract.status) || "")}`}</Typography.Title>
<Button type="primary" htmlType="submit">
{t("general.actions.save")}
</Button>
<Button
disabled={
!!!contract ||
(contract && contract.status !== "contracts.status.out")
}
onClick={() => {
setCourtesyCarReturnModalContext({
actions: { refetch },
context: {
contractId: contract.id,
courtesyCarId: courtesyCar.id,
},
});
}}
>
{t("courtesycars.actions.return")}
</Button>
<ContractConvertToRo contract={contract} />
</Row> </Row>
<Row> <Row>
@@ -57,6 +37,43 @@ export function ContractDetailPage({
<ContractCourtesyCarBlock courtesyCar={courtesyCar} /> <ContractCourtesyCarBlock courtesyCar={courtesyCar} />
</Col> </Col>
<Col span={18} offset={1}> <Col span={18} offset={1}>
<Form.Item shouldUpdate>
{() => {
return (
<Space>
<Button
type="primary"
htmlType="submit"
loading={saveLoading}
>
{t("general.actions.save")}
</Button>
<Button
disabled={
!!!contract ||
(contract && contract.status !== "contracts.status.out")
}
onClick={() => {
setCourtesyCarReturnModalContext({
actions: { refetch },
context: {
contractId: contract.id,
courtesyCarId: courtesyCar.id,
},
});
}}
>
{t("courtesycars.actions.return")}
</Button>
<ContractConvertToRo
contract={contract}
disabled={form.isFieldsTouched()}
/>
</Space>
);
}}
</Form.Item>
<ContractFormComponent form={form} /> <ContractFormComponent form={form} />
</Col> </Col>
</Row> </Row>

View File

@@ -1,7 +1,7 @@
import { useMutation, useQuery } from "@apollo/react-hooks"; import { useMutation, useQuery } from "@apollo/react-hooks";
import { Form, notification } from "antd"; import { Form, notification } from "antd";
import moment from "moment"; import moment from "moment";
import React, { useEffect } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
@@ -35,6 +35,7 @@ export function ContractDetailPageContainer({
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [updateContract] = useMutation(UPDATE_CONTRACT); const [updateContract] = useMutation(UPDATE_CONTRACT);
const [saveLoading, setsaveLoading] = useState(false);
const [form] = Form.useForm(); const [form] = Form.useForm();
const { contractId } = useParams(); const { contractId } = useParams();
@@ -95,6 +96,7 @@ export function ContractDetailPageContainer({
]); ]);
const handleFinish = async (values) => { const handleFinish = async (values) => {
setsaveLoading(true);
const result = await updateContract({ const result = await updateContract({
variables: { cccontract: { ...values }, contractId: contractId }, variables: { cccontract: { ...values }, contractId: contractId },
}); });
@@ -108,6 +110,8 @@ export function ContractDetailPageContainer({
} }
notification["success"]({ message: t("contracts.successes.saved") }); notification["success"]({ message: t("contracts.successes.saved") });
if (refetch) await refetch(); if (refetch) await refetch();
setsaveLoading(false);
form.resetFields(); form.resetFields();
form.resetFields(); form.resetFields();
}; };
@@ -160,6 +164,7 @@ export function ContractDetailPageContainer({
courtesyCar={data ? data.cccontracts_by_pk.courtesycar : null} courtesyCar={data ? data.cccontracts_by_pk.courtesycar : null}
refetch={refetch} refetch={refetch}
form={form} form={form}
saveLoading={saveLoading}
/> />
</Form> </Form>
</div> </div>

View File

@@ -1,6 +1,6 @@
import { useMutation } from "@apollo/react-hooks"; import { useMutation } from "@apollo/react-hooks";
import { Form, notification } from "antd"; import { Form, notification } from "antd";
import React, { useEffect } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
@@ -27,11 +27,13 @@ export function CourtesyCarCreateContainer({
setSelectedHeader, setSelectedHeader,
}) { }) {
const [form] = Form.useForm(); const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
const [insertCourtesyCar] = useMutation(INSERT_NEW_COURTESY_CAR); const [insertCourtesyCar] = useMutation(INSERT_NEW_COURTESY_CAR);
const { t } = useTranslation(); const { t } = useTranslation();
const history = useHistory(); const history = useHistory();
const handleFinish = async (values) => { const handleFinish = async (values) => {
setLoading(true);
const result = await insertCourtesyCar({ const result = await insertCourtesyCar({
variables: { courtesycar: { ...values, bodyshopid: bodyshop.id } }, variables: { courtesycar: { ...values, bodyshopid: bodyshop.id } },
}); });
@@ -42,7 +44,9 @@ export function CourtesyCarCreateContainer({
message: JSON.stringify(result.errors), message: JSON.stringify(result.errors),
}), }),
}); });
setLoading(false);
} else { } else {
setLoading(false);
form.resetFields(); form.resetFields();
form.resetFields(); form.resetFields();
notification["success"]({ message: t("courtesycars.successes.saved") }); notification["success"]({ message: t("courtesycars.successes.saved") });
@@ -67,7 +71,7 @@ export function CourtesyCarCreateContainer({
return ( return (
<RbacWrapper action="courtesycar:create"> <RbacWrapper action="courtesycar:create">
<Form form={form} autoComplete="new-password" onFinish={handleFinish}> <Form form={form} autoComplete="new-password" onFinish={handleFinish}>
<CourtesyCarFormComponent form={form} /> <CourtesyCarFormComponent form={form} saveLoading={loading} />
</Form> </Form>
</RbacWrapper> </RbacWrapper>
); );

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,7 @@
- args:
cascade: false
read_only: false
sql: |-
create trigger jobs_assign_RO_insert before insert on jobs for
each row execute function assign_ro_number();
type: run_sql

View File

@@ -131,7 +131,9 @@ function CalculatePartsTotals(jobLines) {
sublets: { sublets: {
...acc.sublets, ...acc.sublets,
subtotal: acc.sublets.subtotal.add( subtotal: acc.sublets.subtotal.add(
Dinero({ amount: Math.round(value.act_price * 100) }) Dinero({
amount: Math.round(value.act_price * 100),
}).multiply(value.part_qty || 0)
), ),
}, },
}; };
@@ -151,19 +153,19 @@ function CalculatePartsTotals(jobLines) {
total: acc.parts.list[value.part_type].total.add( total: acc.parts.list[value.part_type].total.add(
Dinero({ Dinero({
amount: Math.round((value.act_price || 0) * 100), amount: Math.round((value.act_price || 0) * 100),
}).multiply(value.part_qty || 1) }).multiply(value.part_qty || 0)
), ),
} }
: { : {
total: Dinero({ total: Dinero({
amount: Math.round((value.act_price || 0) * 100), amount: Math.round((value.act_price || 0) * 100),
}).multiply(value.part_qty || 1), }).multiply(value.part_qty || 0),
}, },
}, },
subtotal: acc.parts.subtotal.add( subtotal: acc.parts.subtotal.add(
Dinero({ Dinero({
amount: Math.round(value.act_price * 100), amount: Math.round(value.act_price * 100),
}).multiply(value.part_qty) }).multiply(value.part_qty || 0)
), ),
}, },
}; };
@@ -233,7 +235,7 @@ function CalculateTaxesTotals(job, otherTotals) {
if (!!!val.tax_part || !!!val.part_type || IsAdditionalCost(val)) { if (!!!val.tax_part || !!!val.part_type || IsAdditionalCost(val)) {
additionalItemsTax = additionalItemsTax.add( additionalItemsTax = additionalItemsTax.add(
Dinero({ amount: Math.round((val.act_price || 0) * 100) }) Dinero({ amount: Math.round((val.act_price || 0) * 100) })
.multiply(val.part_qty || 1) .multiply(val.value.part_qty || 0)
.percentage( .percentage(
(job.parts_tax_rates && (job.parts_tax_rates &&
job.parts_tax_rates["PAN"] && job.parts_tax_rates["PAN"] &&
@@ -243,7 +245,7 @@ function CalculateTaxesTotals(job, otherTotals) {
} else { } else {
statePartsTax = statePartsTax.add( statePartsTax = statePartsTax.add(
Dinero({ amount: Math.round((val.act_price || 0) * 100) }) Dinero({ amount: Math.round((val.act_price || 0) * 100) })
.multiply(val.part_qty || 1) .multiply(val.part_qty || 0)
.percentage( .percentage(
(job.parts_tax_rates && (job.parts_tax_rates &&
job.parts_tax_rates[val.part_type] && job.parts_tax_rates[val.part_type] &&