WIP PBS AP.

This commit is contained in:
Patrick Fic
2022-10-31 10:42:42 -07:00
parent 7d81898a45
commit 8d5202f46d
16 changed files with 690 additions and 12 deletions

View File

@@ -0,0 +1,138 @@
import { SyncOutlined } from "@ant-design/icons";
import { Button, Card, Table, Typography } from "antd";
import Dinero from "dinero.js";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(DmsAllocationsSummaryAp);
export function DmsAllocationsSummaryAp({ socket, bodyshop, billids, title }) {
const { t } = useTranslation();
const [allocationsSummary, setAllocationsSummary] = useState([]);
useEffect(() => {
if (socket.connected) {
socket.emit("pbs-calculate-allocations-ap", billids, (ack) => {
setAllocationsSummary(ack);
socket.allocationsSummary = ack;
});
}
}, [socket, socket.connected, billids]);
const columns = [
{
title: t("jobs.fields.dms.center"),
dataIndex: "center",
key: "center",
},
{
title: t("jobs.fields.dms.sale"),
dataIndex: "sale",
key: "sale",
render: (text, record) => Dinero(record.sale).toFormat(),
},
{
title: t("jobs.fields.dms.cost"),
dataIndex: "cost",
key: "cost",
render: (text, record) => Dinero(record.cost).toFormat(),
},
{
title: t("jobs.fields.dms.sale_dms_acctnumber"),
dataIndex: "sale_dms_acctnumber",
key: "sale_dms_acctnumber",
render: (text, record) =>
record.profitCenter && record.profitCenter.dms_acctnumber,
},
{
title: t("jobs.fields.dms.cost_dms_acctnumber"),
dataIndex: "cost_dms_acctnumber",
key: "cost_dms_acctnumber",
render: (text, record) =>
record.costCenter && record.costCenter.dms_acctnumber,
},
{
title: t("jobs.fields.dms.dms_wip_acctnumber"),
dataIndex: "dms_wip_acctnumber",
key: "dms_wip_acctnumber",
render: (text, record) =>
record.costCenter && record.costCenter.dms_wip_acctnumber,
},
];
return (
<Card
title={title}
extra={
<Button
onClick={() => {
socket.emit("pbs-calculate-allocations-ap", billids, (ack) =>
setAllocationsSummary(ack)
);
}}
>
<SyncOutlined />
</Button>
}
>
<Table
pagination={{ position: "top", defaultPageSize: 50 }}
columns={columns}
rowKey="center"
dataSource={allocationsSummary}
locale={{ emptyText: t("dms.labels.refreshallocations") }}
summary={() => {
const totals =
allocationsSummary &&
allocationsSummary.reduce(
(acc, val) => {
return {
totalSale: acc.totalSale.add(Dinero(val.sale)),
totalCost: acc.totalCost.add(Dinero(val.cost)),
};
},
{
totalSale: Dinero(),
totalCost: Dinero(),
}
);
return (
<Table.Summary.Row>
<Table.Summary.Cell>
<Typography.Title level={4}>
{t("general.labels.totals")}
</Typography.Title>
</Table.Summary.Cell>
<Table.Summary.Cell>
{totals && totals.totalSale.toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell>
{
// totals.totalCost.toFormat()
}
</Table.Summary.Cell>
<Table.Summary.Cell></Table.Summary.Cell>
<Table.Summary.Cell></Table.Summary.Cell>
</Table.Summary.Row>
);
}}
/>
</Card>
);
}

View File

@@ -89,6 +89,11 @@ function Header({
{},
bodyshop && bodyshop.imexshopid
);
const { DmsAp } = useTreatments(
["DmsAp"],
{},
bodyshop && bodyshop.imexshopid
);
const { t } = useTranslation();
@@ -264,10 +269,11 @@ function Header({
{t("menus.header.accounting-receivables")}
</Link>
</Menu.Item>
{!(
{(!(
(bodyshop && bodyshop.cdk_dealerid) ||
(bodyshop && bodyshop.pbs_serialnumber)
) && (
) ||
DmsAp.treatment === "on") && (
<Menu.Item key="payables">
<Link to="/manage/accounting/payables">
{t("menus.header.accounting-payables")}

View File

@@ -14,6 +14,7 @@ import {
import { logImEXEvent } from "../../firebase/firebase.utils";
import _ from "lodash";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { Link } from "react-router-dom";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -176,6 +177,13 @@ export function PayableExportAll({
setLoading(false);
};
if (bodyshop.pbs_serialnumber)
return (
<Link to={{ state: { billids }, pathname: `/manage/dmsap` }}>
<Button>{t("jobs.actions.export")}</Button>
</Link>
);
return (
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
{t("jobs.actions.exportselected")}

View File

@@ -13,6 +13,7 @@ import {
} from "../../redux/user/user.selectors";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { Link } from "react-router-dom";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -179,6 +180,13 @@ export function PayableExportButton({
setLoading(false);
};
if (bodyshop.pbs_serialnumber)
return (
<Link to={{ state: { billids: [billId] }, pathname: `/manage/dmsap` }}>
<Button>{t("jobs.actions.export")}</Button>
</Link>
);
return (
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
{t("jobs.actions.export")}

View File

@@ -43,7 +43,11 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
{},
bodyshop && bodyshop.imexshopid
);
const { DmsAp } = useTreatments(
["DmsAp"],
{},
bodyshop && bodyshop.imexshopid
);
const [costOptions, setCostOptions] = useState(
[
...((form.getFieldValue(["md_responsibility_centers", "costs"]) &&
@@ -4159,6 +4163,121 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<InputNumber precision={2} />
</Form.Item>
</LayoutFormRow>
{DmsAp.treatment === "on" && (
<LayoutFormRow>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.federal_tax_itc")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "taxes", "federal_itc", "name"]}
>
<Input />
</Form.Item>
{/* <Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountnumber")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={[
"md_responsibility_centers",
"taxes",
"federal_itc",
"accountnumber",
]}
>
<Input />
</Form.Item> */}
{/* <Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountname")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={[
"md_responsibility_centers",
"taxes",
"federal_itc",
"accountname",
]}
>
<Input />
</Form.Item> */}
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountdesc")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={[
"md_responsibility_centers",
"taxes",
"federal_itc",
"accountdesc",
]}
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountitem")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={[
"md_responsibility_centers",
"taxes",
"federal_itc",
"accountitem",
]}
>
<Input />
</Form.Item>
{(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
<Form.Item
label={t("bodyshop.fields.dms.dms_acctnumber")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={[
"md_responsibility_centers",
"taxes",
"federal_itc",
"dms_acctnumber",
]}
>
<Input />
</Form.Item>
)}
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_rate")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "taxes", "federal_itc", "rate"]}
>
<InputNumber precision={2} />
</Form.Item>
</LayoutFormRow>
)}
<LayoutFormRow>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.state_tax")}

View File

@@ -0,0 +1,178 @@
import { Button, Card, Col, notification, Row, Select, Space } from "antd";
import queryString from "query-string";
import React, { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import SocketIO from "socket.io-client";
import DmsAllocationsSummaryApComponent from "../../components/dms-allocations-summary-ap/dms-allocations-summary-ap.component";
import DmsCustomerSelector from "../../components/dms-customer-selector/dms-customer-selector.component";
import DmsLogEvents from "../../components/dms-log-events/dms-log-events.component";
import DmsPostForm from "../../components/dms-post-form/dms-post-form.component";
import { auth } from "../../firebase/firebase.utils";
import {
setBreadcrumbs,
setSelectedHeader,
} from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
});
export default connect(mapStateToProps, mapDispatchToProps)(DmsContainer);
export const socket = SocketIO(
process.env.NODE_ENV === "production"
? process.env.REACT_APP_AXIOS_BASE_API_URL
: window.location.origin,
{
path: "/ws",
withCredentials: true,
auth: async (callback) => {
const token = auth.currentUser && (await auth.currentUser.getIdToken());
callback({ token });
},
}
);
export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
const { t } = useTranslation();
const [logLevel, setLogLevel] = useState("DEBUG");
const history = useHistory();
const [logs, setLogs] = useState([]);
const search = queryString.parse(useLocation().search);
const { state } = useLocation();
const { jobId } = search;
const logsRef = useRef(null);
useEffect(() => {
document.title = t("titles.dms");
setSelectedHeader("dms");
setBreadcrumbs([
{
link: "/manage/accounting/receivables",
label: t("titles.bc.accounting-receivables"),
},
{
link: "/manage/dms",
label: t("titles.bc.dms"),
},
]);
}, [t, setBreadcrumbs, setSelectedHeader]);
useEffect(() => {
socket.on("connect", () => socket.emit("set-log-level", logLevel));
socket.on("reconnect", () => {
setLogs((logs) => {
return [
...logs,
{
timestamp: new Date(),
level: "WARNING",
message: "Reconnected to CDK Export Service",
},
];
});
});
socket.on("log-event", (payload) => {
setLogs((logs) => {
return [...logs, payload];
});
});
socket.on("export-success", (payload) => {
notification.success({
message: t("jobs.successes.exported"),
});
history.push("/manage/accounting/receivables");
});
if (socket.disconnected) socket.connect();
return () => {
socket.removeAllListeners();
socket.disconnect();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (!state?.billids) {
history.push(`/manage/accounting/payables`);
}
return (
<div>
DMS PAYABLES SCREEN
<Row gutter={[16, 16]}>
<Col md={24} lg={10}>
<DmsAllocationsSummaryApComponent
socket={socket}
billids={state?.billids}
/>
</Col>
<Col md={24} lg={14}>
{/* <DmsPostForm
socket={socket}
jobId={jobId}
// job={data && data.jobs_by_pk}
logsRef={logsRef}
/> */}
</Col>
<DmsCustomerSelector />
<Col span={24}>
<div ref={logsRef}>
<Card
title={t("jobs.labels.dms.logs")}
extra={
<Space wrap>
<Select
placeholder="Log Level"
value={logLevel}
onChange={(value) => {
setLogLevel(value);
socket.emit("set-log-level", value);
}}
>
<Select.Option key="TRACE">TRACE</Select.Option>
<Select.Option key="DEBUG">DEBUG</Select.Option>
<Select.Option key="INFO">INFO</Select.Option>
<Select.Option key="WARNING">WARNING</Select.Option>
<Select.Option key="ERROR">ERROR</Select.Option>
</Select>
<Button onClick={() => setLogs([])}>Clear Logs</Button>
<Button
onClick={() => {
setLogs([]);
socket.disconnect();
socket.connect();
}}
>
Reconnect
</Button>
</Space>
}
>
<DmsLogEvents socket={socket} logs={logs} />
</Card>
</div>
</Col>
</Row>
</div>
);
}
export const determineDmsType = (bodyshop) => {
if (bodyshop.cdk_dealerid) return "cdk";
else {
return "pbs";
}
};

View File

@@ -164,6 +164,9 @@ const EmailTest = lazy(() =>
);
const Dashboard = lazy(() => import("../dashboard/dashboard.container"));
const Dms = lazy(() => import("../dms/dms.container"));
const DmsPayables = lazy(() =>
import("../dms-payables/dms-payables.container")
);
const { Content, Footer } = Layout;
@@ -391,6 +394,7 @@ export function Manage({ match, conflict, bodyshop }) {
<Route exact path={`${match.path}/emailtest`} component={EmailTest} />
<Route exact path={`${match.path}/dashboard`} component={Dashboard} />
<Route exact path={`${match.path}/dms`} component={Dms} />
<Route exact path={`${match.path}/dmsap`} component={DmsPayables} />
</Suspense>
);

View File

@@ -437,6 +437,7 @@
"ar": "Accounts Receivable",
"ats": "ATS",
"federal_tax": "Federal Tax",
"federal_tax_itc": "Federal Tax Credit",
"gst_override": "GST Override Account #",
"la1": "LA1",
"la2": "LA2",

View File

@@ -437,6 +437,7 @@
"ar": "",
"ats": "",
"federal_tax": "",
"federal_tax_itc": "",
"gst_override": "",
"la1": "",
"la2": "",

View File

@@ -437,6 +437,7 @@
"ar": "",
"ats": "",
"federal_tax": "",
"federal_tax_itc": "",
"gst_override": "",
"la1": "",
"la2": "",