diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 516cab7d9..4101a134e 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -2181,6 +2181,27 @@ + + existinginventoryline + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + exporting false @@ -17266,6 +17287,27 @@ + + addtoro + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + consumefrominventory false diff --git a/client/src/components/bill-delete-button/bill-delete-button.component.jsx b/client/src/components/bill-delete-button/bill-delete-button.component.jsx index eee5f522b..b6cb19992 100644 --- a/client/src/components/bill-delete-button/bill-delete-button.component.jsx +++ b/client/src/components/bill-delete-button/bill-delete-button.component.jsx @@ -15,7 +15,8 @@ export default function BillDeleteButton({ bill }) { setLoading(true); const result = await deleteBill({ variables: { billId: bill.id }, - update(cache) { + update(cache, { errors }) { + if (errors) return; cache.modify({ fields: { bills(existingBills, { readField }) { @@ -36,11 +37,22 @@ export default function BillDeleteButton({ bill }) { if (!!!result.errors) { notification["success"]({ message: t("bills.successes.deleted") }); } else { - notification["error"]({ - message: t("bills.errors.deleting", { - error: JSON.stringify(result.errors), - }), - }); + //Check if it's an fkey violation. + const error = JSON.stringify(result.errors); + + if (error.toLowerCase().includes("inventory_billid_fkey")) { + notification["error"]({ + message: t("bills.errors.deleting", { + error: t("bills.errors.existinginventoryline"), + }), + }); + } else { + notification["error"]({ + message: t("bills.errors.deleting", { + error: JSON.stringify(result.errors), + }), + }); + } } setLoading(false); diff --git a/client/src/components/bill-form/bill-form.lines.component.jsx b/client/src/components/bill-form/bill-form.lines.component.jsx index 18d996d5a..71a0e3b72 100644 --- a/client/src/components/bill-form/bill-form.lines.component.jsx +++ b/client/src/components/bill-form/bill-form.lines.component.jsx @@ -485,22 +485,33 @@ export function BillEnterModalLinesComponent({ dataIndex: "actions", render: (text, record) => ( - - remove(record.name)}> - - - - {() => - Simple_Inventory.treatment === "on" && ( + + {() => ( + + 0 + } + onClick={() => remove(record.name)} + > + + + {Simple_Inventory.treatment === "on" && ( - ) - } - - + )} + + )} + ), }, ]; diff --git a/client/src/components/bill-inventory-table/bill-inventory-table.component.jsx b/client/src/components/bill-inventory-table/bill-inventory-table.component.jsx index d8fb121d0..e9403a0f1 100644 --- a/client/src/components/bill-inventory-table/bill-inventory-table.component.jsx +++ b/client/src/components/bill-inventory-table/bill-inventory-table.component.jsx @@ -7,9 +7,11 @@ import "./bill-inventory-table.styles.scss"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { selectBodyshop } from "../../redux/user/user.selectors"; +import { selectBillEnterModal } from "../../redux/modals/modals.selectors"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, + billEnterModal: selectBillEnterModal, }); const mapDispatchToProps = (dispatch) => ({ //setUserLanguage: language => dispatch(setUserLanguage(language)) @@ -17,6 +19,7 @@ const mapDispatchToProps = (dispatch) => ({ export default connect(mapStateToProps, mapDispatchToProps)(BillInventoryTable); export function BillInventoryTable({ + billEnterModal, bodyshop, form, billEdit, @@ -28,11 +31,18 @@ export function BillInventoryTable({ useEffect(() => { if (inventoryData) { form.setFieldsValue({ - inventory: inventoryData.inventory, + inventory: billEnterModal.context.consumeinventoryid + ? inventoryData.inventory.map((i) => { + if (i.id === billEnterModal.context.consumeinventoryid) + i.consumefrominventory = true; + return i; + }) + : inventoryData.inventory, }); } - }, [inventoryData, form]); + }, [inventoryData, form, billEnterModal.context.consumeinventoryid]); + console.log(form.getFieldsValue()); return ( prev.vendorid !== cur.vendorid} diff --git a/client/src/components/bill-mark-exported-button/bill-mark-exported-button.component.jsx b/client/src/components/bill-mark-exported-button/bill-mark-exported-button.component.jsx index d81244098..80fa1ae44 100644 --- a/client/src/components/bill-mark-exported-button/bill-mark-exported-button.component.jsx +++ b/client/src/components/bill-mark-exported-button/bill-mark-exported-button.component.jsx @@ -9,11 +9,14 @@ import { createStructuredSelector } from "reselect"; import { selectAuthLevel, selectBodyshop, + selectCurrentUser, } from "../../redux/user/user.selectors"; import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component"; +import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, authLevel: selectAuthLevel, + currentUser: selectCurrentUser, }); const mapDispatchToProps = (dispatch) => ({ //setUserLanguage: language => dispatch(setUserLanguage(language)) @@ -24,9 +27,15 @@ export default connect( mapDispatchToProps )(BillMarkExportedButton); -export function BillMarkExportedButton({ bodyshop, authLevel, bill }) { +export function BillMarkExportedButton({ + currentUser, + bodyshop, + authLevel, + bill, +}) { const { t } = useTranslation(); const [loading, setLoading] = useState(false); + const [insertExportLog] = useMutation(INSERT_EXPORT_LOG); const [updateBill] = useMutation(gql` mutation UPDATE_BILL($billId: uuid!) { @@ -46,6 +55,20 @@ export function BillMarkExportedButton({ bodyshop, authLevel, bill }) { variables: { billId: bill.id }, }); + await insertExportLog({ + variables: { + logs: [ + { + bodyshopid: bodyshop.id, + billid: bill.id, + successful: true, + message: t("general.labels.markedexported"), + useremail: currentUser.email, + }, + ], + }, + }); + if (!result.errors) { notification["success"]({ message: t("bills.successes.markexported"), @@ -69,11 +92,7 @@ export function BillMarkExportedButton({ bodyshop, authLevel, bill }) { if (hasAccess) return ( - + {t("bills.labels.markexported")} ); diff --git a/client/src/components/billline-add-inventory/billline-add-inventory.component.jsx b/client/src/components/billline-add-inventory/billline-add-inventory.component.jsx index 2a6d76355..d2cbe92bc 100644 --- a/client/src/components/billline-add-inventory/billline-add-inventory.component.jsx +++ b/client/src/components/billline-add-inventory/billline-add-inventory.component.jsx @@ -64,9 +64,9 @@ export function BilllineAddInventory({ cost_center: billline.cost_center, deductedfromlbr: billline.deductedfromlbr, applicable_taxes: { - local: false, //billline.applicable_taxes.local, - state: false, //billline.applicable_taxes.state, - federal: false, // billline.applicable_taxes.federal, + local: billline.applicable_taxes.local, + state: billline.applicable_taxes.state, + federal: billline.applicable_taxes.federal, }, }, ], @@ -76,7 +76,9 @@ export function BilllineAddInventory({ const insertResult = await insertInventoryLine({ variables: { - joblineId: billline.joblineid, + joblineId: + billline.joblineid === "noline" ? billline.id : billline.joblineid, //This will return null as there will be no jobline that has the id of the bill line. + //Unfortunately, we can't send null as the GQL syntax validation fails. joblineStatus: bodyshop.md_order_statuses.default_returned, inv: { shopid: bodyshop.id, @@ -99,8 +101,9 @@ export function BilllineAddInventory({ act_price: billline.actual_price, cost: billline.actual_cost, quantity: billline.quantity, - job_line_id: billline.joblineid, - part_type: billline.jobline.part_type, + job_line_id: + billline.joblineid === "noline" ? null : billline.joblineid, + part_type: billline.jobline && billline.jobline.part_type, cm_received: true, }, ], diff --git a/client/src/components/inventory-bill-ro/inventory-bill-ro.component.jsx b/client/src/components/inventory-bill-ro/inventory-bill-ro.component.jsx new file mode 100644 index 000000000..f83811ed7 --- /dev/null +++ b/client/src/components/inventory-bill-ro/inventory-bill-ro.component.jsx @@ -0,0 +1,65 @@ +import { Button } from "antd"; +import React from "react"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { setModalContext } from "../../redux/modals/modals.actions"; +import { selectBodyshop } from "../../redux/user/user.selectors"; +import moment from "moment"; +import { useTranslation } from "react-i18next"; +const mapStateToProps = createStructuredSelector({ + bodyshop: selectBodyshop, +}); +const mapDispatchToProps = (dispatch) => ({ + setBillEnterContext: (context) => + dispatch(setModalContext({ context: context, modal: "billEnter" })), +}); +export default connect(mapStateToProps, mapDispatchToProps)(InventoryBillRo); +export function InventoryBillRo({ + bodyshop, + setBillEnterContext, + inventoryline, +}) { + const { t } = useTranslation(); + return ( + { + setBillEnterContext({ + actions: { + //refetch: refetch + }, + context: { + disableInvNumber: true, + //job: { id: job.id }, + consumeinventoryid: inventoryline.id, + bill: { + vendorid: bodyshop.inhousevendorid, + invoice_number: "ih", + isinhouse: true, + date: moment(), + total: 0, + + // billlines: selectedLines.map((p) => { + // return { + // joblineid: p.id, + // actual_price: p.act_price, + // actual_cost: 0, //p.act_price, + // line_desc: p.line_desc, + // line_remarks: p.line_remarks, + // part_type: p.part_type, + // quantity: p.quantity || 1, + // applicable_taxes: { + // local: false, + // state: false, + // federal: false, + // }, + // }; + // }), + }, + }, + }); + }} + > + {t("inventory.actions.addtoro")} + + ); +} diff --git a/client/src/components/inventory-list/inventory-list.component.jsx b/client/src/components/inventory-list/inventory-list.component.jsx index 58210eb94..95dcdea13 100644 --- a/client/src/components/inventory-list/inventory-list.component.jsx +++ b/client/src/components/inventory-list/inventory-list.component.jsx @@ -4,10 +4,11 @@ import queryString from "query-string"; import React from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; -import { useHistory, useLocation } from "react-router-dom"; +import { Link, useHistory, useLocation } from "react-router-dom"; import { createStructuredSelector } from "reselect"; import { selectBodyshop } from "../../redux/user/user.selectors"; import CurrencyFormatter from "../../utils/CurrencyFormatter"; +import InventoryBillRo from "../inventory-bill-ro/inventory-bill-ro.component"; const mapStateToProps = createStructuredSelector({ //currentUser: selectCurrentUser bodyshop: selectBodyshop, @@ -75,7 +76,14 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) { key: "consumedbyjob", ellipsis: true, - render: (text, record) => record.bill?.job?.ro_number, + render: (text, record) => + record.bill?.job?.ro_number ? ( + + {record.bill?.job?.ro_number} + + ) : ( + + ), }, ]; diff --git a/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx b/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx index 9ead0530c..fe394f301 100644 --- a/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx +++ b/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx @@ -11,7 +11,7 @@ const mapDispatchToProps = (dispatch) => ({ }); export default connect(mapStateToProps, mapDispatchToProps)(JobPartsQueueCount); -export function JobPartsQueueCount({ bodyshop, parts }) { +export function JobPartsQueueCount({ bodyshop, parts, style }) { const partsStatus = useMemo(() => { if (!parts) return null; return parts.reduce( @@ -36,7 +36,7 @@ export function JobPartsQueueCount({ bodyshop, parts }) { if (!parts) return null; return ( - + {partsStatus.total} diff --git a/client/src/components/jobs-admin-mark-reexport/jobs-admin-mark-reexport.component.jsx b/client/src/components/jobs-admin-mark-reexport/jobs-admin-mark-reexport.component.jsx index 929c7f6d5..70dbf0a31 100644 --- a/client/src/components/jobs-admin-mark-reexport/jobs-admin-mark-reexport.component.jsx +++ b/client/src/components/jobs-admin-mark-reexport/jobs-admin-mark-reexport.component.jsx @@ -6,12 +6,17 @@ import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; -import { selectBodyshop } from "../../redux/user/user.selectors"; +import { + selectBodyshop, + selectCurrentUser, +} from "../../redux/user/user.selectors"; import moment from "moment"; import AuditTrailMapping from "../../utils/AuditTrailMappings"; import { insertAuditTrail } from "../../redux/application/application.actions"; +import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, + currentUser: selectCurrentUser, }); const mapDispatchToProps = (dispatch) => ({ insertAuditTrail: ({ jobid, operation }) => @@ -22,9 +27,15 @@ export default connect( mapDispatchToProps )(JobAdminMarkReexport); -export function JobAdminMarkReexport({ insertAuditTrail, bodyshop, job }) { +export function JobAdminMarkReexport({ + insertAuditTrail, + bodyshop, + currentUser, + job, +}) { const { t } = useTranslation(); const [loading, setLoading] = useState(false); + const [insertExportLog] = useMutation(INSERT_EXPORT_LOG); const [markJobForReexport] = useMutation(gql` mutation MARK_JOB_FOR_REEXPORT($jobId: uuid!) { update_jobs_by_pk( @@ -101,6 +112,20 @@ export function JobAdminMarkReexport({ insertAuditTrail, bodyshop, job }) { variables: { jobId: job.id, date_exported: moment() }, }); + await insertExportLog({ + variables: { + logs: [ + { + bodyshopid: bodyshop.id, + jobid: job.id, + successful: true, + message: t("general.labels.markedexported"), + useremail: currentUser.email, + }, + ], + }, + }); + if (!result.errors) { notification["success"]({ message: t("jobs.successes.save") }); insertAuditTrail({ diff --git a/client/src/components/payable-mark-selected-exported/payable-mark-selected-exported.component.jsx b/client/src/components/payable-mark-selected-exported/payable-mark-selected-exported.component.jsx index a18c370df..8032e03a5 100644 --- a/client/src/components/payable-mark-selected-exported/payable-mark-selected-exported.component.jsx +++ b/client/src/components/payable-mark-selected-exported/payable-mark-selected-exported.component.jsx @@ -4,14 +4,15 @@ import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; +import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; import { - selectAuthLevel, selectBodyshop, + selectCurrentUser, } from "../../redux/user/user.selectors"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, - authLevel: selectAuthLevel, + currentUser: selectCurrentUser, }); const mapDispatchToProps = (dispatch) => ({ //setUserLanguage: language => dispatch(setUserLanguage(language)) @@ -23,6 +24,8 @@ export default connect( )(BillMarkSelectedExported); export function BillMarkSelectedExported({ + bodyshop, + currentUser, billids, disabled, loadingCallback, @@ -31,7 +34,7 @@ export function BillMarkSelectedExported({ }) { const { t } = useTranslation(); const [loading, setLoading] = useState(false); - + const [insertExportLog] = useMutation(INSERT_EXPORT_LOG); const [updateBill] = useMutation(gql` mutation UPDATE_BILL($billIds: [uuid!]!) { update_bills(where: { id: { _in: $billIds } }, _set: { exported: true }) { @@ -49,9 +52,21 @@ export function BillMarkSelectedExported({ loadingCallback(true); const result = await updateBill({ variables: { billIds: billids }, - update(cache){ - - } + update(cache) {}, + }); + + await insertExportLog({ + variables: { + logs: billids.map((id) => { + return { + bodyshopid: bodyshop.id, + billid: id, + successful: true, + message: t("general.labels.markedexported"), + useremail: currentUser.email, + }; + }), + }, }); if (!result.errors) { diff --git a/client/src/components/production-list-columns/production-list-columns.comment.component.jsx b/client/src/components/production-list-columns/production-list-columns.comment.component.jsx index fafccff93..d66f72c75 100644 --- a/client/src/components/production-list-columns/production-list-columns.comment.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.comment.component.jsx @@ -1,6 +1,6 @@ import Icon from "@ant-design/icons"; import { useMutation } from "@apollo/client"; -import { Button, Input, Popover } from "antd"; +import { Button, Input, Popover, Tooltip } from "antd"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { FaRegStickyNote } from "react-icons/fa"; @@ -69,10 +69,11 @@ export default function ProductionListColumnComment({ record }) { cursor: "pointer", overflow: "hidden", textOverflow: "ellipsis", + display: "inline-block", }} > - {record.comment || " "} + {record.comment || " "} ); diff --git a/client/src/components/production-list-columns/production-list-columns.data.js b/client/src/components/production-list-columns/production-list-columns.data.js index d99f4e5a2..e09e98775 100644 --- a/client/src/components/production-list-columns/production-list-columns.data.js +++ b/client/src/components/production-list-columns/production-list-columns.data.js @@ -5,25 +5,26 @@ import moment from "moment"; import React from "react"; import { Link } from "react-router-dom"; import CurrencyFormatter from "../../utils/CurrencyFormatter"; +import { TimeFormatter } from "../../utils/DateFormatter"; import PhoneFormatter from "../../utils/PhoneFormatter"; import { alphaSort, dateSort, statusSort } from "../../utils/sorters"; import JobAltTransportChange from "../job-at-change/job-at-change.component"; +import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component"; +import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import ProductionSubletsManageComponent from "../production-sublets-manage/production-sublets-manage.component"; import ProductionListColumnAlert from "./production-list-columns.alert.component"; import ProductionListColumnBodyPriority from "./production-list-columns.bodypriority.component"; +import ProductionListColumnComment from "./production-list-columns.comment.component"; import ProductionListDate from "./production-list-columns.date.component"; import ProductionListColumnDetailPriority from "./production-list-columns.detailpriority.component"; import ProductionListEmployeeAssignment from "./production-list-columns.empassignment.component"; import ProductionListLastContacted from "./production-list-columns.lastcontacted.component"; import ProductionListColumnPaintPriority from "./production-list-columns.paintpriority.component"; -import ProductionListColumnNote from "./production-list-columns.productionnote.component"; -import ProductionListColumnStatus from "./production-list-columns.status.component"; -import ProductionListColumnCategory from "./production-list-columns.status.category"; -import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component"; -import ProductionListColumnComment from "./production-list-columns.comment.component"; import ProductionListColumnPartsReceived from "./production-list-columns.partsreceived.component"; -import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; -import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component"; +import ProductionListColumnNote from "./production-list-columns.productionnote.component"; +import ProductionListColumnCategory from "./production-list-columns.status.category"; +import ProductionListColumnStatus from "./production-list-columns.status.component"; +import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component"; const r = ({ technician, state, activeStatuses, bodyshop }) => { return [ @@ -105,6 +106,16 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => { ), }, + { + title: i18n.t("jobs.fields.actual_in") + " (HH:MM)", + dataIndex: "actual_in_time", + key: "actual_in_time", + ellipsis: true, + + render: (text, record) => ( + {record.actual_in} + ), + }, { title: i18n.t("jobs.fields.scheduled_completion"), dataIndex: "scheduled_completion", @@ -124,6 +135,16 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => { /> ), }, + { + title: i18n.t("jobs.fields.scheduled_completion") + " (HH:MM)", + dataIndex: "scheduled_completion_time", + key: "scheduled_completion_time", + ellipsis: true, + + render: (text, record) => ( + {record.scheduled_completion} + ), + }, { title: i18n.t("jobs.fields.date_last_contacted"), dataIndex: "date_last_contacted", @@ -176,6 +197,16 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => { /> ), }, + { + title: i18n.t("jobs.fields.scheduled_delivery") + " (HH:MM)", + dataIndex: "scheduled_delivery_time", + key: "scheduled_delivery_time", + ellipsis: true, + + render: (text, record) => ( + {record.scheduled_delivery} + ), + }, { title: i18n.t("jobs.fields.ins_co_nm"), dataIndex: "ins_co_nm", diff --git a/client/src/components/production-list-columns/production-list-columns.date.component.jsx b/client/src/components/production-list-columns/production-list-columns.date.component.jsx index 5927c99e2..4944d628b 100644 --- a/client/src/components/production-list-columns/production-list-columns.date.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.date.component.jsx @@ -50,50 +50,45 @@ export default function ProductionListDate({ "production-completion-soon")); } return ( - - e.stopPropagation()}> + e.stopPropagation()} - > - + {time && ( + e.stopPropagation()} value={(record[field] && moment(record[field])) || null} onChange={handleChange} - format="MM/DD/YYYY" - isDateOnly={!time} + minuteStep={15} + format="hh:mm a" /> - {time && ( - e.stopPropagation()} - value={(record[field] && moment(record[field])) || null} - onChange={handleChange} - minuteStep={15} - format="hh:mm a" - /> - )} - setVisible(false)}> - {t("general.actions.close")} - - - } + )} + setVisible(false)}> + {t("general.actions.close")} + + + } + > + setVisible(true)} + style={{ + height: "19px", + }} + className={className} > - setVisible(true)} - style={{ - height: "19px", - }} - className={className} - > - {record[field]} - - - + {record[field]} + + ); } diff --git a/client/src/components/sign-in-form/sign-in-form.component.jsx b/client/src/components/sign-in-form/sign-in-form.component.jsx index da1c749f7..c964c0dc2 100644 --- a/client/src/components/sign-in-form/sign-in-form.component.jsx +++ b/client/src/components/sign-in-form/sign-in-form.component.jsx @@ -60,7 +60,10 @@ export function SignInComponent({ { + const handleFinish = async ({ removefromproduction, ...values }) => { setLoading(true); const result = await client.mutate({ mutation: generateJobLinesUpdatesForInvoicing(values.joblines), @@ -63,6 +64,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) { kmin: values.kmin, kmout: values.kmout, dms_allocation: values.dms_allocation, + ...(removefromproduction ? { inproduction: false } : {}), }, }, refetchQueries: ["QUERY_JOB_CLOSE_DETAILS"], @@ -248,6 +250,15 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) { onlyFuture={!!bodyshop.cdk_dealerid} /> + {!jobRO && ( + + + + )} {(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && ( ( - + ), }, + { + title: t("jobs.fields.comment"), + dataIndex: "comment", + key: "comment", + render: (text, record) => , + }, { title: t("jobs.fields.queued_for_parts"), dataIndex: "queued_for_parts", @@ -247,7 +257,7 @@ export function PartsQueuePageComponent({ bodyshop }) { value: false, }, ], - //onFilter: (value, record) => record.queued_for_parts === value, + onFilter: (value, record) => record.queued_for_parts === value, render: (text, record) => ( ({ }); export const getBillMedia = ({ jobid, invoice_number }) => { - console.log("in the action"); return { type: MediaActionTypes.GET_MEDIA_FOR_BILL, payload: { jobid, invoice_number }, diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index f283e1777..c40bfe2be 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -147,6 +147,7 @@ "errors": { "creating": "Error adding bill. {{error}}", "deleting": "Error deleting bill. {{error}}", + "existinginventoryline": "This bill cannot be deleted as it is tied to items in inventory.", "exporting": "Error exporting payable(s). {{error}}", "exporting-partner": "Unable to connect to ImEX Partner. Please ensure it is running and logged in.", "invalidro": "Not a valid RO.", @@ -1075,6 +1076,7 @@ "inventory": { "actions": { "addtoinventory": "Add to Inventory", + "addtoro": "Add to RO", "consumefrominventory": "Consume from Inventory?" }, "errors": { diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index b40b5175d..2793b4bf1 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -147,6 +147,7 @@ "errors": { "creating": "", "deleting": "", + "existinginventoryline": "", "exporting": "", "exporting-partner": "", "invalidro": "", @@ -1075,6 +1076,7 @@ "inventory": { "actions": { "addtoinventory": "", + "addtoro": "", "consumefrominventory": "" }, "errors": { diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 6843f664f..b7011de21 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -147,6 +147,7 @@ "errors": { "creating": "", "deleting": "", + "existinginventoryline": "", "exporting": "", "exporting-partner": "", "invalidro": "", @@ -1075,6 +1076,7 @@ "inventory": { "actions": { "addtoinventory": "", + "addtoro": "", "consumefrominventory": "" }, "errors": { diff --git a/client/src/utils/DateFormatter.jsx b/client/src/utils/DateFormatter.jsx index 18c2e633d..134095c78 100644 --- a/client/src/utils/DateFormatter.jsx +++ b/client/src/utils/DateFormatter.jsx @@ -17,6 +17,11 @@ export function DateTimeFormatter(props) { ) : null; } +export function TimeFormatter(props) { + return props.children + ? moment(props.children).format(props.format ? props.format : "hh:mm a") + : null; +} export function TimeAgoFormatter(props) { const m = moment(props.children); diff --git a/server/job/job-costing.js b/server/job/job-costing.js index 5ccdeba0d..13f9201cc 100644 --- a/server/job/job-costing.js +++ b/server/job/job-costing.js @@ -781,33 +781,33 @@ function GenerateCostingData(job) { //Push adjustments to bottom line. if (job.adjustment_bottom_line) { //Add to totals. - const Adjustment = Dinero({ amount: job.adjustment_bottom_line * -100 }); //Need to invert, since this is being assigned as a cost. - summaryData.totalLaborCost = summaryData.totalLaborCost.add(Adjustment); - summaryData.totalCost = summaryData.totalCost.add(Adjustment); + const Adjustment = Dinero({ amount: job.adjustment_bottom_line * 100 }); //Need to invert, since this is being assigned as a cost. + summaryData.totalLaborSales = summaryData.totalLaborSales.add(Adjustment); + summaryData.totalSales = summaryData.totalSales.add(Adjustment); //Add to lines. costCenterData.push({ id: "Adj", cost_center: "Adjustment", - sale_labor: Dinero().toFormat(), - sale_labor_dinero: Dinero(), + sale_labor: Adjustment.toFormat(), + sale_labor_dinero: Adjustment, sale_parts: Dinero().toFormat(), sale_parts_dinero: Dinero(), sale_additional: Dinero(), sale_additional_dinero: Dinero(), sale_sublet: Dinero(), sale_sublet_dinero: Dinero(), - sales: Dinero().toFormat(), - sales_dinero: Dinero(), + sales: Adjustment.toFormat(), + sales_dinero: Adjustment, cost_parts: Dinero().toFormat(), cost_parts_dinero: Dinero(), - cost_labor: Adjustment.toFormat(), - cost_labor_dinero: Adjustment, + cost_labor: Dinero().toFormat(), //Adjustment.toFormat(), + cost_labor_dinero: Dinero(), // Adjustment, cost_additional: Dinero(), cost_additional_dinero: Dinero(), cost_sublet: Dinero(), cost_sublet_dinero: Dinero(), - costs: Adjustment.toFormat(), - costs_dinero: Adjustment, + costs: Dinero().toFormat(), + costs_dinero: Dinero(), gpdollars_dinero: Dinero(), gpdollars: Dinero().toFormat(), gppercent: formatGpPercent(0),