WIP on invoice enter modal.
This commit is contained in:
@@ -1750,6 +1750,32 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>invoices</name>
|
<name>invoices</name>
|
||||||
<children>
|
<children>
|
||||||
|
<folder_node>
|
||||||
|
<name>actions</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>receive</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
</children>
|
||||||
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
<name>errors</name>
|
<name>errors</name>
|
||||||
<children>
|
<children>
|
||||||
@@ -1795,6 +1821,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>validation</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
@@ -1884,6 +1931,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>total</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>vendor</name>
|
<name>vendor</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -1910,6 +1978,27 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>labels</name>
|
<name>labels</name>
|
||||||
<children>
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>actions</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>new</name>
|
<name>new</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -2182,6 +2271,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>part_qty</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>part_type</name>
|
<name>part_type</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -1,7 +1,18 @@
|
|||||||
import { Modal, Form, Input, Switch, DatePicker, AutoComplete } from "antd";
|
import {
|
||||||
|
AutoComplete,
|
||||||
|
Button,
|
||||||
|
DatePicker,
|
||||||
|
Form,
|
||||||
|
Input,
|
||||||
|
InputNumber,
|
||||||
|
Modal,
|
||||||
|
Switch,
|
||||||
|
Row,
|
||||||
|
Col
|
||||||
|
} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import ResetForm from "../form-items-formatted/reset-form-item.component";
|
import InvoiceEnterModalTableComponent from "./invoice-enter-modal.table.component";
|
||||||
|
|
||||||
export default function InvoiceEnterModalComponent({
|
export default function InvoiceEnterModalComponent({
|
||||||
visible,
|
visible,
|
||||||
@@ -10,12 +21,15 @@ export default function InvoiceEnterModalComponent({
|
|||||||
handleSubmit,
|
handleSubmit,
|
||||||
form,
|
form,
|
||||||
handleRoAutoComplete,
|
handleRoAutoComplete,
|
||||||
|
handleRoSelect,
|
||||||
roAutoCompleteOptions,
|
roAutoCompleteOptions,
|
||||||
handleVendorAutoComplete,
|
handleVendorAutoComplete,
|
||||||
vendorAutoCompleteOptions
|
vendorAutoCompleteOptions,
|
||||||
|
lineData,
|
||||||
|
linesState
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { getFieldDecorator, isFieldsTouched, resetFields } = form;
|
const { getFieldDecorator, resetFields } = form;
|
||||||
|
|
||||||
console.log("invoice", invoice);
|
console.log("invoice", invoice);
|
||||||
return (
|
return (
|
||||||
@@ -33,61 +47,70 @@ export default function InvoiceEnterModalComponent({
|
|||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
>
|
>
|
||||||
{isFieldsTouched() ? <ResetForm resetFields={resetFields} /> : null}
|
|
||||||
<Form onSubmit={handleSubmit} autoComplete={"off"}>
|
<Form onSubmit={handleSubmit} autoComplete={"off"}>
|
||||||
<Form.Item label={t("invoices.fields.ro_number")}>
|
<div style={{ display: "flex" }}>
|
||||||
{getFieldDecorator("jobid", {
|
<Form.Item label={t("invoices.fields.ro_number")}>
|
||||||
rules: [
|
{getFieldDecorator("jobid", {
|
||||||
{
|
rules: [
|
||||||
required: true,
|
{
|
||||||
pattern: new RegExp(
|
required: true,
|
||||||
"[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
|
pattern: new RegExp(
|
||||||
),
|
"[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
|
||||||
message: t("invoices.errors.invalidro")
|
),
|
||||||
}
|
message: t("invoices.errors.invalidro")
|
||||||
]
|
}
|
||||||
})(
|
]
|
||||||
<AutoComplete
|
})(
|
||||||
name="ro_number"
|
<AutoComplete
|
||||||
dataSource={roAutoCompleteOptions}
|
name="ro_number"
|
||||||
onSearch={handleRoAutoComplete}
|
dataSource={roAutoCompleteOptions}
|
||||||
autoFocus
|
onSearch={handleRoAutoComplete}
|
||||||
backfill={true}
|
autoFocus
|
||||||
onBlur={props => {
|
style={{ width: "300px" }}
|
||||||
console.log("props", props);
|
onSelect={handleRoSelect}
|
||||||
}}
|
backfill
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("invoices.fields.vendor")}>
|
<Form.Item label={t("invoices.fields.vendor")}>
|
||||||
{getFieldDecorator("vendorid", {
|
{getFieldDecorator("vendorid", {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
pattern: new RegExp(
|
pattern: new RegExp(
|
||||||
"[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
|
"[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
|
||||||
),
|
),
|
||||||
message: t("invoices.errors.invalidvendor")
|
message: t("invoices.errors.invalidvendor")
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})(
|
})(
|
||||||
<AutoComplete
|
<AutoComplete
|
||||||
name="vendor_id"
|
name="vendor_id"
|
||||||
dataSource={vendorAutoCompleteOptions}
|
dataSource={vendorAutoCompleteOptions}
|
||||||
onSearch={handleVendorAutoComplete}
|
style={{ width: "300px" }}
|
||||||
backfill
|
onSearch={handleVendorAutoComplete}
|
||||||
/>
|
backfill
|
||||||
)}
|
/>
|
||||||
</Form.Item>
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
<Button onClick={() => resetFields()}>
|
||||||
|
{t("general.actions.reset")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
<div style={{ display: "flex" }}>
|
<div style={{ display: "flex" }}>
|
||||||
<Form.Item label={t("invoices.fields.invoice_number")}>
|
<Form.Item label={t("invoices.fields.invoice_number")}>
|
||||||
{getFieldDecorator(
|
{getFieldDecorator("invoice_number", {
|
||||||
"invoice_number",
|
rules: [
|
||||||
{}
|
{ required: true, message: t("general.validation.required") }
|
||||||
)(<Input name="invoice_number" />)}
|
]
|
||||||
|
})(<Input name="invoice_number" />)}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("invoices.fields.date")}>
|
<Form.Item label={t("invoices.fields.date")}>
|
||||||
{getFieldDecorator("date", {})(<DatePicker name="date" />)}
|
{getFieldDecorator("date", {
|
||||||
|
rules: [
|
||||||
|
{ required: true, message: t("general.validation.required") }
|
||||||
|
]
|
||||||
|
})(<DatePicker name="date" />)}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("invoices.fields.is_credit_memo")}>
|
<Form.Item label={t("invoices.fields.is_credit_memo")}>
|
||||||
{getFieldDecorator("is_credit_memo", {
|
{getFieldDecorator("is_credit_memo", {
|
||||||
@@ -95,45 +118,24 @@ export default function InvoiceEnterModalComponent({
|
|||||||
valuePropName: "checked"
|
valuePropName: "checked"
|
||||||
})(<Switch name="is_credit_memo" />)}
|
})(<Switch name="is_credit_memo" />)}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item label={t("invoices.fields.total")}>
|
||||||
|
{getFieldDecorator("total", {
|
||||||
|
rules: [
|
||||||
|
{ required: true, message: t("general.validation.required") }
|
||||||
|
]
|
||||||
|
})(<InputNumber precision={2} min={0} name="total" />)}
|
||||||
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
<Row>
|
||||||
// <Form.Item label={t("joblines.fields.line_desc")}>
|
<Col span={12}>
|
||||||
// {getFieldDecorator("line_desc", {
|
<InvoiceEnterModalTableComponent
|
||||||
// initialValue: jobLine.line_desc
|
lineData={lineData}
|
||||||
// })(<Input name="line_desc" />)}
|
linesState={linesState}
|
||||||
// </Form.Item>
|
/>
|
||||||
// <Form.Item label={t("joblines.fields.oem_partno")}>
|
</Col>
|
||||||
// {getFieldDecorator("oem_partno", {
|
<Col span={12}>Table of added items.</Col>
|
||||||
// initialValue: jobLine.oem_partno
|
</Row>
|
||||||
// })(<Input name="oem_partno" />)}
|
|
||||||
// </Form.Item>
|
|
||||||
// <Form.Item label={t("joblines.fields.part_type")}>
|
|
||||||
// {getFieldDecorator("part_type", {
|
|
||||||
// initialValue: jobLine.part_type
|
|
||||||
// })(<Input name="part_type" />)}
|
|
||||||
// </Form.Item>
|
|
||||||
// <Form.Item label={t("joblines.fields.mod_lbr_ty")}>
|
|
||||||
// {getFieldDecorator("mod_lbr_ty", {
|
|
||||||
// initialValue: jobLine.mod_lbr_ty
|
|
||||||
// })(<Input name="mod_lbr_ty" />)}
|
|
||||||
// </Form.Item>
|
|
||||||
// <Form.Item label={t("joblines.fields.op_code_desc")}>
|
|
||||||
// {getFieldDecorator("op_code_desc", {
|
|
||||||
// initialValue: jobLine.op_code_desc
|
|
||||||
// })(<Input name="op_code_desc" />)}
|
|
||||||
// </Form.Item>
|
|
||||||
// <Form.Item label={t("joblines.fields.mod_lb_hrs")}>
|
|
||||||
// {getFieldDecorator("mod_lb_hrs", {
|
|
||||||
// initialValue: jobLine.mod_lb_hrs
|
|
||||||
// })(<InputNumber name="mod_lb_hrs" />)}
|
|
||||||
// </Form.Item>
|
|
||||||
// <Form.Item label={t("joblines.fields.act_price")}>
|
|
||||||
// {getFieldDecorator("act_price", {
|
|
||||||
// initialValue: jobLine.act_price
|
|
||||||
// })(<InputNumber name="act_price" />)}
|
|
||||||
// </Form.Item>
|
|
||||||
}
|
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
import { Form, notification } from "antd";
|
import { Form, notification } from "antd";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useQuery } from "react-apollo";
|
import { useQuery, useLazyQuery } from "react-apollo";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import {
|
import { SEARCH_RO_AUTOCOMPLETE } from "../../graphql/jobs.queries";
|
||||||
INSERT_NEW_JOB_LINE,
|
import { SEARCH_VENDOR_AUTOCOMPLETE } from "../../graphql/vendors.queries";
|
||||||
UPDATE_JOB_LINE
|
|
||||||
} from "../../graphql/jobs-lines.queries";
|
|
||||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||||
import { selectInvoiceEnterModal } from "../../redux/modals/modals.selectors";
|
import { selectInvoiceEnterModal } from "../../redux/modals/modals.selectors";
|
||||||
import InvoiceEnterModalComponent from "./invoice-enter-modal.component";
|
import InvoiceEnterModalComponent from "./invoice-enter-modal.component";
|
||||||
import { SEARCH_RO_AUTOCOMPLETE } from "../../graphql/jobs.queries";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import { SEARCH_VENDOR_AUTOCOMPLETE } from "../../graphql/vendors.queries";
|
import { GET_JOB_LINES_TO_ENTER_INVOICE } from "../../graphql/jobs-lines.queries";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
invoiceEnterModal: selectInvoiceEnterModal
|
invoiceEnterModal: selectInvoiceEnterModal,
|
||||||
|
bodyshop: selectBodyshop
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
toggleModalVisible: () => dispatch(toggleModalVisible("invoiceEnter"))
|
toggleModalVisible: () => dispatch(toggleModalVisible("invoiceEnter"))
|
||||||
@@ -24,19 +23,20 @@ const mapDispatchToProps = dispatch => ({
|
|||||||
function InvoiceEnterModalContainer({
|
function InvoiceEnterModalContainer({
|
||||||
invoiceEnterModal,
|
invoiceEnterModal,
|
||||||
toggleModalVisible,
|
toggleModalVisible,
|
||||||
form
|
form,
|
||||||
|
bodyshop
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const linesState = useState([]);
|
||||||
const roSearchState = useState("");
|
const roSearchState = useState({ text: "", selectedId: null });
|
||||||
const [roSearch, setRoSearch] = roSearchState;
|
const [roSearch, setRoSearch] = roSearchState;
|
||||||
const handleRoAutoComplete = e => {
|
const handleRoAutoComplete = e => {
|
||||||
setRoSearch(e);
|
setRoSearch({ ...roSearch, text: e });
|
||||||
};
|
};
|
||||||
const { data: RoAutoCompleteData } = useQuery(SEARCH_RO_AUTOCOMPLETE, {
|
const { data: RoAutoCompleteData } = useQuery(SEARCH_RO_AUTOCOMPLETE, {
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
variables: { search: `%${roSearch}%` },
|
variables: { search: `%${roSearch.text}%` },
|
||||||
skip: !roSearch
|
skip: !roSearch.text || roSearch.text.length < 3
|
||||||
});
|
});
|
||||||
|
|
||||||
const vendorSearchState = useState("");
|
const vendorSearchState = useState("");
|
||||||
@@ -49,10 +49,26 @@ function InvoiceEnterModalContainer({
|
|||||||
{
|
{
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
variables: { search: `%${vendorSearch}%` },
|
variables: { search: `%${vendorSearch}%` },
|
||||||
skip: !vendorSearch
|
skip: !vendorSearch || vendorSearch.length < 3
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [
|
||||||
|
loadLines,
|
||||||
|
{ called, loading: lineLoading, data: lineData }
|
||||||
|
] = useLazyQuery(GET_JOB_LINES_TO_ENTER_INVOICE, {
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
variables: { id: roSearch.selectedId }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (roSearch.selectedId) {
|
||||||
|
if (!called) loadLines();
|
||||||
|
console.log("lineData", lineData);
|
||||||
|
}
|
||||||
|
const handleRoSelect = v => {
|
||||||
|
setRoSearch({ ...roSearch, selectedId: v });
|
||||||
|
};
|
||||||
|
|
||||||
const handleSubmit = e => {
|
const handleSubmit = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
form.validateFieldsAndScroll((err, values) => {
|
form.validateFieldsAndScroll((err, values) => {
|
||||||
@@ -126,6 +142,7 @@ function InvoiceEnterModalContainer({
|
|||||||
handleCancel={handleCancel}
|
handleCancel={handleCancel}
|
||||||
form={form}
|
form={form}
|
||||||
handleRoAutoComplete={handleRoAutoComplete}
|
handleRoAutoComplete={handleRoAutoComplete}
|
||||||
|
handleRoSelect={handleRoSelect}
|
||||||
roAutoCompleteOptions={
|
roAutoCompleteOptions={
|
||||||
RoAutoCompleteData
|
RoAutoCompleteData
|
||||||
? RoAutoCompleteData.jobs.reduce((acc, value) => {
|
? RoAutoCompleteData.jobs.reduce((acc, value) => {
|
||||||
@@ -152,6 +169,8 @@ function InvoiceEnterModalContainer({
|
|||||||
}, [])
|
}, [])
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
linesState={linesState}
|
||||||
|
lineData={lineData ? lineData.joblines : null}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { Table, Button, Icon } from "antd";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { alphaSort } from "../../utils/sorters";
|
||||||
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
|
|
||||||
|
export default function InvoiceEnterModalTableComponent({
|
||||||
|
lineData,
|
||||||
|
linesState
|
||||||
|
}) {
|
||||||
|
const [selectedLines, setSelectedLines] = linesState;
|
||||||
|
const [state, setState] = useState({
|
||||||
|
sortedInfo: {}
|
||||||
|
});
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.line_desc"),
|
||||||
|
dataIndex: "line_desc",
|
||||||
|
key: "line_desc",
|
||||||
|
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.oem_partno"),
|
||||||
|
dataIndex: "oem_partno",
|
||||||
|
key: "oem_partno",
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
a.oem_partno ? a.oem_partno : a.op_code_desc,
|
||||||
|
b.oem_partno ? b.oem_partno : b.op_code_desc
|
||||||
|
),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "oem_partno" && state.sortedInfo.order,
|
||||||
|
ellipsis: true,
|
||||||
|
render: (text, record) => (
|
||||||
|
<span>
|
||||||
|
{record.oem_partno ? record.oem_partno : record.op_code_desc}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.part_type"),
|
||||||
|
dataIndex: "part_type",
|
||||||
|
key: "part_type",
|
||||||
|
sorter: (a, b) => alphaSort(a.part_type, b.part_type),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "part_type" && state.sortedInfo.order
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.db_price"),
|
||||||
|
dataIndex: "db_price",
|
||||||
|
key: "db_price",
|
||||||
|
sorter: (a, b) => a.db_price - b.db_price,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "db_price" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => (
|
||||||
|
<CurrencyFormatter>{record.db_price}</CurrencyFormatter>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.act_price"),
|
||||||
|
dataIndex: "act_price",
|
||||||
|
key: "act_price",
|
||||||
|
sorter: (a, b) => a.act_price - b.act_price,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "act_price" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => (
|
||||||
|
<CurrencyFormatter>{record.act_price}</CurrencyFormatter>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.part_qty"),
|
||||||
|
dataIndex: "part_qty",
|
||||||
|
key: "part_qty",
|
||||||
|
sorter: (a, b) => a.part_qty - b.part_qty,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "part_qty" && state.sortedInfo.order
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.mod_lb_hrs"),
|
||||||
|
dataIndex: "mod_lb_hrs",
|
||||||
|
key: "mod_lb_hrs",
|
||||||
|
sorter: (a, b) => a.mod_lb_hrs - b.mod_lb_hrs,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "mod_lb_hrs" && state.sortedInfo.order
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("invoices.labels.actions"),
|
||||||
|
dataIndex: "actions",
|
||||||
|
key: "actions",
|
||||||
|
render: (text, record) => (
|
||||||
|
<div>
|
||||||
|
<Button>
|
||||||
|
<Icon type="select" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
|
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||||
|
};
|
||||||
|
|
||||||
|
const formItemLayout = {
|
||||||
|
labelCol: {
|
||||||
|
xs: { span: 12 },
|
||||||
|
sm: { span: 5 }
|
||||||
|
},
|
||||||
|
wrapperCol: {
|
||||||
|
xs: { span: 24 },
|
||||||
|
sm: { span: 12 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
{...formItemLayout}
|
||||||
|
size="small"
|
||||||
|
pagination={{ position: "top", defaultPageSize: 25 }}
|
||||||
|
columns={columns.map(item => ({ ...item }))}
|
||||||
|
rowKey="id"
|
||||||
|
dataSource={lineData}
|
||||||
|
onChange={handleTableChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -71,3 +71,29 @@ export const UPDATE_JOB_LINE = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const GET_JOB_LINES_TO_ENTER_INVOICE = gql`
|
||||||
|
query GET_JOB_LINES_TO_ENTER_INVOICE($id: uuid!) {
|
||||||
|
joblines(
|
||||||
|
where: {
|
||||||
|
jobid: { _eq: $id }
|
||||||
|
oem_partno: { _neq: "" }
|
||||||
|
act_price: { _gt: "0" }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
line_desc
|
||||||
|
part_type
|
||||||
|
oem_partno
|
||||||
|
db_price
|
||||||
|
act_price
|
||||||
|
part_qty
|
||||||
|
mod_lbr_ty
|
||||||
|
db_hrs
|
||||||
|
mod_lb_hrs
|
||||||
|
lbr_op
|
||||||
|
lbr_amt
|
||||||
|
op_code_desc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|||||||
@@ -145,18 +145,24 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"invoices": {
|
"invoices": {
|
||||||
|
"actions": {
|
||||||
|
"receive": "Receive Part"
|
||||||
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"invalidro": "Not a valid RO.",
|
"invalidro": "Not a valid RO.",
|
||||||
"invalidvendor": "Not a valid vendor."
|
"invalidvendor": "Not a valid vendor.",
|
||||||
|
"validation": "Please ensure all fields are entered correctly. "
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"date": "Invoice Date",
|
"date": "Invoice Date",
|
||||||
"invoice_number": "Invoice Number",
|
"invoice_number": "Invoice Number",
|
||||||
"is_credit_memo": "Credit Memo?",
|
"is_credit_memo": "Credit Memo?",
|
||||||
"ro_number": "RO Number",
|
"ro_number": "RO Number",
|
||||||
|
"total": "Invoice Total",
|
||||||
"vendor": "Vendor"
|
"vendor": "Vendor"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"actions": "Actions",
|
||||||
"new": "New Invoice"
|
"new": "New Invoice"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -177,6 +183,7 @@
|
|||||||
"mod_lbr_ty": "Labor Type",
|
"mod_lbr_ty": "Labor Type",
|
||||||
"oem_partno": "OEM Part #",
|
"oem_partno": "OEM Part #",
|
||||||
"op_code_desc": "Operation Code Description",
|
"op_code_desc": "Operation Code Description",
|
||||||
|
"part_qty": "Quantity",
|
||||||
"part_type": "Part Type",
|
"part_type": "Part Type",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"unq_seq": "Seq #"
|
"unq_seq": "Seq #"
|
||||||
|
|||||||
@@ -145,18 +145,24 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"invoices": {
|
"invoices": {
|
||||||
|
"actions": {
|
||||||
|
"receive": ""
|
||||||
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"invalidro": "",
|
"invalidro": "",
|
||||||
"invalidvendor": ""
|
"invalidvendor": "",
|
||||||
|
"validation": ""
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"date": "",
|
"date": "",
|
||||||
"invoice_number": "",
|
"invoice_number": "",
|
||||||
"is_credit_memo": "",
|
"is_credit_memo": "",
|
||||||
"ro_number": "",
|
"ro_number": "",
|
||||||
|
"total": "",
|
||||||
"vendor": ""
|
"vendor": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"actions": "",
|
||||||
"new": ""
|
"new": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -177,6 +183,7 @@
|
|||||||
"mod_lbr_ty": "Tipo de trabajo",
|
"mod_lbr_ty": "Tipo de trabajo",
|
||||||
"oem_partno": "OEM parte #",
|
"oem_partno": "OEM parte #",
|
||||||
"op_code_desc": "",
|
"op_code_desc": "",
|
||||||
|
"part_qty": "",
|
||||||
"part_type": "Tipo de parte",
|
"part_type": "Tipo de parte",
|
||||||
"status": "Estado",
|
"status": "Estado",
|
||||||
"unq_seq": "Seq #"
|
"unq_seq": "Seq #"
|
||||||
|
|||||||
@@ -145,18 +145,24 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"invoices": {
|
"invoices": {
|
||||||
|
"actions": {
|
||||||
|
"receive": ""
|
||||||
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"invalidro": "",
|
"invalidro": "",
|
||||||
"invalidvendor": ""
|
"invalidvendor": "",
|
||||||
|
"validation": ""
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"date": "",
|
"date": "",
|
||||||
"invoice_number": "",
|
"invoice_number": "",
|
||||||
"is_credit_memo": "",
|
"is_credit_memo": "",
|
||||||
"ro_number": "",
|
"ro_number": "",
|
||||||
|
"total": "",
|
||||||
"vendor": ""
|
"vendor": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"actions": "",
|
||||||
"new": ""
|
"new": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -177,6 +183,7 @@
|
|||||||
"mod_lbr_ty": "Type de travail",
|
"mod_lbr_ty": "Type de travail",
|
||||||
"oem_partno": "Pièce OEM #",
|
"oem_partno": "Pièce OEM #",
|
||||||
"op_code_desc": "",
|
"op_code_desc": "",
|
||||||
|
"part_qty": "",
|
||||||
"part_type": "Type de pièce",
|
"part_type": "Type de pièce",
|
||||||
"status": "Statut",
|
"status": "Statut",
|
||||||
"unq_seq": "Seq #"
|
"unq_seq": "Seq #"
|
||||||
|
|||||||
Reference in New Issue
Block a user