From ea77478b02163feb45a984d0ad34f7db3929768a Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Thu, 25 Jun 2020 16:29:12 -0700 Subject: [PATCH] Added quantity to entering invoices BOD-182 --- bodyshop_translations.babel | 84 +++++++++ .../form-fields-changed-alert.component.jsx | 6 +- .../currency-form-item.component.jsx | 4 +- .../invoice-form/invoice-form.container.jsx | 2 +- .../invoice-form.lines.component.jsx | 26 ++- .../invoice-line-search-select.component.jsx | 13 +- .../invoices-list-table.component.jsx | 169 +++++++++--------- .../job-detail-lines/job-lines.component.jsx | 31 ++-- .../job-invoices-total.component.jsx | 7 +- .../labor-allocations-table.utility.js | 2 +- ...-order-line-backorder-button.component.jsx | 10 +- .../parts-order-list-table.component.jsx | 107 +++++------ .../production-board-kanban.utils.js | 1 + .../schedule-block-day.component.jsx | 27 ++- client/src/translations/en_us/common.json | 4 + client/src/translations/es/common.json | 4 + client/src/translations/fr/common.json | 4 + 17 files changed, 315 insertions(+), 186 deletions(-) diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 525fa5515..4539439a1 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -348,6 +348,27 @@ errors + + blocking + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + canceling false @@ -7150,6 +7171,27 @@ + + invoices + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + local_tax false @@ -14605,6 +14647,27 @@ errors + + backordering + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + creating false @@ -14972,6 +15035,27 @@ + + parts_orders + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + print false diff --git a/client/src/components/form-fields-changed-alert/form-fields-changed-alert.component.jsx b/client/src/components/form-fields-changed-alert/form-fields-changed-alert.component.jsx index 61eeae16e..851a3dafc 100644 --- a/client/src/components/form-fields-changed-alert/form-fields-changed-alert.component.jsx +++ b/client/src/components/form-fields-changed-alert/form-fields-changed-alert.component.jsx @@ -16,7 +16,6 @@ export default function FormsFieldChanged({ form }) { return ( {() => { - console.log("Render", form.isFieldsTouched()); if (form.isFieldsTouched()) return (
@@ -29,7 +28,7 @@ export default function FormsFieldChanged({ form }) { }} /> {t("general.messages.unsavedchanges")} @@ -38,8 +37,7 @@ export default function FormsFieldChanged({ form }) { style={{ cursor: "pointer", textDecoration: "underline", - }} - > + }}> {t("general.actions.reset")}
diff --git a/client/src/components/form-items-formatted/currency-form-item.component.jsx b/client/src/components/form-items-formatted/currency-form-item.component.jsx index d99729363..bf301d359 100644 --- a/client/src/components/form-items-formatted/currency-form-item.component.jsx +++ b/client/src/components/form-items-formatted/currency-form-item.component.jsx @@ -5,8 +5,8 @@ function FormItemCurrency(props, ref) { `$ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ",")} - parser={(value) => value.replace(/\$\s?|(,*)/g, "")} + // formatter={(value) => `$ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ",")} + // parser={(value) => value.replace(/\$\s?|(,*)/g, "")} precision={2} /> ); diff --git a/client/src/components/invoice-form/invoice-form.container.jsx b/client/src/components/invoice-form/invoice-form.container.jsx index 88006cf08..0743ed8d0 100644 --- a/client/src/components/invoice-form/invoice-form.container.jsx +++ b/client/src/components/invoice-form/invoice-form.container.jsx @@ -33,7 +33,7 @@ export function InvoiceFormContainer({ bodyshop, form, invoiceEdit }) { VendorAutoCompleteData && VendorAutoCompleteData.vendors } loadLines={loadLines} - lineData={lineData ? lineData.joblines : null} + lineData={lineData ? lineData.joblines : []} responsibilityCenters={bodyshop.md_responsibility_centers || null} /> diff --git a/client/src/components/invoice-form/invoice-form.lines.component.jsx b/client/src/components/invoice-form/invoice-form.lines.component.jsx index 1a861225c..80793b2f8 100644 --- a/client/src/components/invoice-form/invoice-form.lines.component.jsx +++ b/client/src/components/invoice-form/invoice-form.lines.component.jsx @@ -1,10 +1,9 @@ -import { DeleteFilled } from "@ant-design/icons"; -import { Button, Form, Input, Select, Switch } from "antd"; +import { DeleteFilled, WarningOutlined } from "@ant-design/icons"; +import { Button, Form, Input, InputNumber, Select, Switch } from "antd"; import React from "react"; import { useTranslation } from "react-i18next"; import CurrencyInput from "../form-items-formatted/currency-form-item.component"; import InvoiceLineSearchSelect from "../invoice-line-search-select/invoice-line-search-select.component"; -import { WarningOutlined } from "@ant-design/icons"; export default function InvoiceEnterModalLinesComponent({ lineData, @@ -35,16 +34,17 @@ export default function InvoiceEnterModalLinesComponent({ ]}> { setFieldsValue({ invoicelines: getFieldsValue([ "invoicelines", ]).invoicelines.map((item, idx) => { if (idx === index) { + console.log("opt", opt); return { ...item, line_desc: opt.line_desc, + quantity: opt.part_qty || 1, actual_price: opt.cost, cost_center: opt.part_type ? responsibilityCenters.defaults.costs[ @@ -71,6 +71,18 @@ export default function InvoiceEnterModalLinesComponent({ ]}>
+ + + ; }} diff --git a/client/src/components/invoice-line-search-select/invoice-line-search-select.component.jsx b/client/src/components/invoice-line-search-select/invoice-line-search-select.component.jsx index be95d846c..8cb0613c4 100644 --- a/client/src/components/invoice-line-search-select/invoice-line-search-select.component.jsx +++ b/client/src/components/invoice-line-search-select/invoice-line-search-select.component.jsx @@ -30,10 +30,9 @@ const InvoiceLineSearchSelect = ({ width: 300, }} onChange={setOption} - optionFilterProp="line_desc" + optionFilterProp='line_desc' onBlur={onBlur} - onSelect={onSelect} - > + onSelect={onSelect}> {t("invoicelines.labels.other")} @@ -45,14 +44,14 @@ const InvoiceLineSearchSelect = ({ cost={item.act_price ? item.act_price : 0} part_type={item.part_type} line_desc={item.line_desc} - > - + part_qty={item.part_qty}> + {item.line_desc} - {item.oem_partno} + {item.oem_partno} - + {item.act_price || 0} diff --git a/client/src/components/invoices-list-table/invoices-list-table.component.jsx b/client/src/components/invoices-list-table/invoices-list-table.component.jsx index 78835a942..aa1c1ef61 100644 --- a/client/src/components/invoices-list-table/invoices-list-table.component.jsx +++ b/client/src/components/invoices-list-table/invoices-list-table.component.jsx @@ -94,8 +94,7 @@ export function InvoicesListTableComponent({ render: (text, record) => (
+ to={`/manage/invoices?invoiceid=${record.id}&vendorid=${record.vendorid}`}>
@@ -167,6 +165,14 @@ export function InvoicesListTableComponent({ {record.actual_cost} ), }, + { + title: t("invoicelines.fields.quantity"), + dataIndex: "quantity", + key: "quantity", + sorter: (a, b) => a.quantity - b.quantity, + sortOrder: + state.sortedInfo.columnKey === "quantity" && state.sortedInfo.order, + }, { title: t("invoicelines.fields.cost_center"), dataIndex: "cost_center", @@ -237,11 +243,11 @@ export function InvoicesListTableComponent({ @@ -249,82 +255,85 @@ export function InvoicesListTableComponent({ }; return ( -
( -
- - - {" "} -
- { - e.preventDefault(); - }} - /> +
+ + {t("invoices.labels.invoices")} + +
( +
+ + + {" "} +
+ { + e.preventDefault(); + }} + /> +
- - )} - scroll={{ x: "50%", y: "40rem" }} - expandedRowRender={rowExpander} - pagination={{ position: "top", defaultPageSize: 25 }} - columns={columns} - rowKey="id" - dataSource={invoices} - onChange={handleTableChange} - expandable={{ - expandedRowKeys: [selectedInvoice], - onExpand: (expanded, record) => { - handleOnRowClick(expanded ? record : null); - }, - }} - rowSelection={{ - onSelect: (record) => { - handleOnRowClick(record); - }, - selectedRowKeys: [selectedInvoice], - type: "radio", - }} - onRow={(record, rowIndex) => { - return { - onClick: (event) => { + )} + scroll={{ x: "50%", y: "40rem" }} + expandedRowRender={rowExpander} + pagination={{ position: "top", defaultPageSize: 25 }} + columns={columns} + rowKey='id' + dataSource={invoices} + onChange={handleTableChange} + expandable={{ + expandedRowKeys: [selectedInvoice], + onExpand: (expanded, record) => { + handleOnRowClick(expanded ? record : null); + }, + }} + rowSelection={{ + onSelect: (record) => { handleOnRowClick(record); - }, // click row - onDoubleClick: (event) => {}, // double click row - onContextMenu: (event) => {}, // right button click row - onMouseEnter: (event) => {}, // mouse enter row - onMouseLeave: (event) => {}, // mouse leave row - }; - }} - /> + }, + selectedRowKeys: [selectedInvoice], + type: "radio", + }} + onRow={(record, rowIndex) => { + return { + onClick: (event) => { + handleOnRowClick(record); + }, // click row + onDoubleClick: (event) => {}, // double click row + onContextMenu: (event) => {}, // right button click row + onMouseEnter: (event) => {}, // mouse enter row + onMouseLeave: (event) => {}, // mouse leave row + }; + }} + /> + ); } export default connect(null, mapDispatchToProps)(InvoicesListTableComponent); diff --git a/client/src/components/job-detail-lines/job-lines.component.jsx b/client/src/components/job-detail-lines/job-lines.component.jsx index 4d4555d69..d6569f064 100644 --- a/client/src/components/job-detail-lines/job-lines.component.jsx +++ b/client/src/components/job-detail-lines/job-lines.component.jsx @@ -12,8 +12,6 @@ import AllocationsAssignmentContainer from "../allocations-assignment/allocation import AllocationsBulkAssignmentContainer from "../allocations-bulk-assignment/allocations-bulk-assignment.container"; import AllocationsEmployeeLabelContainer from "../allocations-employee-label/allocations-employee-label.container"; import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container"; -import queryString from "query-string"; -import { useHistory, useLocation } from "react-router-dom"; const mapDispatchToProps = (dispatch) => ({ setJobLineEditContext: (context) => @@ -38,9 +36,6 @@ export function JobLinesComponent({ }); const { t } = useTranslation(); - const search = queryString.parse(useLocation().search); - const history = useHistory(); - const columns = [ { title: "#", @@ -219,8 +214,7 @@ export function JobLinesComponent({ actions: { refetch: refetch }, context: record, }); - }} - > + }}> {t("general.actions.edit")} - PAA - PAN - PAL + PAA + PAN + PAL ); @@ -255,16 +249,16 @@ export function JobLinesComponent({
{ return ( -
+
@@ -278,8 +272,7 @@ export function JobLinesComponent({ linesToOrder: selectedLines, }, }); - }} - > + }}> {t("parts.actions.order")} @@ -295,11 +288,10 @@ export function JobLinesComponent({ actions: { refetch: refetch }, context: { jobid: jobId }, }); - }} - > + }}> {t("joblines.actions.new")} -
+
{ @@ -317,8 +309,7 @@ export function JobLinesComponent({ {record.parts_order_lines.map((item) => (
+ to={`/manage/jobs/${jobId}?tab=partssublet&partsorderid=${item.parts_order.id}`}> {item.parts_order.order_number || ""} - 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 index 3476031db..d0bb014a3 100644 --- a/client/src/components/job-invoices-total/job-invoices-total.component.jsx +++ b/client/src/components/job-invoices-total/job-invoices-total.component.jsx @@ -19,9 +19,10 @@ export default function JobInvoiceTotals({ loading, invoices, jobTotals }) { i.invoicelines.forEach((il) => { invoiceTotals = invoiceTotals.add( Dinero({ - amount: - ((il.actual_price || 0) * i.is_credit_memo ? -1 : 1 || 0) * 100, - }) + amount: Math.round( + (il.actual_cost || 0) * (i.is_credit_memo ? -1 : 1) * 100 + ), + }).multiply(il.quantity) ); }) ); diff --git a/client/src/components/labor-allocations-table/labor-allocations-table.utility.js b/client/src/components/labor-allocations-table/labor-allocations-table.utility.js index 5db9d60ae..8d36bcfdd 100644 --- a/client/src/components/labor-allocations-table/labor-allocations-table.utility.js +++ b/client/src/components/labor-allocations-table/labor-allocations-table.utility.js @@ -14,7 +14,7 @@ export const CalculateAllocationsTotals = ( const r = allCodes.reduce((acc, value) => { acc.push({ opcode: value, - cost_center: responsibilitycenters.defaults[value], + cost_center: responsibilitycenters.defaults.costs[value], total: joblines.reduce((acc2, val2) => { return val2.mod_lbr_ty === value ? acc2 + val2.mod_lb_hrs : acc2; }, 0), diff --git a/client/src/components/parts-order-line-backorder-button/parts-order-line-backorder-button.component.jsx b/client/src/components/parts-order-line-backorder-button/parts-order-line-backorder-button.component.jsx index c261d5c7b..21a46594d 100644 --- a/client/src/components/parts-order-line-backorder-button/parts-order-line-backorder-button.component.jsx +++ b/client/src/components/parts-order-line-backorder-button/parts-order-line-backorder-button.component.jsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { useTranslation } from "react-i18next"; -import { Button } from "antd"; +import { Button, notification } from "antd"; import { useMutation } from "@apollo/react-hooks"; import { MUTATION_BACKORDER_PART_LINE } from "../../graphql/parts-orders.queries"; import { selectBodyshop } from "../../redux/user/user.selectors"; @@ -37,6 +37,14 @@ export function PartsOrderLineBackorderButton({ }, }); + if (!!result.errors) { + notification["error"]({ + message: t("parts_orders.errors.backordering", { + message: JSON.stringify(result.errors), + }), + }); + } + setLoading(false); }; diff --git a/client/src/components/parts-order-list-table/parts-order-list-table.component.jsx b/client/src/components/parts-order-list-table/parts-order-list-table.component.jsx index 1b5dbd938..c76e7dd54 100644 --- a/client/src/components/parts-order-list-table/parts-order-list-table.component.jsx +++ b/client/src/components/parts-order-list-table/parts-order-list-table.component.jsx @@ -1,5 +1,5 @@ import { SyncOutlined } from "@ant-design/icons"; -import { Button, Input, Table } from "antd"; +import { Button, Input, Table, Typography } from "antd"; import queryString from "query-string"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; @@ -147,11 +147,11 @@ export function PartsOrderListTableComponent({ return (
@@ -159,57 +159,62 @@ export function PartsOrderListTableComponent({ }; return ( -
( -
- +
+ + {t("parts_orders.labels.parts_orders")} + +
( +
+ -
- { - e.preventDefault(); - }} - /> +
+ { + e.preventDefault(); + }} + /> +
-
- )} - scroll={{ x: "50%", y: "40rem" }} - expandedRowRender={rowExpander} - pagination={{ position: "top", defaultPageSize: 25 }} - columns={columns} - rowKey="id" - dataSource={parts_orders} - onChange={handleTableChange} - expandable={{ - expandedRowKeys: [selectedpartsorder], - onExpand: (expanded, record) => { - handleOnRowClick(expanded ? record : null); - }, - }} - rowSelection={{ - onSelect: (record) => { - handleOnRowClick(record); - }, - selectedRowKeys: [selectedpartsorder], - type: "radio", - }} - onRow={(record, rowIndex) => { - return { - onClick: (event) => { + )} + scroll={{ x: "50%", y: "40rem" }} + expandedRowRender={rowExpander} + pagination={{ position: "top", defaultPageSize: 25 }} + columns={columns} + rowKey='id' + dataSource={parts_orders} + onChange={handleTableChange} + expandable={{ + expandedRowKeys: [selectedpartsorder], + onExpand: (expanded, record) => { + handleOnRowClick(expanded ? record : null); + }, + }} + rowSelection={{ + onSelect: (record) => { handleOnRowClick(record); - }, // click row - onDoubleClick: (event) => {}, // double click row - onContextMenu: (event) => {}, // right button click row - onMouseEnter: (event) => {}, // mouse enter row - onMouseLeave: (event) => {}, // mouse leave row - }; - }} - /> + }, + selectedRowKeys: [selectedpartsorder], + type: "radio", + }} + onRow={(record, rowIndex) => { + return { + onClick: (event) => { + handleOnRowClick(record); + }, // click row + onDoubleClick: (event) => {}, // double click row + onContextMenu: (event) => {}, // right button click row + onMouseEnter: (event) => {}, // mouse enter row + onMouseLeave: (event) => {}, // mouse leave row + }; + }} + /> + ); } export default connect(null, mapDispatchToProps)(PartsOrderListTableComponent); diff --git a/client/src/components/production-board-kanban/production-board-kanban.utils.js b/client/src/components/production-board-kanban/production-board-kanban.utils.js index ddbb9b936..5bdb020da 100644 --- a/client/src/components/production-board-kanban/production-board-kanban.utils.js +++ b/client/src/components/production-board-kanban/production-board-kanban.utils.js @@ -32,6 +32,7 @@ const sortByParentId = (arr) => { sortedList.push(origItem); console.log("DATA CONSISTENCY ERROR: ", origItem.ro_number); } + return 1; }); } diff --git a/client/src/components/schedule-block-day/schedule-block-day.component.jsx b/client/src/components/schedule-block-day/schedule-block-day.component.jsx index d8be28e71..fbf0106b1 100644 --- a/client/src/components/schedule-block-day/schedule-block-day.component.jsx +++ b/client/src/components/schedule-block-day/schedule-block-day.component.jsx @@ -1,20 +1,20 @@ -import React from "react"; -import { MinusCircleTwoTone } from "@ant-design/icons"; -import { Dropdown, Menu } from "antd"; -import { useTranslation } from "react-i18next"; import { useMutation } from "@apollo/react-hooks"; -import { INSERT_APPOINTMENT } from "../../graphql/appointments.queries"; +import { Dropdown, Menu, notification } from "antd"; import moment from "moment"; +import React from "react"; +import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; +import { INSERT_APPOINTMENT } from "../../graphql/appointments.queries"; import { selectBodyshop } from "../../redux/user/user.selectors"; + const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, }); + const mapDispatchToProps = (dispatch) => ({ //setUserLanguage: language => dispatch(setUserLanguage(language)) }); -export default connect(mapStateToProps, mapDispatchToProps)(ScheduleBlockDay); export function ScheduleBlockDay({ date, children, refetch, bodyshop }) { const { t } = useTranslation(); @@ -41,15 +41,23 @@ export function ScheduleBlockDay({ date, children, refetch, bodyshop }) { variables: { app: [blockAppt] }, }); + if (!!result.errors) { + notification["error"]({ + message: t("appointments.errors.blocking", { + message: JSON.stringify(result.errors), + }), + }); + } + if (!!refetch) refetch(); } }; const menu = ( - {t("appointments.actions.block")} - 2nd menu item - 3rd menu item + {t("appointments.actions.block")} + 2nd menu item + 3rd menu item ); @@ -59,3 +67,4 @@ export function ScheduleBlockDay({ date, children, refetch, bodyshop }) { ); } +export default connect(mapStateToProps, mapDispatchToProps)(ScheduleBlockDay); diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 31b397efb..625ec4fc3 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -28,6 +28,7 @@ "viewjob": "View Job" }, "errors": { + "blocking": "Error creating block {{message}}.", "canceling": "Error canceling appointment. {{message}}", "saving": "Error scheduling appointment. {{message}}" }, @@ -477,6 +478,7 @@ "entered_total": "Total of Entered Lines", "federal_tax": "Federal Tax", "invoice_total": "Invoice Total Amount", + "invoices": "Invoices", "local_tax": "Local Tax", "new": "New Invoice", "noneselected": "No invoice selected.", @@ -906,6 +908,7 @@ "receive": "Receive" }, "errors": { + "backordering": "Error backordering part {{message}}.", "creating": "Error encountered when creating parts order. " }, "fields": { @@ -927,6 +930,7 @@ "inthisorder": "Parts in this Order", "newpartsorder": "New Parts Order", "orderhistory": "Order History", + "parts_orders": "Parts Orders", "print": "Show Printed Form", "returnpartsorder": "Return Parts Order" }, diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 8550e2893..8dce19278 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -28,6 +28,7 @@ "viewjob": "Ver trabajo" }, "errors": { + "blocking": "", "canceling": "Error al cancelar la cita. {{message}}", "saving": "Error al programar la cita. {{message}}" }, @@ -477,6 +478,7 @@ "entered_total": "", "federal_tax": "", "invoice_total": "", + "invoices": "", "local_tax": "", "new": "", "noneselected": "", @@ -906,6 +908,7 @@ "receive": "" }, "errors": { + "backordering": "", "creating": "Se encontró un error al crear el pedido de piezas." }, "fields": { @@ -927,6 +930,7 @@ "inthisorder": "Partes en este pedido", "newpartsorder": "", "orderhistory": "Historial de pedidos", + "parts_orders": "", "print": "Mostrar formulario impreso", "returnpartsorder": "" }, diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 5613a52fd..c60dc5360 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -28,6 +28,7 @@ "viewjob": "Voir le travail" }, "errors": { + "blocking": "", "canceling": "Erreur lors de l'annulation du rendez-vous. {{message}}", "saving": "Erreur lors de la planification du rendez-vous. {{message}}" }, @@ -477,6 +478,7 @@ "entered_total": "", "federal_tax": "", "invoice_total": "", + "invoices": "", "local_tax": "", "new": "", "noneselected": "", @@ -906,6 +908,7 @@ "receive": "" }, "errors": { + "backordering": "", "creating": "Erreur rencontrée lors de la création de la commande de pièces." }, "fields": { @@ -927,6 +930,7 @@ "inthisorder": "Pièces dans cette commande", "newpartsorder": "", "orderhistory": "Historique des commandes", + "parts_orders": "", "print": "Afficher le formulaire imprimé", "returnpartsorder": "" },