IO-2371 Closing Period

This commit is contained in:
Allan Carr
2023-08-04 09:05:03 -07:00
parent 5724d0129c
commit 1a5c74dc79
7 changed files with 146 additions and 30 deletions

View File

@@ -1,6 +1,6 @@
import Icon, { UploadOutlined } from "@ant-design/icons"; import Icon, { UploadOutlined } from "@ant-design/icons";
import { useApolloClient } from "@apollo/client"; import { useApolloClient } from "@apollo/client";
import { MdOpenInNew } from "react-icons/md"; import { useTreatments } from "@splitsoftware/splitio-react";
import { import {
Alert, Alert,
Divider, Divider,
@@ -14,12 +14,14 @@ import {
} from "antd"; } from "antd";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { MdOpenInNew } from "react-icons/md";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { CHECK_BILL_INVOICE_NUMBER } from "../../graphql/bills.queries"; import { CHECK_BILL_INVOICE_NUMBER } from "../../graphql/bills.queries";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component"; 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 FormDatePicker from "../form-date-picker/form-date-picker.component";
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component"; import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component"; import CurrencyInput from "../form-items-formatted/currency-form-item.component";
@@ -28,8 +30,6 @@ import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component"; import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
import BillFormLines from "./bill-form.lines.component"; import BillFormLines from "./bill-form.lines.component";
import { CalculateBillTotal } from "./bill-form.totals.utility"; 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({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -58,6 +58,11 @@ export function BillFormComponent({
{}, {},
bodyshop.imexshopid bodyshop.imexshopid
); );
const { ClosingPeriod } = useTreatments(
["ClosingPeriod"],
{},
bodyshop.imexshopid
);
const handleVendorSelect = (props, opt) => { const handleVendorSelect = (props, opt) => {
setDiscount(opt.discount); setDiscount(opt.discount);
@@ -259,6 +264,27 @@ export function BillFormComponent({
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
({ getFieldValue }) => ({
validator(rule, value) {
if (
ClosingPeriod.treatment === "on" &&
bodyshop.accountingconfig.ClosingPeriod
) {
if (
Date.parse(value).valueOf() >=
Date.parse(bodyshop.accountingconfig.ClosingPeriod[0]).valueOf() &&
Date.parse(value).valueOf() <=
Date.parse(bodyshop.accountingconfig.ClosingPeriod[1]).valueOf()
) {
return Promise.resolve();
} else {
return Promise.reject(t("bills.validation.closingperiod"));
}
} else {
return Promise.resolve();
}
},
}),
]} ]}
> >
<FormDatePicker disabled={disabled} /> <FormDatePicker disabled={disabled} />

View File

@@ -1,14 +1,14 @@
import React, { useEffect, useState } from "react"; import { useMutation, useQuery } from "@apollo/client";
import ShopInfoComponent from "./shop-info.component";
import { Form, notification } from "antd"; import { Form, notification } from "antd";
import { useQuery, useMutation } from "@apollo/client"; import moment from "moment";
import { QUERY_BODYSHOP, UPDATE_SHOP } from "../../graphql/bodyshop.queries"; import React, { useEffect, useState } from "react";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import AlertComponent from "../alert/alert.component";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils"; 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 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() { export default function ShopInfoContainer() {
const [form] = Form.useForm(); const [form] = Form.useForm();
const { t } = useTranslation(); const { t } = useTranslation();
@@ -52,13 +52,28 @@ export default function ShopInfoContainer() {
onFinish={handleFinish} onFinish={handleFinish}
initialValues={ initialValues={
data data
? { ? data.bodyshops[0].accountingconfig.ClosingPeriod
...data.bodyshops[0], ? {
schedule_start_time: moment( ...data.bodyshops[0],
data.bodyshops[0].schedule_start_time accountingconfig: {
), ...data.bodyshops[0].accountingconfig,
schedule_end_time: moment(data.bodyshops[0].schedule_end_time), 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 : null
} }
> >

View File

@@ -1,6 +1,8 @@
import { DeleteFilled } from "@ant-design/icons"; import { DeleteFilled } from "@ant-design/icons";
import { useTreatments } from "@splitsoftware/splitio-react";
import { import {
Button, Button,
DatePicker,
Form, Form,
Input, Input,
InputNumber, InputNumber,
@@ -9,8 +11,13 @@ import {
Space, Space,
Switch, Switch,
} from "antd"; } from "antd";
import momentTZ from "moment-timezone";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; 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 CurrencyInput from "../form-items-formatted/currency-form-item.component";
import FormItemEmail from "../form-items-formatted/email-form-item.component"; import FormItemEmail from "../form-items-formatted/email-form-item.component";
import PhoneFormItem, { import PhoneFormItem, {
@@ -18,12 +25,22 @@ import PhoneFormItem, {
} from "../form-items-formatted/phone-form-item.component"; } from "../form-items-formatted/phone-form-item.component";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component"; import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import momentTZ from "moment-timezone";
const timeZonesList = momentTZ.tz.names(); 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 { t } = useTranslation();
const { ClosingPeriod } = useTreatments(
["ClosingPeriod"],
{},
bodyshop && bodyshop.imexshopid
);
return ( return (
<div> <div>
@@ -392,6 +409,20 @@ export default function ShopInfoGeneral({ form }) {
> >
<Select mode="tags" /> <Select mode="tags" />
</Form.Item> </Form.Item>
{ClosingPeriod.treatment === "on" && (
<>
<Form.Item
allowClear
name={["accountingconfig", "ClosingPeriod"]}
label={t("bodyshop.fields.closingperiod")} //{t("reportcenter.labels.dates")}
>
<DatePicker.RangePicker
format="MM/DD/YYYY"
ranges={DatePickerRanges}
/>
</Form.Item>
</>
)}
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow <LayoutFormRow
header={t("bodyshop.labels.scoreboardsetup")} header={t("bodyshop.labels.scoreboardsetup")}
@@ -604,7 +635,9 @@ export default function ShopInfoGeneral({ form }) {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name={["md_email_cc", "parts_return_slip"]} name={["md_email_cc", "parts_return_slip"]}
label={t("bodyshop.fields.md_email_cc", { template: "parts_return_slip" })} label={t("bodyshop.fields.md_email_cc", {
template: "parts_return_slip",
})}
rules={[ rules={[
{ {
//message: t("general.validation.required"), //message: t("general.validation.required"),

View File

@@ -8,7 +8,6 @@ import {
Form, Form,
Input, Input,
InputNumber, InputNumber,
notification,
PageHeader, PageHeader,
Popconfirm, Popconfirm,
Row, Row,
@@ -17,12 +16,14 @@ import {
Statistic, Statistic,
Switch, Switch,
Typography, Typography,
notification,
} from "antd"; } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
//import { useHistory } from "react-router-dom"; //import { useHistory } from "react-router-dom";
import { useTreatments } from "@splitsoftware/splitio-react"; import { useTreatments } from "@splitsoftware/splitio-react";
import Dinero from "dinero.js";
import moment from "moment"; import moment from "moment";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
@@ -37,7 +38,6 @@ import { generateJobLinesUpdatesForInvoicing } from "../../graphql/jobs-lines.qu
import { UPDATE_JOB } from "../../graphql/jobs.queries"; import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { selectJobReadOnly } from "../../redux/application/application.selectors"; import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import Dinero from "dinero.js";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
jobRO: selectJobReadOnly, jobRO: selectJobReadOnly,
@@ -55,6 +55,11 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
{}, {},
bodyshop && bodyshop.imexshopid bodyshop && bodyshop.imexshopid
); );
const { ClosingPeriod } = useTreatments(
["ClosingPeriod"],
{},
bodyshop && bodyshop.imexshopid
);
const handleFinish = async ({ removefromproduction, ...values }) => { const handleFinish = async ({ removefromproduction, ...values }) => {
setLoading(true); setLoading(true);
@@ -250,14 +255,42 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
}, },
({ getFieldValue }) => ({ ({ getFieldValue }) => ({
validator(_, value) { validator(_, value) {
if (!bodyshop.cdk_dealerid) return Promise.resolve(); if (!bodyshop.cdk_dealerid) {
if (!value || moment(value).isSameOrAfter(moment(), "day")) { if (
return Promise.resolve(); ClosingPeriod.treatment === "on" &&
bodyshop.accountingconfig.ClosingPeriod
) {
if (
Date.parse(value).valueOf() >=
Date.parse(
bodyshop.accountingconfig.ClosingPeriod[0]
).valueOf() &&
Date.parse(value).valueOf() <=
Date.parse(
bodyshop.accountingconfig.ClosingPeriod[1]
).valueOf()
) {
return Promise.resolve();
} else {
return Promise.reject(
new Error(t("jobs.labels.closingperiod"))
);
}
} else {
return Promise.resolve();
}
} else {
if (
!value ||
moment(value).isSameOrAfter(moment(), "day")
) {
return Promise.resolve();
} else {
return Promise.reject(
new Error(t("jobs.labels.dms.invoicedatefuture"))
);
}
} }
return Promise.reject(
new Error(t("jobs.labels.dms.invoicedatefuture"))
);
}, },
}), }),
]} ]}

View File

@@ -223,6 +223,7 @@
"reexport": "Bill marked for re-export." "reexport": "Bill marked for re-export."
}, },
"validation": { "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}}).", "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. ", "manualinhouse": "Manual posting to the in house vendor is restricted. ",
"unique_invoice_number": "This invoice number has already been entered for this vendor." "unique_invoice_number": "This invoice number has already been entered for this vendor."
@@ -262,6 +263,7 @@
"bill_local_tax_rate": "Bill - Provincial/State Tax Rate %", "bill_local_tax_rate": "Bill - Provincial/State Tax Rate %",
"bill_state_tax_rate": "Bill - Provincial/State Tax Rate %", "bill_state_tax_rate": "Bill - Provincial/State Tax Rate %",
"city": "City", "city": "City",
"closingperiod": "Closing Period",
"country": "Country", "country": "Country",
"dailybodytarget": "Scoreboard - Daily Body Target", "dailybodytarget": "Scoreboard - Daily Body Target",
"dailypainttarget": "Scoreboard - Daily Paint Target", "dailypainttarget": "Scoreboard - Daily Paint Target",
@@ -1688,6 +1690,7 @@
"checklists": "Checklists", "checklists": "Checklists",
"closeconfirm": "Are you sure you want to close this job? This cannot be easily undone.", "closeconfirm": "Are you sure you want to close this job? This cannot be easily undone.",
"closejob": "Close Job {{ro_number}}", "closejob": "Close Job {{ro_number}}",
"closingperiod": "This Invoice Date is outside of the Closing Period.",
"contracts": "CC Contracts", "contracts": "CC Contracts",
"convertedtolabor": "Lines Converted to Labor", "convertedtolabor": "Lines Converted to Labor",
"cost": "Cost", "cost": "Cost",

View File

@@ -223,6 +223,7 @@
"reexport": "" "reexport": ""
}, },
"validation": { "validation": {
"closingperiod": "",
"inventoryquantity": "", "inventoryquantity": "",
"manualinhouse": "", "manualinhouse": "",
"unique_invoice_number": "" "unique_invoice_number": ""
@@ -262,6 +263,7 @@
"bill_local_tax_rate": "", "bill_local_tax_rate": "",
"bill_state_tax_rate": "", "bill_state_tax_rate": "",
"city": "", "city": "",
"closingperiod": "",
"country": "", "country": "",
"dailybodytarget": "", "dailybodytarget": "",
"dailypainttarget": "", "dailypainttarget": "",
@@ -1688,6 +1690,7 @@
"checklists": "", "checklists": "",
"closeconfirm": "", "closeconfirm": "",
"closejob": "", "closejob": "",
"closingperiod": "",
"contracts": "", "contracts": "",
"convertedtolabor": "", "convertedtolabor": "",
"cost": "", "cost": "",

View File

@@ -223,6 +223,7 @@
"reexport": "" "reexport": ""
}, },
"validation": { "validation": {
"closingperiod": "",
"inventoryquantity": "", "inventoryquantity": "",
"manualinhouse": "", "manualinhouse": "",
"unique_invoice_number": "" "unique_invoice_number": ""
@@ -262,6 +263,7 @@
"bill_local_tax_rate": "", "bill_local_tax_rate": "",
"bill_state_tax_rate": "", "bill_state_tax_rate": "",
"city": "", "city": "",
"closingperiod": "",
"country": "", "country": "",
"dailybodytarget": "", "dailybodytarget": "",
"dailypainttarget": "", "dailypainttarget": "",
@@ -1688,6 +1690,7 @@
"checklists": "", "checklists": "",
"closeconfirm": "", "closeconfirm": "",
"closejob": "", "closejob": "",
"closingperiod": "",
"contracts": "", "contracts": "",
"convertedtolabor": "", "convertedtolabor": "",
"cost": "", "cost": "",