diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index c41c57d59..f6b4545f5 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -3613,6 +3613,27 @@ validation + + closingperiod + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + inventoryquantity false @@ -4247,6 +4268,27 @@ + + closingperiod + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + country false @@ -28658,6 +28700,27 @@ + + closingperiod + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + contracts false @@ -34101,6 +34164,27 @@ + + dispatchedparts + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + home false @@ -36192,9 +36276,56 @@ parts_dispatch + + actions + + + accept + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + errors + + accepting + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + creating false @@ -44923,6 +45054,27 @@ + + work_in_progress_jobs + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + work_in_progress_labour false diff --git a/client/src/components/bill-form/bill-form.component.jsx b/client/src/components/bill-form/bill-form.component.jsx index 3ff123743..0fa28359a 100644 --- a/client/src/components/bill-form/bill-form.component.jsx +++ b/client/src/components/bill-form/bill-form.component.jsx @@ -1,6 +1,6 @@ import Icon, { UploadOutlined } from "@ant-design/icons"; import { useApolloClient } from "@apollo/client"; -import { MdOpenInNew } from "react-icons/md"; +import { useTreatments } from "@splitsoftware/splitio-react"; import { Alert, Divider, @@ -12,14 +12,17 @@ import { Switch, Upload, } from "antd"; +import moment from "moment"; import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; +import { MdOpenInNew } from "react-icons/md"; import { connect } from "react-redux"; import { Link } from "react-router-dom"; import { createStructuredSelector } from "reselect"; import { CHECK_BILL_INVOICE_NUMBER } from "../../graphql/bills.queries"; import { selectBodyshop } from "../../redux/user/user.selectors"; import AlertComponent from "../alert/alert.component"; +import BillFormLinesExtended from "../bill-form-lines-extended/bill-form-lines-extended.component"; import FormDatePicker from "../form-date-picker/form-date-picker.component"; import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component"; import CurrencyInput from "../form-items-formatted/currency-form-item.component"; @@ -28,8 +31,6 @@ import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component"; import BillFormLines from "./bill-form.lines.component"; import { CalculateBillTotal } from "./bill-form.totals.utility"; -import { useTreatments } from "@splitsoftware/splitio-react"; -import BillFormLinesExtended from "../bill-form-lines-extended/bill-form-lines-extended.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -58,6 +59,11 @@ export function BillFormComponent({ {}, bodyshop.imexshopid ); + const { ClosingPeriod } = useTreatments( + ["ClosingPeriod"], + {}, + bodyshop.imexshopid + ); const handleVendorSelect = (props, opt) => { setDiscount(opt.discount); @@ -259,6 +265,37 @@ export function BillFormComponent({ required: true, //message: t("general.validation.required"), }, + ({ getFieldValue }) => ({ + validator(rule, value) { + if ( + ClosingPeriod.treatment === "on" && + bodyshop.accountingconfig.ClosingPeriod + ) { + if ( + moment(value) + .startOf("day") + .isSameOrAfter( + moment( + bodyshop.accountingconfig.ClosingPeriod[0] + ).startOf("day") + ) && + moment(value) + .startOf("day") + .isSameOrBefore( + moment( + bodyshop.accountingconfig.ClosingPeriod[1] + ).endOf("day") + ) + ) { + return Promise.resolve(); + } else { + return Promise.reject(t("bills.validation.closingperiod")); + } + } else { + return Promise.resolve(); + } + }, + }), ]} > diff --git a/client/src/components/job-detail-lines/job-lines-expander.component.jsx b/client/src/components/job-detail-lines/job-lines-expander.component.jsx index 06e00c729..b64924210 100644 --- a/client/src/components/job-detail-lines/job-lines-expander.component.jsx +++ b/client/src/components/job-detail-lines/job-lines-expander.component.jsx @@ -8,7 +8,18 @@ import CurrencyFormatter from "../../utils/CurrencyFormatter"; import { DateFormatter } from "../../utils/DateFormatter"; import AlertComponent from "../alert/alert.component"; -export default function JobLinesExpander({ jobline, jobid }) { +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +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)(JobLinesExpander); + +export function JobLinesExpander({ jobline, jobid, bodyshop }) { const { t } = useTranslation(); const { loading, error, data } = useQuery(GET_JOB_LINE_ORDERS, { fetchPolicy: "network-only", @@ -23,7 +34,7 @@ export default function JobLinesExpander({ jobline, jobid }) { return ( - + {t("parts_orders.labels.parts_orders")} @@ -49,7 +60,7 @@ export default function JobLinesExpander({ jobline, jobid }) { )} - + {t("bills.labels.bills")} {data.billlines.length > 0 ? ( @@ -71,7 +82,7 @@ export default function JobLinesExpander({ jobline, jobid }) { - {`${t("billlines.fields.actual_cost")}: `} + {`${t("billlines.fields.actual_cost")}: `} {line.actual_cost} @@ -89,6 +100,37 @@ export default function JobLinesExpander({ jobline, jobid }) { )} + + + {t("parts_dispatch.labels.parts_dispatch")} + + + {data.parts_dispatch_lines.length > 0 ? ( + data.parts_dispatch_lines.map((line) => ( + + } wrap> + + {line.parts_dispatch.number} + + { + bodyshop.employees.find( + (e) => e.id === line.parts_dispatch.employeeid + )?.first_name + } + + {t("parts_dispatch_lines.fields.accepted_at")} + {line.accepted_at} + + + + )) + ) : ( + + {t("parts_orders.labels.notyetordered")} + + )} + + ); } diff --git a/client/src/components/job-line-dispatch-button/job-line-dispatch-button.component.jsx b/client/src/components/job-line-dispatch-button/job-line-dispatch-button.component.jsx index 04b808932..50fdfee05 100644 --- a/client/src/components/job-line-dispatch-button/job-line-dispatch-button.component.jsx +++ b/client/src/components/job-line-dispatch-button/job-line-dispatch-button.component.jsx @@ -70,7 +70,7 @@ export function JobLineDispatchButton({ notification.open({ type: "error", message: t("parts_dispatch.errors.creating", { - error: JSON.stringify(result.errors), + error: result.errors, }), }); } else { @@ -79,7 +79,7 @@ export function JobLineDispatchButton({ { name: Templates.parts_dispatch.key, variables: { - id: result.data.insert_part_dispatch_one.id, + id: result.data.insert_parts_dispatch_one.id, }, }, {}, @@ -91,7 +91,7 @@ export function JobLineDispatchButton({ notification.open({ type: "error", message: t("parts_dispatch.errors.creating", { - error: JSON.stringify(error), + error: error, }), }); } finally { @@ -137,7 +137,12 @@ export function JobLineDispatchButton({ - + ), }, ]; return ( @@ -38,7 +70,7 @@ export default function PartsDispatchExpander({ dispatch, job }) { diff --git a/client/src/components/shop-info/shop-info.container.jsx b/client/src/components/shop-info/shop-info.container.jsx index fec10877c..dd254a4cb 100644 --- a/client/src/components/shop-info/shop-info.container.jsx +++ b/client/src/components/shop-info/shop-info.container.jsx @@ -1,14 +1,14 @@ -import React, { useEffect, useState } from "react"; -import ShopInfoComponent from "./shop-info.component"; +import { useMutation, useQuery } from "@apollo/client"; import { Form, notification } from "antd"; -import { useQuery, useMutation } from "@apollo/client"; -import { QUERY_BODYSHOP, UPDATE_SHOP } from "../../graphql/bodyshop.queries"; -import LoadingSpinner from "../loading-spinner/loading-spinner.component"; -import AlertComponent from "../alert/alert.component"; +import moment from "moment"; +import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { logImEXEvent } from "../../firebase/firebase.utils"; +import { QUERY_BODYSHOP, UPDATE_SHOP } from "../../graphql/bodyshop.queries"; +import AlertComponent from "../alert/alert.component"; import FormsFieldChanged from "../form-fields-changed-alert/form-fields-changed-alert.component"; -import moment from "moment"; +import LoadingSpinner from "../loading-spinner/loading-spinner.component"; +import ShopInfoComponent from "./shop-info.component"; export default function ShopInfoContainer() { const [form] = Form.useForm(); const { t } = useTranslation(); @@ -52,13 +52,28 @@ export default function ShopInfoContainer() { onFinish={handleFinish} initialValues={ data - ? { - ...data.bodyshops[0], - schedule_start_time: moment( - data.bodyshops[0].schedule_start_time - ), - schedule_end_time: moment(data.bodyshops[0].schedule_end_time), - } + ? data.bodyshops[0].accountingconfig.ClosingPeriod + ? { + ...data.bodyshops[0], + accountingconfig: { + ...data.bodyshops[0].accountingconfig, + ClosingPeriod: [ + moment(data.bodyshops[0].accountingconfig.ClosingPeriod[0]), + moment(data.bodyshops[0].accountingconfig.ClosingPeriod[1]), + ], + }, + schedule_start_time: moment( + data.bodyshops[0].schedule_start_time + ), + schedule_end_time: moment(data.bodyshops[0].schedule_end_time), + } + : { + ...data.bodyshops[0], + schedule_start_time: moment( + data.bodyshops[0].schedule_start_time + ), + schedule_end_time: moment(data.bodyshops[0].schedule_end_time), + } : null } > diff --git a/client/src/components/shop-info/shop-info.general.component.jsx b/client/src/components/shop-info/shop-info.general.component.jsx index 99245c76e..ebf2accaf 100644 --- a/client/src/components/shop-info/shop-info.general.component.jsx +++ b/client/src/components/shop-info/shop-info.general.component.jsx @@ -1,6 +1,8 @@ import { DeleteFilled } from "@ant-design/icons"; +import { useTreatments } from "@splitsoftware/splitio-react"; import { Button, + DatePicker, Form, Input, InputNumber, @@ -9,8 +11,13 @@ import { Space, Switch, } from "antd"; +import momentTZ from "moment-timezone"; import React from "react"; import { useTranslation } from "react-i18next"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { selectBodyshop } from "../../redux/user/user.selectors"; +import DatePickerRanges from "../../utils/DatePickerRanges"; import CurrencyInput from "../form-items-formatted/currency-form-item.component"; import FormItemEmail from "../form-items-formatted/email-form-item.component"; import PhoneFormItem, { @@ -18,12 +25,22 @@ import PhoneFormItem, { } from "../form-items-formatted/phone-form-item.component"; import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component"; - -import momentTZ from "moment-timezone"; const timeZonesList = momentTZ.tz.names(); +const mapStateToProps = createStructuredSelector({ + bodyshop: selectBodyshop, +}); +const mapDispatchToProps = (dispatch) => ({ + //setUserLanguage: language => dispatch(setUserLanguage(language)) +}); +export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoGeneral); -export default function ShopInfoGeneral({ form }) { +export function ShopInfoGeneral({ form, bodyshop }) { const { t } = useTranslation(); + const { ClosingPeriod } = useTreatments( + ["ClosingPeriod"], + {}, + bodyshop && bodyshop.imexshopid + ); return (
@@ -392,6 +409,20 @@ export default function ShopInfoGeneral({ form }) { >
( + + ), + rowExpandable: (record) => true, + //expandRowByClick: true, + expandIcon: ({ expanded, onExpand, record }) => + expanded ? ( + onExpand(record, e)} /> + ) : ( + onExpand(record, e)} /> + ), + }} + /> + + ); +} +export default connect( + mapStateToProps, + mapDispatchToProps +)(TechDispatchedParts); diff --git a/client/src/pages/tech/tech.page.component.jsx b/client/src/pages/tech/tech.page.component.jsx index 078dfca13..8a65b5b3b 100644 --- a/client/src/pages/tech/tech.page.component.jsx +++ b/client/src/pages/tech/tech.page.component.jsx @@ -44,7 +44,10 @@ const TimeTicketModalTask = lazy(() => ); const TechAssignedProdJobs = lazy(() => import("../tech-assigned-prod-jobs/tech-assigned-prod-jobs.component") +);const TechDispatchedParts = lazy(() => + import("../tech-dispatched-parts/tech-dispatched-parts.page") ); + const { Content } = Layout; const mapStateToProps = createStructuredSelector({ @@ -116,6 +119,11 @@ export function TechPage({ technician, match }) { path={`${match.path}/assigned`} component={TechAssignedProdJobs} /> + diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index c42cecf0b..6b708787e 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -224,6 +224,7 @@ "reexport": "Bill marked for re-export." }, "validation": { + "closingperiod": "This Bill Date is outside of the Closing Period.", "inventoryquantity": "Quantity must be greater than or equal to what has been added to inventory ({{number}}).", "manualinhouse": "Manual posting to the in house vendor is restricted. ", "unique_invoice_number": "This invoice number has already been entered for this vendor." @@ -263,6 +264,7 @@ "bill_local_tax_rate": "Bill - Local Tax Rate %", "bill_state_tax_rate": "Bill - State Tax Rate %", "city": "City", + "closingperiod": "Closing Period", "country": "Country", "dailybodytarget": "Scoreboard - Daily Body Target", "dailypainttarget": "Scoreboard - Daily Paint Target", @@ -1695,6 +1697,7 @@ "checklists": "Checklists", "closeconfirm": "Are you sure you want to close this job? This cannot be easily undone.", "closejob": "Close Job {{ro_number}}", + "closingperiod": "This Invoice Date is outside of the Closing Period.", "contracts": "CC Contracts", "convertedtolabor": "Lines Converted to Labor", "cost": "Cost", @@ -2000,6 +2003,7 @@ "tech": { "assignedjobs": "Assigned Jobs", "claimtask": "Flag Hours", + "dispatchedparts": "Dispatched Parts", "home": "Home", "jobclockin": "Job Clock In", "jobclockout": "Job Clock Out", @@ -2141,7 +2145,11 @@ } }, "parts_dispatch": { + "actions": { + "accept": "Accept" + }, "errors": { + "accepting": "Error accepting parts dispatch. {{error}}", "creating": "Error dispatching parts. {{error}}" }, "fields": { @@ -2651,6 +2659,7 @@ "timetickets_summary": "Time Tickets Summary", "unclaimed_hrs": "Unflagged Hours", "void_ros": "Void ROs", + "work_in_progress_jobs": "Work in Progress - Jobs", "work_in_progress_labour": "Work in Progress - Labor", "work_in_progress_payables": "Work in Progress - Payables" } diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 511517aeb..771ba8750 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -224,6 +224,7 @@ "reexport": "" }, "validation": { + "closingperiod": "", "inventoryquantity": "", "manualinhouse": "", "unique_invoice_number": "" @@ -263,6 +264,7 @@ "bill_local_tax_rate": "", "bill_state_tax_rate": "", "city": "", + "closingperiod": "", "country": "", "dailybodytarget": "", "dailypainttarget": "", @@ -1695,6 +1697,7 @@ "checklists": "", "closeconfirm": "", "closejob": "", + "closingperiod": "", "contracts": "", "convertedtolabor": "", "cost": "", @@ -2000,6 +2003,7 @@ "tech": { "assignedjobs": "", "claimtask": "", + "dispatchedparts": "", "home": "", "jobclockin": "", "jobclockout": "", @@ -2141,7 +2145,11 @@ } }, "parts_dispatch": { + "actions": { + "accept": "" + }, "errors": { + "accepting": "", "creating": "" }, "fields": { @@ -2651,6 +2659,7 @@ "timetickets_summary": "", "unclaimed_hrs": "", "void_ros": "", + "work_in_progress_jobs": "", "work_in_progress_labour": "", "work_in_progress_payables": "" } diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index d134b4a6f..99bbc2254 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -224,6 +224,7 @@ "reexport": "" }, "validation": { + "closingperiod": "", "inventoryquantity": "", "manualinhouse": "", "unique_invoice_number": "" @@ -263,6 +264,7 @@ "bill_local_tax_rate": "", "bill_state_tax_rate": "", "city": "", + "closingperiod": "", "country": "", "dailybodytarget": "", "dailypainttarget": "", @@ -1695,6 +1697,7 @@ "checklists": "", "closeconfirm": "", "closejob": "", + "closingperiod": "", "contracts": "", "convertedtolabor": "", "cost": "", @@ -2000,6 +2003,7 @@ "tech": { "assignedjobs": "", "claimtask": "", + "dispatchedparts": "", "home": "", "jobclockin": "", "jobclockout": "", @@ -2141,7 +2145,11 @@ } }, "parts_dispatch": { + "actions": { + "accept": "" + }, "errors": { + "accepting": "", "creating": "" }, "fields": { @@ -2651,6 +2659,7 @@ "timetickets_summary": "", "unclaimed_hrs": "", "void_ros": "", + "work_in_progress_jobs": "", "work_in_progress_labour": "", "work_in_progress_payables": "" } diff --git a/client/src/utils/TemplateConstants.js b/client/src/utils/TemplateConstants.js index 65dfb3dab..e9e8e9b6a 100644 --- a/client/src/utils/TemplateConstants.js +++ b/client/src/utils/TemplateConstants.js @@ -1556,6 +1556,19 @@ export const TemplateList = (type, context) => { }, group: "payroll", }, + work_in_progress_jobs_excel: { + title: i18n.t("reportcenter.templates.work_in_progress_jobs"), + subject: i18n.t("reportcenter.templates.work_in_progress_jobs"), + key: "work_in_progress_jobs_excel", + //idtype: "vendor", + reporttype: "excel", + disabled: false, + rangeFilter: { + object: i18n.t("reportcenter.labels.objects.jobs"), + field: i18n.t("jobs.fields.date_open"), + }, + group: "jobs", + }, work_in_progress_labour: { title: i18n.t("reportcenter.templates.work_in_progress_labour"), description: "", diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index cf189ea3f..a24db9297 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -4649,6 +4649,7 @@ _eq: X-Hasura-User-Id - active: _eq: true + allow_aggregations: true update_permissions: - role: user permission: @@ -4726,6 +4727,7 @@ _eq: X-Hasura-User-Id - active: _eq: true + allow_aggregations: true update_permissions: - role: user permission: diff --git a/hasura/migrations/1691177401095_create_index_parts_dispatch_employeeid/down.sql b/hasura/migrations/1691177401095_create_index_parts_dispatch_employeeid/down.sql new file mode 100644 index 000000000..d49495dbb --- /dev/null +++ b/hasura/migrations/1691177401095_create_index_parts_dispatch_employeeid/down.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS "public"."parts_dispatch_employeeid"; diff --git a/hasura/migrations/1691177401095_create_index_parts_dispatch_employeeid/up.sql b/hasura/migrations/1691177401095_create_index_parts_dispatch_employeeid/up.sql new file mode 100644 index 000000000..cfeb1460e --- /dev/null +++ b/hasura/migrations/1691177401095_create_index_parts_dispatch_employeeid/up.sql @@ -0,0 +1,2 @@ +CREATE INDEX "parts_dispatch_employeeid" on + "public"."parts_dispatch" using btree ("employeeid"); diff --git a/hasura/migrations/1691177452060_create_index_parts_dispatch_dispatchid/down.sql b/hasura/migrations/1691177452060_create_index_parts_dispatch_dispatchid/down.sql new file mode 100644 index 000000000..edc36c9f2 --- /dev/null +++ b/hasura/migrations/1691177452060_create_index_parts_dispatch_dispatchid/down.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS "public"."parts_dispatch_dispatchid"; diff --git a/hasura/migrations/1691177452060_create_index_parts_dispatch_dispatchid/up.sql b/hasura/migrations/1691177452060_create_index_parts_dispatch_dispatchid/up.sql new file mode 100644 index 000000000..643325a9f --- /dev/null +++ b/hasura/migrations/1691177452060_create_index_parts_dispatch_dispatchid/up.sql @@ -0,0 +1,2 @@ +CREATE INDEX "parts_dispatch_dispatchid" on + "public"."parts_dispatch_lines" using btree ("partsdispatchid"); diff --git a/hasura/migrations/1691177467690_create_index_parts_dispatch_line_accepted_at/down.sql b/hasura/migrations/1691177467690_create_index_parts_dispatch_line_accepted_at/down.sql new file mode 100644 index 000000000..5268f9914 --- /dev/null +++ b/hasura/migrations/1691177467690_create_index_parts_dispatch_line_accepted_at/down.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS "public"."parts_dispatch_line_accepted_at"; diff --git a/hasura/migrations/1691177467690_create_index_parts_dispatch_line_accepted_at/up.sql b/hasura/migrations/1691177467690_create_index_parts_dispatch_line_accepted_at/up.sql new file mode 100644 index 000000000..9d88ca6cc --- /dev/null +++ b/hasura/migrations/1691177467690_create_index_parts_dispatch_line_accepted_at/up.sql @@ -0,0 +1,2 @@ +CREATE INDEX "parts_dispatch_line_accepted_at" on + "public"."parts_dispatch_lines" using btree ("accepted_at");