diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index dafe90108..5e032cc66 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -5902,6 +5902,27 @@ + + retailtotal + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + state_tax false @@ -9374,6 +9395,27 @@ + + difference + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + documents false diff --git a/client/src/components/invoice-form/invoice-form.component.jsx b/client/src/components/invoice-form/invoice-form.component.jsx index 4ecf41c24..3e060e929 100644 --- a/client/src/components/invoice-form/invoice-form.component.jsx +++ b/client/src/components/invoice-form/invoice-form.component.jsx @@ -44,21 +44,23 @@ export default function InvoiceFormComponent({ setDiscount(matchingVendors[0].discount); } } - }, [form, setDiscount, vendorAutoCompleteOptions]); + if (form.getFieldValue("jobid")) { + loadLines({ variables: { id: form.getFieldValue("jobid") } }); + } + }, [form, setDiscount, vendorAutoCompleteOptions, loadLines]); return ( -
-
+
+
+ ]}> + ]}>
-
+
+ ]}> + ]}> + name='is_credit_memo' + valuePropName='checked'> + ]}> + name='federal_tax_rate'> + name='state_tax_rate'> + name='local_tax_rate'>
@@ -161,19 +155,18 @@ export default function InvoiceFormComponent({ // } { console.log("Upload event:", e); if (Array.isArray(e)) { return e; } return e && e.fileList; - }} - > - false} listType="picture"> + }}> + false} listType='picture'> @@ -196,7 +189,7 @@ export default function InvoiceFormComponent({ totals = CalculateInvoiceTotal(values); if (!!totals) return ( -
+
( + to={`/manage/invoices?invoiceid=${record.id}&vendorid=${record.vendorid}`}> ), @@ -120,24 +121,74 @@ export default function InvoicesListTableComponent({ state.sortedInfo.columnKey === "cost_center" && state.sortedInfo.order, }, + { + title: t("invoicelines.fields.federal_tax_applicable"), + dataIndex: "applicable_taxes.federal", + key: "applicable_taxes.federal", + render: (text, record) => ( + + ), + }, + { + title: t("invoicelines.fields.state_tax_applicable"), + dataIndex: "applicable_taxes.state", + key: "applicable_taxes.state", + render: (text, record) => ( + + ), + }, + { + title: t("invoicelines.fields.local_tax_applicable"), + dataIndex: "applicable_taxes.local", + key: "applicable_taxes.local", + render: (text, record) => ( + + ), + }, ]; return (
- - Zhou Maomao - 1810000000 - Hangzhou, Zhejiang - empty - - No. 18, Wantang Road, Xihu District, Hangzhou, Zhejiang, China + + + {record.federal_tax_rate || ""} + + + {record.state_tax_rate || ""} + + + {record.local_tax_rate || ""} + + + ({ ...item }))} - rowKey="id" + rowKey='id' dataSource={record.invoicelines} /> @@ -147,11 +198,18 @@ export default function InvoicesListTableComponent({ return (
( +
+ +
+ )} expandedRowRender={rowExpander} pagination={{ position: "top", defaultPageSize: 25 }} columns={columns.map((item) => ({ ...item }))} - rowKey="id" + rowKey='id' dataSource={invoices} onChange={handleTableChange} expandable={{ diff --git a/client/src/components/job-invoices-total/job-invoices-total.component.jsx b/client/src/components/job-invoices-total/job-invoices-total.component.jsx new file mode 100644 index 000000000..b280dffd9 --- /dev/null +++ b/client/src/components/job-invoices-total/job-invoices-total.component.jsx @@ -0,0 +1,67 @@ +import React from "react"; +import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component"; +import { Statistic, Descriptions } from "antd"; +import { useTranslation } from "react-i18next"; +import Dinero from "dinero.js"; + +export default function JobInvoiceTotals({ loading, invoices, jobTotals }) { + const { t } = useTranslation(); + if (loading) return ; + const totals = JSON.parse(jobTotals); + + let invoiceTotals = Dinero({ amount: 0 }); + invoices.forEach((i) => + i.invoicelines.forEach((il) => { + invoiceTotals = invoiceTotals.add( + Dinero({ + amount: + ((il.actual_price || 0) * i.is_credit_memo ? -1 : 1 || 0) * 100, + }) + ); + }) + ); + + const discrepancy = Dinero(totals.parts.parts.total).subtract(invoiceTotals); + + return ( +
+ + + + + + + + + + + + + + +
+ ); +} diff --git a/client/src/components/jobs-detail-pli/jobs-detail-pli.component.jsx b/client/src/components/jobs-detail-pli/jobs-detail-pli.component.jsx index ae26ad764..0bca9581d 100644 --- a/client/src/components/jobs-detail-pli/jobs-detail-pli.component.jsx +++ b/client/src/components/jobs-detail-pli/jobs-detail-pli.component.jsx @@ -4,6 +4,7 @@ import { connect } from "react-redux"; import { setModalContext } from "../../redux/modals/modals.actions"; import AlertComponent from "../alert/alert.component"; import InvoicesListTableComponent from "../invoices-list-table/invoices-list-table.component"; +import JobInvoicesTotalsComponent from "../job-invoices-total/job-invoices-total.component"; const mapDispatchToProps = (dispatch) => ({ setInvoiceEnterContext: (context) => @@ -27,13 +28,19 @@ export function JobsDetailPliComponent({ job, }, }); - }} - > + }}> Enter Invoice {invoicesQuery.error ? ( - + ) : null} + + + { setgalleryImages( data.reduce((acc, value) => { @@ -34,7 +34,7 @@ function JobsDocumentsComponent({ }, [data, setgalleryImages]); return ( -
+
-
+ {t("timetickets.fields.cost_center")}@@ -40,12 +40,23 @@ export function LaborAllocationsTable({ joblines, timetickets, bodyshop }) { {t("jobs.labels.hrs_claimed")} + + {t("jobs.labels.difference")} + {totals.map((t, idx) => ( - {t.cost_center} - {t.total} - {t.claimed} + {t.cost_center} + {t.total.toFixed(2)} + {t.claimed.toFixed(2)} + + 0 ? "green" : "red", + }}> + {(t.total - t.claimed).toFixed(2)} + + ))} diff --git a/client/src/graphql/invoices.queries.js b/client/src/graphql/invoices.queries.js index f6cb7c443..f1bd0d12d 100644 --- a/client/src/graphql/invoices.queries.js +++ b/client/src/graphql/invoices.queries.js @@ -49,6 +49,9 @@ export const QUERY_INVOICES_BY_JOBID = gql` total invoice_number date + federal_tax_rate + state_tax_rate + local_tax_rate invoicelines { actual_price quantity @@ -56,6 +59,7 @@ export const QUERY_INVOICES_BY_JOBID = gql` cost_center id line_desc + applicable_taxes } } } diff --git a/client/src/pages/invoice-detail/invoice-detail.page.component.jsx b/client/src/pages/invoice-detail/invoice-detail.page.component.jsx deleted file mode 100644 index 6a3885aba..000000000 --- a/client/src/pages/invoice-detail/invoice-detail.page.component.jsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from "react"; - -export default function InvoiceDetailPageComponent() { - return
Invoice Detail Page Component
; -} diff --git a/client/src/pages/invoice-detail/invoice-detail.page.container.jsx b/client/src/pages/invoice-detail/invoice-detail.page.container.jsx deleted file mode 100644 index 966c4441e..000000000 --- a/client/src/pages/invoice-detail/invoice-detail.page.container.jsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from "react"; -import { useParams } from "react-router-dom"; -import InvoiceDetailPageComponent from "./invoice-detail.page.component"; -import { useQuery } from "@apollo/react-hooks"; -import { QUERY_INVOICE_BY_PK } from "../../graphql/invoices.queries"; -import AlertComponent from "../../components/alert/alert.component"; -import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component"; -import { Form } from "antd"; - -export default function InvoiceDetailPageContainer() { - const { invoiceId } = useParams(); - const [form] = Form.useForm(); - - const { loading, error, data } = useQuery(QUERY_INVOICE_BY_PK, { - variables: { invoiceid: invoiceId }, - skip: !!!invoiceId, - }); - - if (loading) return ; - if (error) return ; - - return ( -
- - - ); -} diff --git a/client/src/pages/jobs-detail/jobs-detail.page.component.jsx b/client/src/pages/jobs-detail/jobs-detail.page.component.jsx index 5bd58cd38..039d0b697 100644 --- a/client/src/pages/jobs-detail/jobs-detail.page.component.jsx +++ b/client/src/pages/jobs-detail/jobs-detail.page.component.jsx @@ -1,28 +1,16 @@ -import Icon, { - BarsOutlined, - CalendarFilled, - DollarCircleOutlined, - FileImageFilled, - ToolFilled, -} from "@ant-design/icons"; +import Icon, { BarsOutlined, CalendarFilled, DollarCircleOutlined, FileImageFilled, ToolFilled } from "@ant-design/icons"; import { Form, notification, Tabs } from "antd"; import moment from "moment"; +import queryString from "query-string"; import React, { lazy, Suspense } from "react"; import { useTranslation } from "react-i18next"; -import { - FaHardHat, - FaHistory, - FaInfo, - FaRegStickyNote, - FaShieldAlt, -} from "react-icons/fa"; -import { useHistory, useLocation } from "react-router-dom"; -import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component"; -import queryString from "query-string"; +import { FaHardHat, FaHistory, FaInfo, FaRegStickyNote, FaShieldAlt } from "react-icons/fa"; import { connect } from "react-redux"; +import { useHistory, useLocation } from "react-router-dom"; import { createStructuredSelector } from "reselect"; -import { selectBodyshop } from "../../redux/user/user.selectors"; import { CalculateJob } from "../../components/job-totals-table/job-totals.utility"; +import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component"; +import { selectBodyshop } from "../../redux/user/user.selectors"; const JobsLinesContainer = lazy(() => import("../../components/job-detail-lines/job-lines.container") @@ -127,14 +115,6 @@ export function JobsDetailPage({ fallback={}> - -
console.log("a,b", a, b)} diff --git a/client/src/pages/manage/manage.page.component.jsx b/client/src/pages/manage/manage.page.component.jsx index 6e519d12e..05134c254 100644 --- a/client/src/pages/manage/manage.page.component.jsx +++ b/client/src/pages/manage/manage.page.component.jsx @@ -67,9 +67,7 @@ const ContractsList = lazy(() => const InvoicesListPage = lazy(() => import("../invoices/invoices.page.container") ); -const InvoiceDetailPage = lazy(() => - import("../invoice-detail/invoice-detail.page.container") -); + const EnterInvoiceModalContainer = lazy(() => import("../../components/invoice-enter-modal/invoice-enter-modal.container") ); @@ -201,11 +199,6 @@ export default function Manage({ match }) { path={`${match.path}/invoices`} component={InvoicesListPage} /> -