diff --git a/client/src/components/bill-detail-edit/bill-detail-edit-component.jsx b/client/src/components/bill-detail-edit/bill-detail-edit-component.jsx index e92bb175b..12ca80301 100644 --- a/client/src/components/bill-detail-edit/bill-detail-edit-component.jsx +++ b/client/src/components/bill-detail-edit/bill-detail-edit-component.jsx @@ -10,7 +10,7 @@ import { createStructuredSelector } from "reselect"; import { DELETE_BILL_LINE, INSERT_NEW_BILL_LINES, - UPDATE_BILL_LINE + UPDATE_BILL_LINE, } from "../../graphql/bill-lines.queries"; import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries"; import { insertAuditTrail } from "../../redux/application/application.actions"; @@ -20,6 +20,7 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings"; import AlertComponent from "../alert/alert.component"; import BillFormContainer from "../bill-form/bill-form.container"; import BillMarkExportedButton from "../bill-mark-exported-button/bill-mark-exported-button.component"; +import BillPrintButton from "../bill-print-button/bill-print-button.component"; import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-button.component"; import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container"; import JobsDocumentsLocalGallery from "../jobs-documents-local-gallery/jobs-documents-local-gallery.container"; @@ -176,7 +177,7 @@ export function BillDetailEditcontainer({ extra={ - + form.submit()} diff --git a/client/src/components/bill-print-button/bill-print-button.component.jsx b/client/src/components/bill-print-button/bill-print-button.component.jsx new file mode 100644 index 000000000..46e7d2f72 --- /dev/null +++ b/client/src/components/bill-print-button/bill-print-button.component.jsx @@ -0,0 +1,38 @@ +import { Button, Space } from "antd"; +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { GenerateDocument } from "../../utils/RenderTemplate"; +import { TemplateList } from "../../utils/TemplateConstants"; + +export default function BillPrintButton({ billid }) { + const { t } = useTranslation(); + const [loading, setLoading] = useState(false); + const Templates = TemplateList("job_special"); + + const submitHandler = async () => { + setLoading(true); + try { + await GenerateDocument( + { + name: Templates.parts_invoice_label_single.key, + variables: { + id: billid, + }, + }, + {}, + "p" + ); + } catch (e) { + console.warn("Warning: Error generating a document."); + } + setLoading(false); + }; + + return ( + + + + ); +} diff --git a/client/src/components/contract-cars/contract-cars.component.jsx b/client/src/components/contract-cars/contract-cars.component.jsx index 541d6d7c3..6162ae7af 100644 --- a/client/src/components/contract-cars/contract-cars.component.jsx +++ b/client/src/components/contract-cars/contract-cars.component.jsx @@ -35,6 +35,15 @@ export default function ContractsCarsComponent({ state.sortedInfo.columnKey === "status" && state.sortedInfo.order, render: (text, record) =>
{t(record.status)}
, }, + { + title: t("courtesycars.fields.readiness"), + dataIndex: "readiness", + key: "readiness", + sorter: (a, b) => alphaSort(a.readiness, b.readiness), + sortOrder: + state.sortedInfo.columnKey === "readiness" && state.sortedInfo.order, + render: (text, record) => t(record.readiness), + }, { title: t("courtesycars.fields.year"), dataIndex: "year", diff --git a/client/src/components/courtesy-car-form/courtesy-car-form.component.jsx b/client/src/components/courtesy-car-form/courtesy-car-form.component.jsx index d5315ed3a..a64cb8a2a 100644 --- a/client/src/components/courtesy-car-form/courtesy-car-form.component.jsx +++ b/client/src/components/courtesy-car-form/courtesy-car-form.component.jsx @@ -7,6 +7,7 @@ import { useTranslation } from "react-i18next"; import { CHECK_CC_FLEET_NUMBER } from "../../graphql/courtesy-car.queries"; import { DateFormatter } from "../../utils/DateFormatter"; import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component"; +import CourtesyCarReadiness from "../courtesy-car-readiness-select/courtesy-car-readiness-select.component"; import CourtesyCarStatus from "../courtesy-car-status-select/courtesy-car-status-select.component"; import FormDatePicker from "../form-date-picker/form-date-picker.component"; //import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component"; @@ -213,6 +214,12 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) { > + + +
{ step={null} style={{ marginLeft: "2rem", marginRight: "2rem" }} {...props} + tooltip={{ + formatter: (value) => { + switch (value) { + case 0: + return t("courtesycars.labels.fuel.empty"); + case 13: + return t("courtesycars.labels.fuel.18"); + case 25: + return t("courtesycars.labels.fuel.14"); + case 38: + return t("courtesycars.labels.fuel.38"); + case 50: + return t("courtesycars.labels.fuel.12"); + case 63: + return t("courtesycars.labels.fuel.58"); + case 75: + return t("courtesycars.labels.fuel.34"); + case 88: + return t("courtesycars.labels.fuel.78"); + case 100: + return t("courtesycars.labels.fuel.full"); + default: + return value; + } + }, + }} /> ); }; diff --git a/client/src/components/courtesy-car-readiness-select/courtesy-car-readiness-select.component.jsx b/client/src/components/courtesy-car-readiness-select/courtesy-car-readiness-select.component.jsx new file mode 100644 index 000000000..9dd1fd514 --- /dev/null +++ b/client/src/components/courtesy-car-readiness-select/courtesy-car-readiness-select.component.jsx @@ -0,0 +1,35 @@ +import { Select } from "antd"; +import React, { forwardRef, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +const { Option } = Select; + +const CourtesyCarReadinessComponent = ({ value, onChange }, ref) => { + const [option, setOption] = useState(value); + const { t } = useTranslation(); + + useEffect(() => { + if (value !== option && onChange) { + onChange(option); + } + }, [value, option, onChange]); + + return ( + + ); +}; +export default forwardRef(CourtesyCarReadinessComponent); diff --git a/client/src/components/courtesy-cars-list/courtesy-cars-list.component.jsx b/client/src/components/courtesy-cars-list/courtesy-cars-list.component.jsx index ab85f95bc..7c4b25534 100644 --- a/client/src/components/courtesy-cars-list/courtesy-cars-list.component.jsx +++ b/client/src/components/courtesy-cars-list/courtesy-cars-list.component.jsx @@ -91,6 +91,26 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) { ); }, }, + { + title: t("courtesycars.fields.readiness"), + dataIndex: "readiness", + key: "readiness", + sorter: (a, b) => alphaSort(a.readiness, b.readiness), + filters: [ + { + text: t("courtesycars.readiness.ready"), + value: "courtesycars.readiness.ready", + }, + { + text: t("courtesycars.readiness.notready"), + value: "courtesycars.readiness.notready", + }, + ], + onFilter: (value, record) => value.includes(record.readiness), + sortOrder: + state.sortedInfo.columnKey === "readiness" && state.sortedInfo.order, + render: (text, record) => t(record.readiness), + }, { title: t("courtesycars.fields.year"), dataIndex: "year", @@ -131,6 +151,36 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) { sortOrder: state.sortedInfo.columnKey === "plate" && state.sortedInfo.order, }, + { + title: t("courtesycars.fields.fuel"), + dataIndex: "fuel", + key: "fuel", + sorter: (a, b) => alphaSort(a.fuel, b.fuel), + sortOrder: + state.sortedInfo.columnKey === "fuel" && state.sortedInfo.order, + render: (text, record) => { + switch (record.fuel) { + case 100: + return t("courtesycars.labels.fuel.full"); + case 88: + return t("courtesycars.labels.fuel.78"); + case 63: + return t("courtesycars.labels.fuel.58"); + case 50: + return t("courtesycars.labels.fuel.12"); + case 38: + return t("courtesycars.labels.fuel.34"); + case 25: + return t("courtesycars.labels.fuel.14"); + case 13: + return t("courtesycars.labels.fuel.18"); + case 0: + return t("courtesycars.labels.fuel.empty"); + default: + return record.fuel; + } + }, + }, { title: t("courtesycars.labels.outwith"), dataIndex: "outwith", diff --git a/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx b/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx index a5dd99f6f..30bb98560 100644 --- a/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx +++ b/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx @@ -1,8 +1,8 @@ import { + BranchesOutlined, ExclamationCircleFilled, PauseCircleOutlined, WarningFilled, - BranchesOutlined, } from "@ant-design/icons"; import { Card, Col, Row, Space, Tag, Tooltip } from "antd"; import React, { useState } from "react"; @@ -222,6 +222,9 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) { {`${job.v_vin || t("general.labels.na")}`} + + {job.regie_number || t("general.labels.na")} + diff --git a/client/src/graphql/courtesy-car.queries.js b/client/src/graphql/courtesy-car.queries.js index a5bcbea55..4f7bcd0ca 100644 --- a/client/src/graphql/courtesy-car.queries.js +++ b/client/src/graphql/courtesy-car.queries.js @@ -30,15 +30,15 @@ export const QUERY_AVAILABLE_CC = gql` fuel id make - model - plate - status - year - dailycost mileage + model notes nextservicekm nextservicedate + plate + readiness + status + year } } `; @@ -68,19 +68,20 @@ export const QUERY_ALL_CC = gql` insuranceexpires leaseenddate make + mileage model nextservicedate nextservicekm notes plate purchasedate + readiness registrationexpires serviceenddate servicestartdate status vin year - mileage cccontracts( where: { status: { _eq: "contracts.status.out" } } order_by: { contract_date: desc } @@ -90,10 +91,10 @@ export const QUERY_ALL_CC = gql` scheduledreturn job { id - ro_number ownr_fn ownr_ln ownr_co_nm + ro_number } } } @@ -119,19 +120,20 @@ export const QUERY_CC_BY_PK = gql` insuranceexpires leaseenddate make + mileage model nextservicedate nextservicekm notes plate purchasedate + readiness registrationexpires serviceenddate servicestartdate status vin year - mileage cccontracts_aggregate { aggregate { count(distinct: true) @@ -139,21 +141,20 @@ export const QUERY_CC_BY_PK = gql` } cccontracts(offset: $offset, limit: $limit, order_by: $order) { agreementnumber + driver_fn + driver_ln id - status - start - scheduledreturn kmstart kmend - driver_ln - driver_fn + scheduledreturn + start + status job { - ro_number - + id ownr_ln ownr_fn ownr_co_nm - id + ro_number } } } diff --git a/client/src/pages/courtesy-car-detail/courtesy-car-detail.page.container.jsx b/client/src/pages/courtesy-car-detail/courtesy-car-detail.page.container.jsx index 93bffe97f..bf73b4cc3 100644 --- a/client/src/pages/courtesy-car-detail/courtesy-car-detail.page.container.jsx +++ b/client/src/pages/courtesy-car-detail/courtesy-car-detail.page.container.jsx @@ -1,11 +1,14 @@ import { useMutation, useQuery } from "@apollo/client"; import { Form, notification } from "antd"; import moment from "moment"; +import queryString from "query-string"; import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; -import { useParams } from "react-router-dom"; +import { useLocation, useParams } from "react-router-dom"; import AlertComponent from "../../components/alert/alert.component"; +import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component"; +import NotFound from "../../components/not-found/not-found.component"; import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component"; import { QUERY_CC_BY_PK, UPDATE_CC } from "../../graphql/courtesy-car.queries"; import { @@ -13,13 +16,10 @@ import { setBreadcrumbs, setSelectedHeader, } from "../../redux/application/application.actions"; +import { pageLimit } from "../../utils/config"; import { CreateRecentItem } from "../../utils/create-recent-item"; +import UndefinedToNull from "./../../utils/undefinedtonull"; import CourtesyCarDetailPageComponent from "./courtesy-car-detail.page.component"; -import NotFound from "../../components/not-found/not-found.component"; -import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component"; -import queryString from "query-string"; -import { useLocation } from "react-router-dom"; -import {pageLimit} from "../../utils/config"; const mapDispatchToProps = (dispatch) => ({ setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)), @@ -112,7 +112,10 @@ export function CourtesyCarDetailPageContainer({ setSaveLoading(true); const result = await updateCourtesyCar({ - variables: { cc: { ...values }, ccId: ccId }, + variables: { + cc: { ...UndefinedToNull(values, ["readiness"]) }, + ccId: ccId, + }, refetchQueries: ["QUERY_CC_BY_PK"], awaitRefetchQueries: true, }); diff --git a/client/src/pages/jobs-create/jobs-create.container.jsx b/client/src/pages/jobs-create/jobs-create.container.jsx index 15af524ed..dfe83358c 100644 --- a/client/src/pages/jobs-create/jobs-create.container.jsx +++ b/client/src/pages/jobs-create/jobs-create.container.jsx @@ -1,6 +1,6 @@ -import _ from "lodash"; import { useLazyQuery, useMutation } from "@apollo/client"; import { Form, notification } from "antd"; +import _ from "lodash"; import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; @@ -90,6 +90,7 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) { {}, values, { date_open: new Date() }, + { date_estimated: new Date() }, { vehicle: state.vehicle.selectedid || state.vehicle.none diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index b96b555db..09798f0a5 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -214,6 +214,7 @@ "new": "New Bill", "noneselected": "No bill selected.", "onlycmforinvoiced": "Only credit memos can be entered for any Job that has been invoiced, exported, or voided.", + "printlabels": "Print Labels", "retailtotal": "Bills Retail Total", "savewithdiscrepancy": "You are about to save this bill with a discrepancy. The system will continue to use the calculated amount using the bill lines. Press cancel to return to the bill.", "state_tax": "Provincial/State Tax", @@ -785,6 +786,7 @@ "notes": "Notes", "plate": "Plate Number", "purchasedate": "Purchase Date", + "readiness": "Readiness", "registrationexpires": "Registration Expires On", "serviceenddate": "Usage End Date", "servicestartdate": "Usage Start Date", @@ -821,6 +823,10 @@ }, "successes": { "saved": "Courtesy Car saved successfully." + }, + "readiness": { + "notready": "Not Ready", + "ready": "Ready" } }, "csi": { diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index f68cf4a41..da9f6b4e0 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -214,6 +214,7 @@ "new": "", "noneselected": "", "onlycmforinvoiced": "", + "printlabels": "", "retailtotal": "", "savewithdiscrepancy": "", "state_tax": "", @@ -785,6 +786,7 @@ "notes": "", "plate": "", "purchasedate": "", + "readiness": "", "registrationexpires": "", "serviceenddate": "", "servicestartdate": "", @@ -821,6 +823,10 @@ }, "successes": { "saved": "" + }, + "readiness": { + "notready": "", + "ready": "" } }, "csi": { diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index d0e44b704..a4c1dc686 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -214,6 +214,7 @@ "new": "", "noneselected": "", "onlycmforinvoiced": "", + "printlabels": "", "retailtotal": "", "savewithdiscrepancy": "", "state_tax": "", @@ -785,6 +786,7 @@ "notes": "", "plate": "", "purchasedate": "", + "readiness": "", "registrationexpires": "", "serviceenddate": "", "servicestartdate": "", @@ -821,6 +823,10 @@ }, "successes": { "saved": "" + }, + "readiness": { + "notready": "", + "ready": "" } }, "csi": { diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index d94a97af0..216ac8c48 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -1388,60 +1388,62 @@ - active: _eq: true columns: - - id - - created_at - - updated_at - bodyshopid - - make - - model - - year - - plate - color - - vin - - fleetnumber - - purchasedate - - servicestartdate - - serviceenddate - - leaseenddate - - status - - nextservicekm - - nextservicedate - - damage - - notes - - fuel - - registrationexpires - - insuranceexpires + - created_at - dailycost + - damage + - fleetnumber + - fuel + - id + - insuranceexpires + - leaseenddate + - make - mileage + - model + - nextservicedate + - nextservicekm + - notes + - plate + - purchasedate + - readiness + - registrationexpires + - serviceenddate + - servicestartdate + - status + - updated_at + - vin + - year select_permissions: - role: user permission: columns: + - bodyshopid + - color + - created_at + - dailycost + - damage + - fleetnumber + - fuel + - id - insuranceexpires - leaseenddate + - make + - mileage + - model - nextservicedate + - nextservicekm + - notes + - plate - purchasedate + - readiness - registrationexpires - serviceenddate - servicestartdate - - dailycost - - fuel - - mileage - - nextservicekm - - color - - damage - - fleetnumber - - make - - model - - notes - - plate - status + - updated_at - vin - year - - created_at - - updated_at - - bodyshopid - - id filter: bodyshop: associations: @@ -1456,31 +1458,32 @@ - role: user permission: columns: + - bodyshopid + - color + - created_at + - dailycost + - damage + - fleetnumber + - fuel + - id - insuranceexpires - leaseenddate + - make + - mileage + - model - nextservicedate + - nextservicekm + - notes + - plate - purchasedate + - readiness - registrationexpires - serviceenddate - servicestartdate - - dailycost - - fuel - - mileage - - nextservicekm - - color - - damage - - fleetnumber - - make - - model - - notes - - plate - status + - updated_at - vin - year - - created_at - - updated_at - - bodyshopid - - id filter: bodyshop: associations: diff --git a/hasura/migrations/1703706449547_alter_table_public_courtesycars_add_column_readiness/down.sql b/hasura/migrations/1703706449547_alter_table_public_courtesycars_add_column_readiness/down.sql new file mode 100644 index 000000000..5e741dcc7 --- /dev/null +++ b/hasura/migrations/1703706449547_alter_table_public_courtesycars_add_column_readiness/down.sql @@ -0,0 +1,4 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- alter table "public"."courtesycars" add column "readiness" text +-- null; diff --git a/hasura/migrations/1703706449547_alter_table_public_courtesycars_add_column_readiness/up.sql b/hasura/migrations/1703706449547_alter_table_public_courtesycars_add_column_readiness/up.sql new file mode 100644 index 000000000..4f5339ede --- /dev/null +++ b/hasura/migrations/1703706449547_alter_table_public_courtesycars_add_column_readiness/up.sql @@ -0,0 +1,2 @@ +alter table "public"."courtesycars" add column "readiness" text + null;