MAJOR CHANGE: Renamed invoices to bills BOD-410
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -4,18 +4,15 @@ import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import InvoiceExportButton from "../invoice-export-button/invoice-export-button.component";
|
||||
import InvoiceExportAllButton from "../invoice-export-all-button/invoice-export-all-button.component";
|
||||
import PayableExportButton from "../payable-export-button/payable-export-button.component";
|
||||
import PayableExportAll from "../payable-export-all-button/payable-export-all-button.component";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import queryString from "query-string";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
|
||||
export default function AccountingPayablesTableComponent({
|
||||
loading,
|
||||
invoices,
|
||||
}) {
|
||||
export default function AccountingPayablesTableComponent({ loading, bills }) {
|
||||
const { t } = useTranslation();
|
||||
const [selectedInvoices, setSelectedInvoices] = useState([]);
|
||||
const [selectedBills, setSelectedBills] = useState([]);
|
||||
const [transInProgress, setTransInProgress] = useState(false);
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
@@ -28,7 +25,7 @@ export default function AccountingPayablesTableComponent({
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("invoices.fields.vendorname"),
|
||||
title: t("bills.fields.vendorname"),
|
||||
dataIndex: "vendorname",
|
||||
key: "vendorname",
|
||||
sorter: (a, b) => alphaSort(a.vendor.name, b.vendor.name),
|
||||
@@ -46,7 +43,7 @@ export default function AccountingPayablesTableComponent({
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("invoices.fields.invoice_number"),
|
||||
title: t("bills.fields.invoice_number"),
|
||||
dataIndex: "invoice_number",
|
||||
key: "invoice_number",
|
||||
sorter: (a, b) => alphaSort(a.invoice_number, b.invoice_number),
|
||||
@@ -56,9 +53,9 @@ export default function AccountingPayablesTableComponent({
|
||||
render: (text, record) => (
|
||||
<Link
|
||||
to={{
|
||||
pathname: `/manage/invoices`,
|
||||
pathname: `/manage/bills`,
|
||||
search: queryString.stringify({
|
||||
invoiceid: record.id,
|
||||
billid: record.id,
|
||||
vendorid: record.vendor.id,
|
||||
}),
|
||||
}}
|
||||
@@ -79,7 +76,7 @@ export default function AccountingPayablesTableComponent({
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("invoices.fields.date"),
|
||||
title: t("bills.fields.date"),
|
||||
dataIndex: "date",
|
||||
key: "date",
|
||||
|
||||
@@ -89,7 +86,7 @@ export default function AccountingPayablesTableComponent({
|
||||
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
|
||||
},
|
||||
{
|
||||
title: t("invoices.fields.total"),
|
||||
title: t("bills.fields.total"),
|
||||
dataIndex: "total",
|
||||
key: "total",
|
||||
|
||||
@@ -101,7 +98,7 @@ export default function AccountingPayablesTableComponent({
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("invoices.fields.is_credit_memo"),
|
||||
title: t("bills.fields.is_credit_memo"),
|
||||
dataIndex: "is_credit_memo",
|
||||
key: "is_credit_memo",
|
||||
sorter: (a, b) => a.is_credit_memo - b.is_credit_memo,
|
||||
@@ -120,8 +117,8 @@ export default function AccountingPayablesTableComponent({
|
||||
|
||||
render: (text, record) => (
|
||||
<div>
|
||||
<InvoiceExportButton
|
||||
invoiceId={record.id}
|
||||
<PayableExportButton
|
||||
billId={record.id}
|
||||
disabled={transInProgress || !!record.exported}
|
||||
loadingCallback={setTransInProgress}
|
||||
/>
|
||||
@@ -136,7 +133,7 @@ export default function AccountingPayablesTableComponent({
|
||||
};
|
||||
|
||||
const dataSource = state.search
|
||||
? invoices.filter(
|
||||
? bills.filter(
|
||||
(v) =>
|
||||
(v.vendor.name || "")
|
||||
.toLowerCase()
|
||||
@@ -145,7 +142,7 @@ export default function AccountingPayablesTableComponent({
|
||||
.toLowerCase()
|
||||
.includes(state.search.toLowerCase())
|
||||
)
|
||||
: invoices;
|
||||
: bills;
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -160,11 +157,11 @@ export default function AccountingPayablesTableComponent({
|
||||
placeholder={t("general.labels.search")}
|
||||
allowClear
|
||||
/>
|
||||
<InvoiceExportAllButton
|
||||
invoiceIds={selectedInvoices}
|
||||
disabled={transInProgress || selectedInvoices.length === 0}
|
||||
<PayableExportAll
|
||||
billIds={selectedBills}
|
||||
disabled={transInProgress || selectedBills.length === 0}
|
||||
loadingCallback={setTransInProgress}
|
||||
completedCallback={setSelectedInvoices}
|
||||
completedCallback={setSelectedBills}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -177,14 +174,14 @@ export default function AccountingPayablesTableComponent({
|
||||
onChange={handleTableChange}
|
||||
rowSelection={{
|
||||
onSelectAll: (selected, selectedRows) =>
|
||||
setSelectedInvoices(selectedRows.map((i) => i.id)),
|
||||
setSelectedBills(selectedRows.map((i) => i.id)),
|
||||
onSelect: (record, selected, selectedRows, nativeEvent) => {
|
||||
setSelectedInvoices(selectedRows.map((i) => i.id));
|
||||
setSelectedBills(selectedRows.map((i) => i.id));
|
||||
},
|
||||
getCheckboxProps: (record) => ({
|
||||
disabled: record.exported,
|
||||
}),
|
||||
selectedRowKeys: selectedInvoices,
|
||||
selectedRowKeys: selectedBills,
|
||||
type: "checkbox",
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,57 +1,47 @@
|
||||
import { useMutation, useQuery } from "@apollo/react-hooks";
|
||||
import { Form, Button } from "antd";
|
||||
import { Button, Form } 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 { useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {
|
||||
QUERY_INVOICE_BY_PK,
|
||||
UPDATE_INVOICE,
|
||||
} from "../../graphql/invoices.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { UPDATE_BILL_LINE } from "../../graphql/bill-lines.queries";
|
||||
import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import InvoiceFormContainer from "../invoice-form/invoice-form.container";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
import { UPDATE_INVOICE_LINE } from "../../graphql/invoice-lines.queries";
|
||||
import BillFormContainer from "../bill-form/bill-form.container";
|
||||
import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export function InvoiceDetailEditContainer({ bodyshop }) {
|
||||
export default function BillDetailEditcontainer() {
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const [updateLoading, setUpdateLoading] = useState(false);
|
||||
const [updateInvoice] = useMutation(UPDATE_INVOICE);
|
||||
const [updateInvoiceLine] = useMutation(UPDATE_INVOICE_LINE);
|
||||
const [update_bill] = useMutation(UPDATE_BILL);
|
||||
const [updateBillLine] = useMutation(UPDATE_BILL_LINE);
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_INVOICE_BY_PK, {
|
||||
variables: { invoiceid: search.invoiceid },
|
||||
skip: !!!search.invoiceid,
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_BILL_BY_PK, {
|
||||
variables: { billid: search.billid },
|
||||
skip: !!!search.billid,
|
||||
});
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setUpdateLoading(true);
|
||||
const { invoicelines, upload, ...invoice } = values;
|
||||
const { billlines, upload, ...bill } = values;
|
||||
const updates = [];
|
||||
updates.push(
|
||||
updateInvoice({
|
||||
variables: { invoiceId: search.invoiceid, invoice: invoice },
|
||||
update_bill({
|
||||
variables: { billId: search.billid, bill: bill },
|
||||
})
|
||||
);
|
||||
|
||||
invoicelines.forEach((il) => {
|
||||
billlines.forEach((il) => {
|
||||
delete il.__typename;
|
||||
updates.push(
|
||||
updateInvoiceLine({
|
||||
updateBillLine({
|
||||
variables: {
|
||||
invoicelineId: il.id,
|
||||
invoiceLine: {
|
||||
billLineId: il.id,
|
||||
billLine: {
|
||||
...il,
|
||||
joblineid: il.joblineid === "noline" ? null : il.joblineid,
|
||||
},
|
||||
@@ -65,14 +55,13 @@ export function InvoiceDetailEditContainer({ bodyshop }) {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (search.invoiceid) {
|
||||
if (search.billid) {
|
||||
form.resetFields();
|
||||
}
|
||||
}, [form, search.invoiceid]);
|
||||
}, [form, search.billid]);
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (!!!search.invoiceid)
|
||||
return <div>{t("invoices.labels.noneselected")}</div>;
|
||||
if (!!!search.billid) return <div>{t("bills.labels.noneselected")}</div>;
|
||||
return (
|
||||
<LoadingSkeleton loading={loading}>
|
||||
<Form
|
||||
@@ -81,9 +70,9 @@ export function InvoiceDetailEditContainer({ bodyshop }) {
|
||||
initialValues={
|
||||
data
|
||||
? {
|
||||
...data.invoices_by_pk,
|
||||
...data.bills_by_pk,
|
||||
|
||||
invoicelines: data.invoices_by_pk.invoicelines.map((i) => {
|
||||
billlines: data.bills_by_pk.billlines.map((i) => {
|
||||
return {
|
||||
...i,
|
||||
joblineid: !!i.joblineid ? i.joblineid : "noline",
|
||||
@@ -100,9 +89,7 @@ export function InvoiceDetailEditContainer({ bodyshop }) {
|
||||
},
|
||||
};
|
||||
}),
|
||||
date: data.invoices_by_pk
|
||||
? moment(data.invoices_by_pk.date)
|
||||
: null,
|
||||
date: data.bills_by_pk ? moment(data.bills_by_pk.date) : null,
|
||||
}
|
||||
: {}
|
||||
}
|
||||
@@ -110,15 +97,14 @@ export function InvoiceDetailEditContainer({ bodyshop }) {
|
||||
<Button htmlType="submit" loading={updateLoading} type="primary">
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
<InvoiceFormContainer form={form} invoiceEdit />
|
||||
<BillFormContainer form={form} billEdit />
|
||||
<JobDocumentsGallery
|
||||
jobId={data ? data.invoices_by_pk.jobid : null}
|
||||
invoiceId={search.invoiceid}
|
||||
documentsList={data ? data.invoices_by_pk.documents : []}
|
||||
invoicesCallback={refetch}
|
||||
jobId={data ? data.bills_by_pk.jobid : null}
|
||||
billId={search.billid}
|
||||
documentsList={data ? data.bills_by_pk.documents : []}
|
||||
billsCallback={refetch}
|
||||
/>
|
||||
</Form>
|
||||
</LoadingSkeleton>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, null)(InvoiceDetailEditContainer);
|
||||
@@ -4,28 +4,28 @@ import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { INSERT_NEW_INVOICE } from "../../graphql/invoices.queries";
|
||||
import { INSERT_NEW_BILL } from "../../graphql/bills.queries";
|
||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||
import { selectInvoiceEnterModal } from "../../redux/modals/modals.selectors";
|
||||
import { selectBillEnterModal } from "../../redux/modals/modals.selectors";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import { handleUpload } from "../documents-upload/documents-upload.utility";
|
||||
import InvoiceFormContainer from "../invoice-form/invoice-form.container";
|
||||
import BillFormContainer from "../bill-form/bill-form.container";
|
||||
import { UPDATE_JOB_LINE_STATUS } from "../../graphql/jobs-lines.queries";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
invoiceEnterModal: selectInvoiceEnterModal,
|
||||
billEnterModal: selectBillEnterModal,
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
toggleModalVisible: () => dispatch(toggleModalVisible("invoiceEnter")),
|
||||
toggleModalVisible: () => dispatch(toggleModalVisible("billEnter")),
|
||||
});
|
||||
|
||||
function InvoiceEnterModalContainer({
|
||||
invoiceEnterModal,
|
||||
function BillEnterModalContainer({
|
||||
billEnterModal,
|
||||
toggleModalVisible,
|
||||
bodyshop,
|
||||
currentUser,
|
||||
@@ -33,21 +33,21 @@ function InvoiceEnterModalContainer({
|
||||
const [form] = Form.useForm();
|
||||
const { t } = useTranslation();
|
||||
const [enterAgain, setEnterAgain] = useState(false);
|
||||
const [insertInvoice] = useMutation(INSERT_NEW_INVOICE);
|
||||
const [insertBill] = useMutation(INSERT_NEW_BILL);
|
||||
const [updateJobLines] = useMutation(UPDATE_JOB_LINE_STATUS);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleFinish = (values) => {
|
||||
setLoading(true);
|
||||
const { upload, location, ...remainingValues } = values;
|
||||
insertInvoice({
|
||||
insertBill({
|
||||
variables: {
|
||||
invoice: [
|
||||
bill: [
|
||||
Object.assign({}, remainingValues, {
|
||||
invoicelines: {
|
||||
billlines: {
|
||||
data:
|
||||
remainingValues.invoicelines &&
|
||||
remainingValues.invoicelines.map((i) => {
|
||||
remainingValues.billlines &&
|
||||
remainingValues.billlines.map((i) => {
|
||||
return {
|
||||
...i,
|
||||
joblineid: i.joblineid === "noline" ? null : i.joblineid,
|
||||
@@ -59,11 +59,11 @@ function InvoiceEnterModalContainer({
|
||||
},
|
||||
})
|
||||
.then((r) => {
|
||||
const invoiceId = r.data.insert_invoices.returning[0].id;
|
||||
const billId = r.data.insert_bills.returning[0].id;
|
||||
|
||||
updateJobLines({
|
||||
variables: {
|
||||
ids: remainingValues.invoicelines
|
||||
ids: remainingValues.billlines
|
||||
.filter((il) => il.joblineid !== "noline")
|
||||
.map((li) => li.joblineid),
|
||||
status: bodyshop.md_order_statuses.default_received || "Received*",
|
||||
@@ -80,7 +80,7 @@ function InvoiceEnterModalContainer({
|
||||
bodyshop: bodyshop,
|
||||
uploaded_by: currentUser.email,
|
||||
jobId: values.jobid,
|
||||
invoiceId: invoiceId,
|
||||
billId: billId,
|
||||
tagsArray: null,
|
||||
callback: null,
|
||||
}
|
||||
@@ -90,14 +90,13 @@ function InvoiceEnterModalContainer({
|
||||
///////////////////////////
|
||||
setLoading(false);
|
||||
notification["success"]({
|
||||
message: t("invoices.successes.created"),
|
||||
message: t("bills.successes.created"),
|
||||
});
|
||||
if (invoiceEnterModal.actions.refetch)
|
||||
invoiceEnterModal.actions.refetch();
|
||||
if (billEnterModal.actions.refetch) billEnterModal.actions.refetch();
|
||||
|
||||
if (enterAgain) {
|
||||
form.resetFields();
|
||||
form.setFieldsValue({ invoicelines: [] });
|
||||
form.setFieldsValue({ billlines: [] });
|
||||
} else {
|
||||
toggleModalVisible();
|
||||
}
|
||||
@@ -108,7 +107,7 @@ function InvoiceEnterModalContainer({
|
||||
setLoading(false);
|
||||
setEnterAgain(false);
|
||||
notification["error"]({
|
||||
message: t("invoices.errors.creating", {
|
||||
message: t("bills.errors.creating", {
|
||||
message: JSON.stringify(error),
|
||||
}),
|
||||
});
|
||||
@@ -124,14 +123,14 @@ function InvoiceEnterModalContainer({
|
||||
}, [enterAgain, form]);
|
||||
|
||||
useEffect(() => {
|
||||
if (invoiceEnterModal.visible) form.resetFields();
|
||||
}, [invoiceEnterModal.visible, form]);
|
||||
if (billEnterModal.visible) form.resetFields();
|
||||
}, [billEnterModal.visible, form]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={t("invoices.labels.new")}
|
||||
title={t("bills.labels.new")}
|
||||
width={"90%"}
|
||||
visible={invoiceEnterModal.visible}
|
||||
visible={billEnterModal.visible}
|
||||
okText={t("general.actions.save")}
|
||||
onOk={() => form.submit()}
|
||||
onCancel={handleCancel}
|
||||
@@ -142,7 +141,7 @@ function InvoiceEnterModalContainer({
|
||||
<Button loading={loading} onClick={() => form.submit()}>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
{invoiceEnterModal.context && invoiceEnterModal.context.id ? null : (
|
||||
{billEnterModal.context && billEnterModal.context.id ? null : (
|
||||
<Button
|
||||
type="primary"
|
||||
loading={loading}
|
||||
@@ -166,26 +165,25 @@ function InvoiceEnterModalContainer({
|
||||
setEnterAgain(false);
|
||||
}}
|
||||
initialValues={{
|
||||
...invoiceEnterModal.context.invoice,
|
||||
...billEnterModal.context.bill,
|
||||
jobid:
|
||||
(invoiceEnterModal.context.job &&
|
||||
invoiceEnterModal.context.job.id) ||
|
||||
(billEnterModal.context.job && billEnterModal.context.job.id) ||
|
||||
null,
|
||||
federal_tax_rate:
|
||||
(bodyshop.invoice_tax_rates &&
|
||||
bodyshop.invoice_tax_rates.federal_tax_rate) ||
|
||||
(bodyshop.bill_tax_rates &&
|
||||
bodyshop.bill_tax_rates.federal_tax_rate) ||
|
||||
0,
|
||||
state_tax_rate:
|
||||
(bodyshop.invoice_tax_rates &&
|
||||
bodyshop.invoice_tax_rates.state_tax_rate) ||
|
||||
(bodyshop.bill_tax_rates &&
|
||||
bodyshop.bill_tax_rates.state_tax_rate) ||
|
||||
0,
|
||||
local_tax_rate:
|
||||
(bodyshop.invoice_tax_rates &&
|
||||
bodyshop.invoice_tax_rates.local_tax_rate) ||
|
||||
(bodyshop.bill_tax_rates &&
|
||||
bodyshop.bill_tax_rates.local_tax_rate) ||
|
||||
0,
|
||||
}}
|
||||
>
|
||||
<InvoiceFormContainer form={form} />
|
||||
<BillFormContainer form={form} />
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
@@ -194,4 +192,4 @@ function InvoiceEnterModalContainer({
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(InvoiceEnterModalContainer);
|
||||
)(BillEnterModalContainer);
|
||||
@@ -20,25 +20,21 @@ import CurrencyInput from "../form-items-formatted/currency-form-item.component"
|
||||
import JobSearchSelect from "../job-search-select/job-search-select.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
||||
import InvoiceFormLines from "./invoice-form.lines.component";
|
||||
import "./invoice-form.styles.scss";
|
||||
import { CalculateInvoiceTotal } from "./invoice-form.totals.utility";
|
||||
import BillFormLines from "./bill-form.lines.component";
|
||||
import { CalculateBillTotal } from "./bill-form.totals.utility";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({});
|
||||
|
||||
export function InvoiceFormComponent({
|
||||
export function BillFormComponent({
|
||||
bodyshop,
|
||||
form,
|
||||
vendorAutoCompleteOptions,
|
||||
lineData,
|
||||
responsibilityCenters,
|
||||
loadLines,
|
||||
invoiceEdit,
|
||||
billEdit,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -69,7 +65,7 @@ export function InvoiceFormComponent({
|
||||
<LayoutFormRow>
|
||||
<Form.Item
|
||||
name="jobid"
|
||||
label={t("invoices.fields.ro_number")}
|
||||
label={t("bills.fields.ro_number")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
@@ -78,7 +74,7 @@ export function InvoiceFormComponent({
|
||||
]}
|
||||
>
|
||||
<JobSearchSelect
|
||||
disabled={invoiceEdit}
|
||||
disabled={billEdit}
|
||||
onBlur={() => {
|
||||
if (form.getFieldValue("jobid") !== null) {
|
||||
loadLines({ variables: { id: form.getFieldValue("jobid") } });
|
||||
@@ -87,9 +83,9 @@ export function InvoiceFormComponent({
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("invoices.fields.vendor")}
|
||||
label={t("bills.fields.vendor")}
|
||||
name="vendorid"
|
||||
style={{ display: invoiceEdit ? "none" : null }}
|
||||
style={{ display: billEdit ? "none" : null }}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
@@ -106,7 +102,7 @@ export function InvoiceFormComponent({
|
||||
|
||||
<LayoutFormRow>
|
||||
<Form.Item
|
||||
label={t("invoices.fields.invoice_number")}
|
||||
label={t("bills.fields.invoice_number")}
|
||||
name="invoice_number"
|
||||
rules={[
|
||||
{
|
||||
@@ -118,7 +114,7 @@ export function InvoiceFormComponent({
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("invoices.fields.date")}
|
||||
label={t("bills.fields.date")}
|
||||
name="date"
|
||||
rules={[
|
||||
{
|
||||
@@ -130,14 +126,14 @@ export function InvoiceFormComponent({
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("invoices.fields.is_credit_memo")}
|
||||
label={t("bills.fields.is_credit_memo")}
|
||||
name="is_credit_memo"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("invoices.fields.total")}
|
||||
label={t("bills.fields.total")}
|
||||
name="total"
|
||||
rules={[
|
||||
{
|
||||
@@ -149,28 +145,25 @@ export function InvoiceFormComponent({
|
||||
<CurrencyInput min={0} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("invoices.fields.federal_tax_rate")}
|
||||
label={t("bills.fields.federal_tax_rate")}
|
||||
name="federal_tax_rate"
|
||||
>
|
||||
<CurrencyInput min={0} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("invoices.fields.state_tax_rate")}
|
||||
label={t("bills.fields.state_tax_rate")}
|
||||
name="state_tax_rate"
|
||||
>
|
||||
<CurrencyInput min={0} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("invoices.fields.local_tax_rate")}
|
||||
label={t("bills.fields.local_tax_rate")}
|
||||
name="local_tax_rate"
|
||||
>
|
||||
<CurrencyInput min={0} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t("invoices.fields.allpartslocation")}
|
||||
name="location"
|
||||
>
|
||||
<Form.Item label={t("bills.fields.allpartslocation")} name="location">
|
||||
<Select style={{ width: "10rem" }}>
|
||||
{bodyshop.md_parts_locations.map((loc, idx) => (
|
||||
<Select.Option key={idx} value={loc}>
|
||||
@@ -181,9 +174,9 @@ export function InvoiceFormComponent({
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<Typography.Title level={4}>
|
||||
{t("invoices.labels.invoice_lines")}
|
||||
{t("bills.labels.bill_lines")}
|
||||
</Typography.Title>
|
||||
<InvoiceFormLines
|
||||
<BillFormLines
|
||||
lineData={lineData}
|
||||
discount={discount}
|
||||
form={form}
|
||||
@@ -193,7 +186,7 @@ export function InvoiceFormComponent({
|
||||
<Form.Item
|
||||
name="upload"
|
||||
label="Upload"
|
||||
style={{ display: invoiceEdit ? "none" : null }}
|
||||
style={{ display: billEdit ? "none" : null }}
|
||||
valuePropName="fileList"
|
||||
getValueFromEvent={(e) => {
|
||||
if (Array.isArray(e)) {
|
||||
@@ -210,7 +203,7 @@ export function InvoiceFormComponent({
|
||||
<Form.Item shouldUpdate>
|
||||
{() => {
|
||||
const values = form.getFieldsValue([
|
||||
"invoicelines",
|
||||
"billlines",
|
||||
"total",
|
||||
"federal_tax_rate",
|
||||
"state_tax_rate",
|
||||
@@ -219,46 +212,46 @@ export function InvoiceFormComponent({
|
||||
let totals;
|
||||
if (
|
||||
!!values.total &&
|
||||
!!values.invoicelines &&
|
||||
values.invoicelines.length > 0
|
||||
!!values.billlines &&
|
||||
values.billlines.length > 0
|
||||
)
|
||||
totals = CalculateInvoiceTotal(values);
|
||||
totals = CalculateBillTotal(values);
|
||||
if (!!totals)
|
||||
return (
|
||||
<div>
|
||||
<Space>
|
||||
<Statistic
|
||||
title={t("invoices.labels.subtotal")}
|
||||
title={t("bills.labels.subtotal")}
|
||||
value={totals.subtotal.toFormat()}
|
||||
precision={2}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("invoices.labels.federal_tax")}
|
||||
title={t("bills.labels.federal_tax")}
|
||||
value={totals.federalTax.toFormat()}
|
||||
precision={2}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("invoices.labels.state_tax")}
|
||||
title={t("bills.labels.state_tax")}
|
||||
value={totals.stateTax.toFormat()}
|
||||
precision={2}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("invoices.labels.local_tax")}
|
||||
title={t("bills.labels.local_tax")}
|
||||
value={totals.localTax.toFormat()}
|
||||
precision={2}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("invoices.labels.entered_total")}
|
||||
title={t("bills.labels.entered_total")}
|
||||
value={totals.enteredTotal.toFormat()}
|
||||
precision={2}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("invoices.labels.invoice_total")}
|
||||
title={t("bills.labels.bill_total")}
|
||||
value={totals.invoiceTotal.toFormat()}
|
||||
precision={2}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("invoices.labels.discrepancy")}
|
||||
title={t("bills.labels.discrepancy")}
|
||||
valueStyle={{
|
||||
color:
|
||||
totals.discrepancy.getAmount() === 0 ? "green" : "red",
|
||||
@@ -270,7 +263,7 @@ export function InvoiceFormComponent({
|
||||
{form.getFieldValue("is_credit_memo") ? (
|
||||
<AlertComponent
|
||||
type="warning"
|
||||
message={t("invoices.labels.enteringcreditmemo")}
|
||||
message={t("bills.labels.enteringcreditmemo")}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -282,7 +275,4 @@ export function InvoiceFormComponent({
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(InvoiceFormComponent);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(BillFormComponent);
|
||||
@@ -2,27 +2,27 @@ import { useLazyQuery, useQuery } from "@apollo/react-hooks";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { GET_JOB_LINES_TO_ENTER_INVOICE } from "../../graphql/jobs-lines.queries";
|
||||
import { GET_JOB_LINES_TO_ENTER_BILL } from "../../graphql/jobs-lines.queries";
|
||||
import { SEARCH_VENDOR_AUTOCOMPLETE } from "../../graphql/vendors.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import InvoiceFormComponent from "./invoice-form.component";
|
||||
import BillFormComponent from "./bill-form.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export function InvoiceFormContainer({ bodyshop, form, invoiceEdit }) {
|
||||
export function BillFormContainer({ bodyshop, form, billEdit }) {
|
||||
const { data: VendorAutoCompleteData } = useQuery(SEARCH_VENDOR_AUTOCOMPLETE);
|
||||
|
||||
const [loadLines, { data: lineData }] = useLazyQuery(
|
||||
GET_JOB_LINES_TO_ENTER_INVOICE
|
||||
GET_JOB_LINES_TO_ENTER_BILL
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<InvoiceFormComponent
|
||||
<BillFormComponent
|
||||
form={form}
|
||||
invoiceEdit={invoiceEdit}
|
||||
billEdit={billEdit}
|
||||
vendorAutoCompleteOptions={
|
||||
VendorAutoCompleteData && VendorAutoCompleteData.vendors
|
||||
}
|
||||
@@ -33,4 +33,4 @@ export function InvoiceFormContainer({ bodyshop, form, invoiceEdit }) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, null)(InvoiceFormContainer);
|
||||
export default connect(mapStateToProps, null)(BillFormContainer);
|
||||
@@ -11,10 +11,10 @@ import {
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import InvoiceLineSearchSelect from "../invoice-line-search-select/invoice-line-search-select.component";
|
||||
import BillLineSearchSelect from "../bill-line-search-select/bill-line-search-select.component";
|
||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
export default function InvoiceEnterModalLinesComponent({
|
||||
export default function BillEnterModalLinesComponent({
|
||||
lineData,
|
||||
discount,
|
||||
form,
|
||||
@@ -24,7 +24,7 @@ export default function InvoiceEnterModalLinesComponent({
|
||||
const { setFieldsValue, getFieldsValue } = form;
|
||||
|
||||
return (
|
||||
<Form.List name="invoicelines">
|
||||
<Form.List name="billlines">
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
<div className="invoice-form-lines-wrapper">
|
||||
@@ -34,7 +34,7 @@ export default function InvoiceEnterModalLinesComponent({
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<LayoutFormRow style={{ flex: 1 }} grow>
|
||||
<Form.Item
|
||||
label={t("invoicelines.fields.jobline")}
|
||||
label={t("billlines.fields.jobline")}
|
||||
key={`${index}joblinename`}
|
||||
name={[field.name, "joblineid"]}
|
||||
rules={[
|
||||
@@ -44,13 +44,13 @@ export default function InvoiceEnterModalLinesComponent({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InvoiceLineSearchSelect
|
||||
<BillLineSearchSelect
|
||||
options={lineData}
|
||||
onSelect={(value, opt) => {
|
||||
setFieldsValue({
|
||||
invoicelines: getFieldsValue([
|
||||
"invoicelines",
|
||||
]).invoicelines.map((item, idx) => {
|
||||
billlines: getFieldsValue([
|
||||
"billlines",
|
||||
]).billlines.map((item, idx) => {
|
||||
if (idx === index) {
|
||||
return {
|
||||
...item,
|
||||
@@ -72,7 +72,7 @@ export default function InvoiceEnterModalLinesComponent({
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t("invoicelines.fields.line_desc")}
|
||||
label={t("billlines.fields.line_desc")}
|
||||
key={`${index}line_desc`}
|
||||
name={[field.name, "line_desc"]}
|
||||
rules={[
|
||||
@@ -85,7 +85,7 @@ export default function InvoiceEnterModalLinesComponent({
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("invoicelines.fields.quantity")}
|
||||
label={t("billlines.fields.quantity")}
|
||||
key={`${index}quantity`}
|
||||
name={[field.name, "quantity"]}
|
||||
rules={[
|
||||
@@ -98,7 +98,7 @@ export default function InvoiceEnterModalLinesComponent({
|
||||
<InputNumber precision={0} min={0} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("invoicelines.fields.actual")}
|
||||
label={t("billlines.fields.actual")}
|
||||
key={`${index}actual_price`}
|
||||
name={[field.name, "actual_price"]}
|
||||
rules={[
|
||||
@@ -112,9 +112,9 @@ export default function InvoiceEnterModalLinesComponent({
|
||||
min={0}
|
||||
onBlur={(e) => {
|
||||
setFieldsValue({
|
||||
invoicelines: getFieldsValue(
|
||||
"invoicelines"
|
||||
).invoicelines.map((item, idx) => {
|
||||
billlines: getFieldsValue(
|
||||
"billlines"
|
||||
).billlines.map((item, idx) => {
|
||||
if (idx === index) {
|
||||
return {
|
||||
...item,
|
||||
@@ -131,7 +131,7 @@ export default function InvoiceEnterModalLinesComponent({
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("invoicelines.fields.actual_cost")}
|
||||
label={t("billlines.fields.actual_cost")}
|
||||
key={`${index}actual_cost`}
|
||||
name={[field.name, "actual_cost"]}
|
||||
rules={[
|
||||
@@ -145,8 +145,9 @@ export default function InvoiceEnterModalLinesComponent({
|
||||
</Form.Item>
|
||||
<Form.Item shouldUpdate>
|
||||
{() => {
|
||||
const line = getFieldsValue(["invoicelines"])
|
||||
.invoicelines[index];
|
||||
const line = getFieldsValue(["billlines"]).billlines[
|
||||
index
|
||||
];
|
||||
if (!!!line) return null;
|
||||
const lineDiscount = (
|
||||
1 -
|
||||
@@ -161,7 +162,7 @@ export default function InvoiceEnterModalLinesComponent({
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("invoicelines.fields.cost_center")}
|
||||
label={t("billlines.fields.cost_center")}
|
||||
key={`${index}cost_center`}
|
||||
name={[field.name, "cost_center"]}
|
||||
rules={[
|
||||
@@ -180,7 +181,7 @@ export default function InvoiceEnterModalLinesComponent({
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("invoicelines.fields.federal_tax_applicable")}
|
||||
label={t("billlines.fields.federal_tax_applicable")}
|
||||
key={`${index}fedtax`}
|
||||
initialValue={true}
|
||||
valuePropName="checked"
|
||||
@@ -189,7 +190,7 @@ export default function InvoiceEnterModalLinesComponent({
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("invoicelines.fields.state_tax_applicable")}
|
||||
label={t("billlines.fields.state_tax_applicable")}
|
||||
key={`${index}statetax`}
|
||||
valuePropName="checked"
|
||||
name={[field.name, "applicable_taxes", "state"]}
|
||||
@@ -197,7 +198,7 @@ export default function InvoiceEnterModalLinesComponent({
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("invoicelines.fields.local_tax_applicable")}
|
||||
label={t("billlines.fields.local_tax_applicable")}
|
||||
key={`${index}localtax`}
|
||||
valuePropName="checked"
|
||||
name={[field.name, "applicable_taxes", "local"]}
|
||||
@@ -227,7 +228,7 @@ export default function InvoiceEnterModalLinesComponent({
|
||||
}}
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
{t("invoicelines.actions.newline")}
|
||||
{t("billlines.actions.newline")}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
@@ -1,13 +1,12 @@
|
||||
import Dinero from "dinero.js";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
|
||||
export const CalculateInvoiceTotal = (invoice) => {
|
||||
export const CalculateBillTotal = (invoice) => {
|
||||
logImEXEvent("invoice_calculate_total");
|
||||
|
||||
const {
|
||||
total,
|
||||
|
||||
invoicelines,
|
||||
billlines,
|
||||
federal_tax_rate,
|
||||
local_tax_rate,
|
||||
state_tax_rate,
|
||||
@@ -17,8 +16,10 @@ export const CalculateInvoiceTotal = (invoice) => {
|
||||
let federalTax = Dinero({ amount: 0 });
|
||||
let stateTax = Dinero({ amount: 0 });
|
||||
let localTax = Dinero({ amount: 0 });
|
||||
if (!!!invoicelines) return null;
|
||||
invoicelines.forEach((i) => {
|
||||
|
||||
if (!!!billlines) return null;
|
||||
|
||||
billlines.forEach((i) => {
|
||||
if (!!i) {
|
||||
const itemTotal = Dinero({
|
||||
amount: Math.round((i.actual_cost || 0) * 100) || 0,
|
||||
@@ -5,7 +5,7 @@ import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
|
||||
//To be used as a form element only.
|
||||
const { Option } = Select;
|
||||
const InvoiceLineSearchSelect = (
|
||||
const BillLineSearchSelect = (
|
||||
{ value, onChange, options, onBlur, onSelect },
|
||||
ref
|
||||
) => {
|
||||
@@ -33,7 +33,7 @@ const InvoiceLineSearchSelect = (
|
||||
onSelect={onSelect}
|
||||
>
|
||||
<Select.Option key={null} value={"noline"} cost={0} line_desc={""}>
|
||||
{t("invoicelines.labels.other")}
|
||||
{t("billlines.labels.other")}
|
||||
</Select.Option>
|
||||
{options
|
||||
? options.map((item) => (
|
||||
@@ -68,4 +68,4 @@ const InvoiceLineSearchSelect = (
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
export default forwardRef(InvoiceLineSearchSelect);
|
||||
export default forwardRef(BillLineSearchSelect);
|
||||
@@ -14,38 +14,34 @@ import { useLocation } from "react-router-dom";
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setPartsOrderContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
||||
setInvoiceEnterContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "invoiceEnter" })),
|
||||
setBillEnterContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
||||
setReconciliationContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "reconciliation" })),
|
||||
});
|
||||
|
||||
export function InvoicesListTableComponent({
|
||||
export function BillsListTableComponent({
|
||||
job,
|
||||
loading,
|
||||
invoicesQuery,
|
||||
billsQuery,
|
||||
handleOnRowClick,
|
||||
setPartsOrderContext,
|
||||
setInvoiceEnterContext,
|
||||
setBillEnterContext,
|
||||
setReconciliationContext,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [
|
||||
selectedInvoiceLinesByInvoice,
|
||||
setSelectedInvoiceLinesByInvoice,
|
||||
] = useState({});
|
||||
const [selectedBillLinesByBill, setSelectedBillLinesByBill] = useState({});
|
||||
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
});
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const selectedInvoice = search.invoiceid;
|
||||
const selectedBill = search.billid;
|
||||
|
||||
const invoices = invoicesQuery.data ? invoicesQuery.data.invoices : [];
|
||||
const { refetch } = invoicesQuery;
|
||||
const bills = billsQuery.data ? billsQuery.data.bills : [];
|
||||
const { refetch } = billsQuery;
|
||||
const columns = [
|
||||
{
|
||||
title: t("invoices.fields.vendorname"),
|
||||
title: t("bills.fields.vendorname"),
|
||||
dataIndex: "vendorname",
|
||||
key: "vendorname",
|
||||
sorter: (a, b) => alphaSort(a.vendor.name, b.vendor.name),
|
||||
@@ -54,7 +50,7 @@ export function InvoicesListTableComponent({
|
||||
render: (text, record) => <span>{record.vendor.name}</span>,
|
||||
},
|
||||
{
|
||||
title: t("invoices.fields.invoice_number"),
|
||||
title: t("bills.fields.invoice_number"),
|
||||
dataIndex: "invoice_number",
|
||||
key: "invoice_number",
|
||||
sorter: (a, b) => alphaSort(a.invoice_number, b.invoice_number),
|
||||
@@ -63,7 +59,7 @@ export function InvoicesListTableComponent({
|
||||
state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("invoices.fields.date"),
|
||||
title: t("bills.fields.date"),
|
||||
dataIndex: "date",
|
||||
key: "date",
|
||||
sorter: (a, b) => a.date - b.date,
|
||||
@@ -72,7 +68,7 @@ export function InvoicesListTableComponent({
|
||||
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
|
||||
},
|
||||
{
|
||||
title: t("invoices.fields.total"),
|
||||
title: t("bills.fields.total"),
|
||||
dataIndex: "total",
|
||||
key: "total",
|
||||
sorter: (a, b) => a.total - b.total,
|
||||
@@ -83,7 +79,7 @@ export function InvoicesListTableComponent({
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("invoices.fields.is_credit_memo"),
|
||||
title: t("bills.fields.is_credit_memo"),
|
||||
dataIndex: "is_credit_memo",
|
||||
key: "is_credit_memo",
|
||||
sorter: (a, b) => a.is_credit_memo - b.is_credit_memo,
|
||||
@@ -99,9 +95,9 @@ export function InvoicesListTableComponent({
|
||||
render: (text, record) => (
|
||||
<div>
|
||||
<Link
|
||||
to={`/manage/invoices?invoiceid=${record.id}&vendorid=${record.vendorid}`}
|
||||
to={`/manage/bills?billid=${record.id}&vendorid=${record.vendorid}`}
|
||||
>
|
||||
<Button>{t("invoices.actions.edit")}</Button>
|
||||
<Button>{t("bills.actions.edit")}</Button>
|
||||
</Link>
|
||||
</div>
|
||||
),
|
||||
@@ -115,7 +111,7 @@ export function InvoicesListTableComponent({
|
||||
const rowExpander = (record) => {
|
||||
const columns = [
|
||||
{
|
||||
title: t("invoicelines.fields.line_desc"),
|
||||
title: t("billlines.fields.line_desc"),
|
||||
dataIndex: "line_desc",
|
||||
key: "line_desc",
|
||||
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
||||
@@ -123,7 +119,7 @@ export function InvoicesListTableComponent({
|
||||
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("invoicelines.fields.retail"),
|
||||
title: t("billlines.fields.retail"),
|
||||
dataIndex: "actual_price",
|
||||
key: "actual_price",
|
||||
sorter: (a, b) => a.actual_price - b.actual_price,
|
||||
@@ -135,7 +131,7 @@ export function InvoicesListTableComponent({
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("invoicelines.fields.actual_cost"),
|
||||
title: t("billlines.fields.actual_cost"),
|
||||
dataIndex: "actual_cost",
|
||||
key: "actual_cost",
|
||||
sorter: (a, b) => a.actual_cost - b.actual_cost,
|
||||
@@ -147,7 +143,7 @@ export function InvoicesListTableComponent({
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("invoicelines.fields.quantity"),
|
||||
title: t("billlines.fields.quantity"),
|
||||
dataIndex: "quantity",
|
||||
key: "quantity",
|
||||
sorter: (a, b) => a.quantity - b.quantity,
|
||||
@@ -155,7 +151,7 @@ export function InvoicesListTableComponent({
|
||||
state.sortedInfo.columnKey === "quantity" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("invoicelines.fields.cost_center"),
|
||||
title: t("billlines.fields.cost_center"),
|
||||
dataIndex: "cost_center",
|
||||
key: "cost_center",
|
||||
sorter: (a, b) => alphaSort(a.cost_center, b.cost_center),
|
||||
@@ -164,7 +160,7 @@ export function InvoicesListTableComponent({
|
||||
state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("invoicelines.fields.federal_tax_applicable"),
|
||||
title: t("billlines.fields.federal_tax_applicable"),
|
||||
dataIndex: "applicable_taxes.federal",
|
||||
key: "applicable_taxes.federal",
|
||||
render: (text, record) => (
|
||||
@@ -178,7 +174,7 @@ export function InvoicesListTableComponent({
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("invoicelines.fields.state_tax_applicable"),
|
||||
title: t("billlines.fields.state_tax_applicable"),
|
||||
dataIndex: "applicable_taxes.state",
|
||||
key: "applicable_taxes.state",
|
||||
render: (text, record) => (
|
||||
@@ -192,7 +188,7 @@ export function InvoicesListTableComponent({
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("invoicelines.fields.local_tax_applicable"),
|
||||
title: t("billlines.fields.local_tax_applicable"),
|
||||
dataIndex: "applicable_taxes.local",
|
||||
key: "applicable_taxes.local",
|
||||
render: (text, record) => (
|
||||
@@ -207,26 +203,26 @@ export function InvoicesListTableComponent({
|
||||
},
|
||||
];
|
||||
|
||||
const handleOnInvoiceRowclick = (selectedRows) => {
|
||||
setSelectedInvoiceLinesByInvoice({
|
||||
...selectedInvoiceLinesByInvoice,
|
||||
const handleOnBillrowclick = (selectedRows) => {
|
||||
setSelectedBillLinesByBill({
|
||||
...selectedBillLinesByBill,
|
||||
[record.id]: selectedRows.map((r) => r.id),
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Typography.Title level={3}>{`${t("invoices.fields.invoice_number")} ${
|
||||
<Typography.Title level={3}>{`${t("bills.fields.invoice_number")} ${
|
||||
record.invoice_number
|
||||
}`}</Typography.Title>
|
||||
<Descriptions>
|
||||
<Descriptions.Item label={t("invoices.fields.federal_tax_rate")}>
|
||||
<Descriptions.Item label={t("bills.fields.federal_tax_rate")}>
|
||||
{`${record.federal_tax_rate}%` || ""}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("invoices.fields.state_tax_rate")}>
|
||||
<Descriptions.Item label={t("bills.fields.state_tax_rate")}>
|
||||
{`${record.state_tax_rate}%` || ""}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("invoices.fields.local_tax_rate")}>
|
||||
<Descriptions.Item label={t("bills.fields.local_tax_rate")}>
|
||||
{`${record.local_tax_rate}%` || ""}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
@@ -238,11 +234,11 @@ export function InvoicesListTableComponent({
|
||||
context: {
|
||||
jobId: job.id,
|
||||
vendorId: record.vendorid,
|
||||
returnFromInvoice: record.id,
|
||||
returnFromBill: record.id,
|
||||
invoiceNumber: record.invoice_number,
|
||||
linesToOrder: record.invoicelines
|
||||
linesToOrder: record.billlines
|
||||
.filter((il) =>
|
||||
selectedInvoiceLinesByInvoice[record.id].includes(il.id)
|
||||
selectedBillLinesByBill[record.id].includes(il.id)
|
||||
)
|
||||
.map((i) => {
|
||||
return {
|
||||
@@ -258,7 +254,7 @@ export function InvoicesListTableComponent({
|
||||
})
|
||||
}
|
||||
>
|
||||
{t("invoices.actions.return")}
|
||||
{t("bills.actions.return")}
|
||||
</Button>
|
||||
<Table
|
||||
size="small"
|
||||
@@ -266,15 +262,15 @@ export function InvoicesListTableComponent({
|
||||
pagination={{ position: "top", defaultPageSize: 25 }}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={record.invoicelines}
|
||||
dataSource={record.billlines}
|
||||
rowSelection={{
|
||||
onSelect: (record, selected, selectedRows) => {
|
||||
handleOnInvoiceRowclick(selectedRows);
|
||||
handleOnBillrowclick(selectedRows);
|
||||
},
|
||||
onSelectAll: (selected, selectedRows, changeRows) => {
|
||||
handleOnInvoiceRowclick(selectedRows);
|
||||
handleOnBillrowclick(selectedRows);
|
||||
},
|
||||
selectedRowKeys: selectedInvoiceLinesByInvoice[record.id],
|
||||
selectedRowKeys: selectedBillLinesByBill[record.id],
|
||||
type: "checkbox",
|
||||
}}
|
||||
/>
|
||||
@@ -284,11 +280,9 @@ export function InvoicesListTableComponent({
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Typography.Title level={4}>
|
||||
{t("invoices.labels.invoices")}
|
||||
</Typography.Title>
|
||||
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
|
||||
<Table
|
||||
loading={loading}
|
||||
loading={billsQuery.loading}
|
||||
size="small"
|
||||
title={() => (
|
||||
<div className="imex-table-header">
|
||||
@@ -299,25 +293,23 @@ export function InvoicesListTableComponent({
|
||||
<div>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setInvoiceEnterContext({
|
||||
actions: { refetch: invoicesQuery.refetch },
|
||||
setBillEnterContext({
|
||||
actions: { refetch: billsQuery.refetch },
|
||||
context: {
|
||||
job,
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("jobs.actions.postInvoices")}
|
||||
{t("jobs.actions.postbills")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setReconciliationContext({
|
||||
actions: { refetch: invoicesQuery.refetch },
|
||||
actions: { refetch: billsQuery.refetch },
|
||||
context: {
|
||||
job,
|
||||
invoices:
|
||||
(invoicesQuery.data && invoicesQuery.data.invoices) ||
|
||||
[],
|
||||
bills: (billsQuery.data && billsQuery.data.bills) || [],
|
||||
},
|
||||
});
|
||||
}}
|
||||
@@ -342,10 +334,10 @@ export function InvoicesListTableComponent({
|
||||
pagination={{ position: "top", defaultPageSize: 25 }}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={invoices}
|
||||
dataSource={bills}
|
||||
onChange={handleTableChange}
|
||||
expandable={{
|
||||
expandedRowKeys: [selectedInvoice],
|
||||
expandedRowKeys: [selectedBill],
|
||||
onExpand: (expanded, record) => {
|
||||
handleOnRowClick(expanded ? record : null);
|
||||
},
|
||||
@@ -354,7 +346,7 @@ export function InvoicesListTableComponent({
|
||||
onSelect: (record) => {
|
||||
handleOnRowClick(record);
|
||||
},
|
||||
selectedRowKeys: [selectedInvoice],
|
||||
selectedRowKeys: [selectedBill],
|
||||
type: "radio",
|
||||
}}
|
||||
onRow={(record, rowIndex) => {
|
||||
@@ -372,4 +364,4 @@ export function InvoicesListTableComponent({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(null, mapDispatchToProps)(InvoicesListTableComponent);
|
||||
export default connect(null, mapDispatchToProps)(BillsListTableComponent);
|
||||
@@ -8,7 +8,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
|
||||
export default function InvoicesVendorsList() {
|
||||
export default function BillsVendorsList() {
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const history = useHistory();
|
||||
|
||||
@@ -51,7 +51,7 @@ export default function InvoicesVendorsList() {
|
||||
|
||||
const handleOnRowClick = (record) => {
|
||||
if (record) {
|
||||
delete search.invoiceid;
|
||||
delete search.billid;
|
||||
if (record.id) {
|
||||
search.vendorid = record.id;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
@@ -116,9 +116,9 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract }) {
|
||||
shopid: bodyshop.id,
|
||||
ownerid: contract.job.ownerid,
|
||||
vehicleid: contract.job.vehicleid,
|
||||
federal_tax_rate: bodyshop.invoice_tax_rates.federal_tax_rate / 100,
|
||||
state_tax_rate: bodyshop.invoice_tax_rates.state_tax_rate / 100,
|
||||
local_tax_rate: bodyshop.invoice_tax_rates.local_tax_rate / 100,
|
||||
federal_tax_rate: bodyshop.bill_tax_rates.federal_tax_rate / 100,
|
||||
state_tax_rate: bodyshop.bill_tax_rates.state_tax_rate / 100,
|
||||
local_tax_rate: bodyshop.bill_tax_rates.local_tax_rate / 100,
|
||||
clm_no: `${contract.job.clm_no}-CC`,
|
||||
clm_total: 1234, //TODO
|
||||
ownr_fn: contract.job.owner.ownr_fn,
|
||||
|
||||
@@ -20,7 +20,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
toggleModalVisible: () => dispatch(toggleModalVisible("courtesyCarReturn")),
|
||||
});
|
||||
|
||||
export function InvoiceEnterModalContainer({
|
||||
export function BillEnterModalContainer({
|
||||
courtesyCarReturnModal,
|
||||
toggleModalVisible,
|
||||
bodyshop,
|
||||
@@ -84,4 +84,4 @@ export function InvoiceEnterModalContainer({
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(InvoiceEnterModalContainer);
|
||||
)(BillEnterModalContainer);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import DocumentsUploadComponent from "./documents-upload.component";
|
||||
import { handleUpload } from "./documents-upload.utility";
|
||||
|
||||
@@ -13,7 +16,7 @@ const mapStateToProps = createStructuredSelector({
|
||||
export function DocumentsUploadContainer({
|
||||
jobId,
|
||||
tagsArray,
|
||||
invoiceId,
|
||||
billId,
|
||||
currentUser,
|
||||
bodyshop,
|
||||
callbackAfterUpload,
|
||||
@@ -26,7 +29,7 @@ export function DocumentsUploadContainer({
|
||||
bodyshop: bodyshop,
|
||||
uploaded_by: currentUser.email,
|
||||
jobId: jobId,
|
||||
invoiceId: invoiceId,
|
||||
billId: billId,
|
||||
tagsArray: tagsArray,
|
||||
callback: callbackAfterUpload,
|
||||
})
|
||||
|
||||
@@ -67,14 +67,7 @@ export const uploadToS3 = (
|
||||
onProgress,
|
||||
context
|
||||
) => {
|
||||
const {
|
||||
bodyshop,
|
||||
jobId,
|
||||
invoiceId,
|
||||
uploaded_by,
|
||||
callback,
|
||||
tagsArray,
|
||||
} = context;
|
||||
const { bodyshop, jobId, billId, uploaded_by, callback, tagsArray } = context;
|
||||
|
||||
let timestamp = Math.floor(Date.now() / 1000);
|
||||
let public_id = fileName;
|
||||
@@ -121,7 +114,7 @@ export const uploadToS3 = (
|
||||
jobid: jobId,
|
||||
uploaded_by: uploaded_by,
|
||||
key: fileName,
|
||||
invoiceid: invoiceId,
|
||||
billid: billId,
|
||||
type: fileType,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -132,16 +132,16 @@ export default function GlobalSearch() {
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: renderTitle(t("menus.header.search.invoices")),
|
||||
options: data.search_invoices.map((invoice) => {
|
||||
label: renderTitle(t("menus.header.search.bills")),
|
||||
options: data.search_bills.map((bill) => {
|
||||
return {
|
||||
value: `${invoice.invoice_number}`,
|
||||
value: `${bill.invoice_number}`,
|
||||
label: (
|
||||
<Link to={`/manage/invoices?invoiceid=${invoice.id}`}>
|
||||
<Link to={`/manage/bills?billid=${bill.id}`}>
|
||||
<div className="imex-flex-row">
|
||||
<span className="imex-flex-row__margin-large">{`${invoice.invoice_number}`}</span>
|
||||
<span className="imex-flex-row__margin-large">{`${invoice.vendor.name}`}</span>
|
||||
<span className="imex-flex-row__margin-large">{`${invoice.date}`}</span>
|
||||
<span className="imex-flex-row__margin-large">{`${bill.invoice_number}`}</span>
|
||||
<span className="imex-flex-row__margin-large">{`${bill.vendor.name}`}</span>
|
||||
<span className="imex-flex-row__margin-large">{`${bill.date}`}</span>
|
||||
</div>
|
||||
</Link>
|
||||
),
|
||||
|
||||
@@ -43,8 +43,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setInvoiceEnterContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "invoiceEnter" })),
|
||||
setBillEnterContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
||||
setTimeTicketContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "timeTicket" })),
|
||||
setPaymentContext: (context) =>
|
||||
@@ -57,7 +57,7 @@ function Header({
|
||||
currentUser,
|
||||
selectedHeader,
|
||||
signOutStart,
|
||||
setInvoiceEnterContext,
|
||||
setBillEnterContext,
|
||||
setTimeTicketContext,
|
||||
setPaymentContext,
|
||||
recentItems,
|
||||
@@ -183,20 +183,20 @@ function Header({
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Menu.Item key="invoices">
|
||||
<Link to="/manage/invoices">{t("menus.header.invoices")}</Link>
|
||||
<Menu.Item key="bills">
|
||||
<Link to="/manage/bills">{t("menus.header.bills")}</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
key="enterinvoices"
|
||||
key="enterbills"
|
||||
onClick={() => {
|
||||
setInvoiceEnterContext({
|
||||
setBillEnterContext({
|
||||
actions: {},
|
||||
context: {},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Icon component={FaFileInvoiceDollar} />
|
||||
{t("menus.header.enterinvoices")}
|
||||
{t("menus.header.enterbills")}
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
<Menu.Item key="allpayments">
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
// .invoice-form-wrapper {
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
// justify-content: left;
|
||||
// }
|
||||
|
||||
// .invoice-form-totals {
|
||||
// display: flex;
|
||||
// justify-content: space-around;
|
||||
// align-items: flex-start;
|
||||
// flex-wrap: wrap;
|
||||
// & > * {
|
||||
// padding: 5px;
|
||||
// }
|
||||
// }
|
||||
|
||||
// .invoice-form-invoice-details {
|
||||
// display: flex;
|
||||
// align-items: flex-start;
|
||||
// flex-wrap: wrap;
|
||||
// & > * {
|
||||
// padding: 5px;
|
||||
// }
|
||||
// }
|
||||
|
||||
// .invoice-form-lines-wrapper {
|
||||
// border: 3px ridge rgba(28, 110, 164, 0.24);
|
||||
// border-radius: 4px;
|
||||
// }
|
||||
|
||||
// .invoice-form-line {
|
||||
// display: flex;
|
||||
// flex-wrap: wrap;
|
||||
// align-items: flex-start;
|
||||
// justify-content: space-around;
|
||||
// border-bottom: 2px dashed rgba(7, 7, 7, 0.4);
|
||||
// F & > * {
|
||||
// margin: 5px;
|
||||
// }
|
||||
// }
|
||||
@@ -1,161 +0,0 @@
|
||||
//DEPRECATED.
|
||||
|
||||
// import React, { useState } from "react";
|
||||
// import { QUERY_INVOICES_BY_VENDOR_PAGINATED } from "../../graphql/invoices.queries";
|
||||
// import { useQuery } from "@apollo/react-hooks";
|
||||
// import queryString from "query-string";
|
||||
// import { useHistory, useLocation } from "react-router-dom";
|
||||
// import { Table, Input } from "antd";
|
||||
// import { useTranslation } from "react-i18next";
|
||||
// import { alphaSort } from "../../utils/sorters";
|
||||
// import AlertComponent from "../alert/alert.component";
|
||||
// import { DateFormatter } from "../../utils/DateFormatter";
|
||||
// import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
|
||||
// export default function InvoicesByVendorList() {
|
||||
// const search = queryString.parse(useLocation().search);
|
||||
// const history = useHistory();
|
||||
// const { page, sortcolumn, sortorder } = search;
|
||||
|
||||
// const { loading, error, data } = useQuery(
|
||||
// QUERY_INVOICES_BY_VENDOR_PAGINATED,
|
||||
// {
|
||||
// variables: {
|
||||
// vendorId: search.vendorid,
|
||||
// offset: page ? (page - 1) * 25 : 0,
|
||||
// limit: 25,
|
||||
// order: [
|
||||
// {
|
||||
// [sortcolumn || "date"]: sortorder
|
||||
// ? sortorder === "descend"
|
||||
// ? "desc"
|
||||
// : "asc"
|
||||
// : "desc",
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// skip: !!!search.vendorid,
|
||||
// }
|
||||
// );
|
||||
|
||||
// const { t } = useTranslation();
|
||||
|
||||
// const [state, setState] = useState({
|
||||
// sortedInfo: {},
|
||||
// search: "",
|
||||
// });
|
||||
|
||||
// const handleTableChange = (pagination, filters, sorter) => {
|
||||
// setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||
// search.page = pagination.current;
|
||||
// search.sortcolumn = sorter.columnKey;
|
||||
// search.sortorder = sorter.order;
|
||||
// history.push({ search: queryString.stringify(search) });
|
||||
// };
|
||||
|
||||
// const handleOnRowClick = (record) => {
|
||||
// if (record) {
|
||||
// if (record.id) {
|
||||
// search.invoiceid = record.id;
|
||||
// history.push({ search: queryString.stringify(search) });
|
||||
// }
|
||||
// } else {
|
||||
// delete search.invoiceid;
|
||||
// history.push({ search: queryString.stringify(search) });
|
||||
// }
|
||||
// };
|
||||
|
||||
// const columns = [
|
||||
// {
|
||||
// title: t("invoices.fields.invoice_number"),
|
||||
// dataIndex: "invoice_number",
|
||||
// key: "invoice_number",
|
||||
// sorter: (a, b) => alphaSort(a.invoice_number, b.invoice_number),
|
||||
// sortOrder:
|
||||
// state.sortedInfo.columnKey === "invoice_number" &&
|
||||
// state.sortedInfo.order,
|
||||
// },
|
||||
// {
|
||||
// title: t("invoices.fields.date"),
|
||||
// dataIndex: "date",
|
||||
// key: "date",
|
||||
|
||||
// sorter: (a, b) => a.date - b.date,
|
||||
// sortOrder:
|
||||
// state.sortedInfo.columnKey === "date" && state.sortedInfo.order,
|
||||
// render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
|
||||
// },
|
||||
// {
|
||||
// title: t("invoices.fields.total"),
|
||||
// dataIndex: "total",
|
||||
// key: "total",
|
||||
|
||||
// sorter: (a, b) => a.total - b.total,
|
||||
// sortOrder:
|
||||
// state.sortedInfo.columnKey === "total" && state.sortedInfo.order,
|
||||
// render: (text, record) => (
|
||||
// <CurrencyFormatter>{record.total}</CurrencyFormatter>
|
||||
// ),
|
||||
// },
|
||||
// ];
|
||||
|
||||
// const handleSearch = (e) => {
|
||||
// setState({ ...state, search: e.target.value });
|
||||
// };
|
||||
|
||||
// const dataSource = state.search
|
||||
// ? data.invoices.filter(
|
||||
// (i) =>
|
||||
// (i.invoice_number || "")
|
||||
// .toLowerCase()
|
||||
// .includes(state.search.toLowerCase()) ||
|
||||
// (i.amount || "").toString().includes(state.search)
|
||||
// )
|
||||
// : (data && data.invoices) || [];
|
||||
|
||||
// if (error) return <AlertComponent message={error.message} type='error' />;
|
||||
|
||||
// return (
|
||||
// <Table
|
||||
// loading={loading}
|
||||
// title={() => {
|
||||
// return (
|
||||
// <div>
|
||||
// <Input
|
||||
// value={state.search}
|
||||
// onChange={handleSearch}
|
||||
// placeholder={t("general.labels.search")}
|
||||
// allowClear
|
||||
// />
|
||||
// </div>
|
||||
// );
|
||||
// }}
|
||||
// dataSource={dataSource}
|
||||
// size='small'
|
||||
// scroll={{ x: true }}
|
||||
// pagination={{
|
||||
// position: "top",
|
||||
// pageSize: 25,
|
||||
// current: parseInt(page || 1),
|
||||
// total: data ? data.invoices_aggregate.aggregate.count : 0,
|
||||
// }}
|
||||
// columns={columns}
|
||||
// rowKey='id'
|
||||
// onChange={handleTableChange}
|
||||
// rowSelection={{
|
||||
// onSelect: (record) => {
|
||||
// handleOnRowClick(record);
|
||||
// },
|
||||
// selectedRowKeys: [search.invoiceid],
|
||||
// type: "radio",
|
||||
// }}
|
||||
// onRow={(record, rowIndex) => {
|
||||
// return {
|
||||
// onClick: (event) => {
|
||||
// handleOnRowClick(record);
|
||||
// }, // click row
|
||||
// };
|
||||
// }}
|
||||
// />
|
||||
// );
|
||||
// }
|
||||
@@ -4,9 +4,9 @@ import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
import "./job-invoices-total.styles.scss";
|
||||
import "./job-bills-total.styles.scss";
|
||||
|
||||
export default function JobInvoiceTotals({ loading, invoices, jobTotals }) {
|
||||
export default function JobBillsTotalComponent({ loading, bills, jobTotals }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (loading) return <LoadingSkeleton />;
|
||||
@@ -17,10 +17,10 @@ export default function JobInvoiceTotals({ loading, invoices, jobTotals }) {
|
||||
|
||||
const totals = jobTotals;
|
||||
|
||||
let invoiceTotals = Dinero({ amount: 0 });
|
||||
invoices.forEach((i) =>
|
||||
i.invoicelines.forEach((il) => {
|
||||
invoiceTotals = invoiceTotals.add(
|
||||
let billTotals = Dinero({ amount: 0 });
|
||||
bills.forEach((i) =>
|
||||
i.billlines.forEach((il) => {
|
||||
billTotals = billTotals.add(
|
||||
Dinero({
|
||||
amount: Math.round(
|
||||
(il.actual_cost || 0) * (i.is_credit_memo ? -1 : 1) * 100
|
||||
@@ -30,10 +30,10 @@ export default function JobInvoiceTotals({ loading, invoices, jobTotals }) {
|
||||
})
|
||||
);
|
||||
|
||||
const discrepancy = Dinero(totals.parts.parts.total).subtract(invoiceTotals);
|
||||
const discrepancy = Dinero(totals.parts.parts.total).subtract(billTotals);
|
||||
|
||||
return (
|
||||
<div className="job-invoices-totals-container">
|
||||
<div className="job-bills-totals-container">
|
||||
<Statistic
|
||||
title={t("jobs.labels.partstotal")}
|
||||
value={Dinero(totals.parts.parts.total).toFormat()}
|
||||
@@ -43,11 +43,11 @@ export default function JobInvoiceTotals({ loading, invoices, jobTotals }) {
|
||||
value={Dinero(totals.parts.sublets.total).toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("invoices.labels.retailtotal")}
|
||||
value={invoiceTotals.toFormat()}
|
||||
title={t("bills.labels.retailtotal")}
|
||||
value={billTotals.toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("invoices.labels.discrepancy")}
|
||||
title={t("bills.labels.discrepancy")}
|
||||
valueStyle={{
|
||||
color: discrepancy.getAmount === 0 ? "green" : "red",
|
||||
}}
|
||||
@@ -1,4 +1,4 @@
|
||||
.job-invoices-totals-container {
|
||||
.job-bills-totals-container {
|
||||
margin: 0rem 2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -51,28 +51,25 @@ export function JobCostingModalComponent({ bodyshop, job }) {
|
||||
{ parts: {}, labor: {} }
|
||||
);
|
||||
|
||||
const invoiceTotalsByProfitCenter = job.invoices.reduce(
|
||||
(inv_acc, inv_val) => {
|
||||
//At the invoice level.
|
||||
inv_val.invoicelines.map((line_val) => {
|
||||
//At the invoice line level.
|
||||
//console.log("JobCostingPartsTable -> line_val", line_val);
|
||||
if (!!!inv_acc[line_val.cost_center])
|
||||
inv_acc[line_val.cost_center] = Dinero();
|
||||
const billTotalsByProfitCenter = job.bills.reduce((bill_acc, bill_val) => {
|
||||
//At the invoice level.
|
||||
bill_val.billlines.map((line_val) => {
|
||||
//At the invoice line level.
|
||||
//console.log("JobCostingPartsTable -> line_val", line_val);
|
||||
if (!!!bill_acc[line_val.cost_center])
|
||||
bill_acc[line_val.cost_center] = Dinero();
|
||||
|
||||
inv_acc[line_val.cost_center] = inv_acc[line_val.cost_center].add(
|
||||
Dinero({
|
||||
amount: Math.round((line_val.actual_cost || 0) * 100),
|
||||
})
|
||||
.multiply(line_val.quantity)
|
||||
.multiply(inv_val.is_credit_memo ? -1 : 1)
|
||||
);
|
||||
return null;
|
||||
});
|
||||
return inv_acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
bill_acc[line_val.cost_center] = bill_acc[line_val.cost_center].add(
|
||||
Dinero({
|
||||
amount: Math.round((line_val.actual_cost || 0) * 100),
|
||||
})
|
||||
.multiply(line_val.quantity)
|
||||
.multiply(bill_val.is_credit_memo ? -1 : 1)
|
||||
);
|
||||
return null;
|
||||
});
|
||||
return bill_acc;
|
||||
}, {});
|
||||
|
||||
const ticketTotalsByProfitCenter = job.timetickets.reduce(
|
||||
(ticket_acc, ticket_val) => {
|
||||
@@ -114,12 +111,11 @@ export function JobCostingModalComponent({ bodyshop, job }) {
|
||||
|
||||
const cost_labor =
|
||||
ticketTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 });
|
||||
const cost_parts =
|
||||
invoiceTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 });
|
||||
const cost_parts = billTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 });
|
||||
|
||||
const cost = (
|
||||
invoiceTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 })
|
||||
).add(ticketTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 }));
|
||||
const cost = (billTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 })).add(
|
||||
ticketTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 })
|
||||
);
|
||||
const totalSales = sale_labor.add(sale_parts);
|
||||
const gpdollars = totalSales.subtract(cost);
|
||||
const gppercent = (
|
||||
|
||||
@@ -5,8 +5,8 @@ import { useTranslation } from "react-i18next";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
|
||||
export default function JobReconciliationInvoiceTable({
|
||||
invoiceLineState,
|
||||
export default function JobReconciliationBillsTable({
|
||||
billLineState,
|
||||
invoiceLineData,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
@@ -15,12 +15,12 @@ export default function JobReconciliationInvoiceTable({
|
||||
sortedInfo: {},
|
||||
});
|
||||
|
||||
const [selectedLines, setSelectedLines] = invoiceLineState;
|
||||
const [selectedLines, setSelectedLines] = billLineState;
|
||||
const [total, setTotal] = useState(Dinero({ amount: 0 }).toFormat());
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("invoicelines.fields.line_desc"),
|
||||
title: t("billlines.fields.line_desc"),
|
||||
dataIndex: "line_desc",
|
||||
key: "line_desc",
|
||||
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
||||
@@ -28,7 +28,7 @@ export default function JobReconciliationInvoiceTable({
|
||||
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("invoicelines.fields.retail"),
|
||||
title: t("billlines.fields.retail"),
|
||||
dataIndex: "actual_price",
|
||||
key: "actual_price",
|
||||
sorter: (a, b) => a.actual_price - b.actual_price,
|
||||
@@ -39,7 +39,7 @@ export default function JobReconciliationInvoiceTable({
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("invoicelines.fields.actual_cost"),
|
||||
title: t("billlines.fields.actual_cost"),
|
||||
dataIndex: "actual_cost",
|
||||
key: "actual_cost",
|
||||
sorter: (a, b) => a.actual_cost - b.actual_cost,
|
||||
@@ -50,7 +50,7 @@ export default function JobReconciliationInvoiceTable({
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("invoicelines.fields.quantity"),
|
||||
title: t("billlines.fields.quantity"),
|
||||
dataIndex: "quantity",
|
||||
key: "quantity",
|
||||
sorter: (a, b) => a.quantity - b.quantity,
|
||||
@@ -58,7 +58,7 @@ export default function JobReconciliationInvoiceTable({
|
||||
state.sortedInfo.columnKey === "quantity" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("invoices.fields.is_credit_memo"),
|
||||
title: t("bills.fields.is_credit_memo"),
|
||||
dataIndex: "is_credit_memo",
|
||||
key: "is_credit_memo",
|
||||
sorter: (a, b) => a.is_credit_memo - b.is_credit_memo,
|
||||
@@ -95,11 +95,11 @@ export default function JobReconciliationInvoiceTable({
|
||||
return (
|
||||
<div>
|
||||
<Table
|
||||
size='small'
|
||||
size="small"
|
||||
title={() => <div></div>}
|
||||
pagination={{ position: "top", defaultPageSize: 25 }}
|
||||
columns={columns}
|
||||
rowKey='id'
|
||||
rowKey="id"
|
||||
dataSource={invoiceLineData}
|
||||
onChange={handleTableChange}
|
||||
rowSelection={{
|
||||
@@ -107,7 +107,7 @@ export default function JobReconciliationInvoiceTable({
|
||||
selectedRowKeys: selectedLines,
|
||||
}}
|
||||
/>
|
||||
<Statistic value={total} title='total' />
|
||||
<Statistic value={total} title="total" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Col, Row } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import JobReconciliationInvoicesTable from "../job-reconciliation-invoices-table/job-reconciliation-invoices-table.component";
|
||||
import JobReconciliationBillsTable from "../job-reconciliation-bills-table/job-reconciliation-bills-table.component";
|
||||
import JobReconciliationPartsTable from "../job-reconciliation-parts-table/job-reconciliation-parts-table.component";
|
||||
|
||||
export default function JobReconciliationModalComponent({ job, invoices }) {
|
||||
export default function JobReconciliationModalComponent({ job, bills }) {
|
||||
const jobLineState = useState([]);
|
||||
const invoiceLineState = useState([]);
|
||||
const billLineState = useState([]);
|
||||
|
||||
const invoiceLineData =
|
||||
invoices
|
||||
bills
|
||||
.map((i) =>
|
||||
i.invoicelines.map((il) => {
|
||||
i.billlines.map((il) => {
|
||||
return { ...il, is_credit_memo: i.is_credit_memo };
|
||||
})
|
||||
)
|
||||
@@ -28,9 +28,9 @@ export default function JobReconciliationModalComponent({ job, invoices }) {
|
||||
/>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<JobReconciliationInvoicesTable
|
||||
<JobReconciliationBillsTable
|
||||
invoiceLineData={invoiceLineData}
|
||||
invoiceLineState={invoiceLineState}
|
||||
billLineState={billLineState}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@@ -5,27 +5,22 @@ import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||
import { selectReconciliation } from "../../redux/modals/modals.selectors";
|
||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||
import JobReconciliationModalComponent from "./job-reconciliation-modal.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
reconciliationModal: selectReconciliation,
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
toggleModalVisible: () => dispatch(toggleModalVisible("reconciliation")),
|
||||
});
|
||||
|
||||
function InvoiceEnterModalContainer({
|
||||
function JobReconciliationModalContainer({
|
||||
reconciliationModal,
|
||||
toggleModalVisible,
|
||||
bodyshop,
|
||||
currentUser,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const { context, visible } = reconciliationModal;
|
||||
const { job, invoices } = context;
|
||||
const { job, bills } = context;
|
||||
|
||||
const handleCancel = () => {
|
||||
toggleModalVisible();
|
||||
@@ -39,8 +34,9 @@ function InvoiceEnterModalContainer({
|
||||
okText={t("general.actions.save")}
|
||||
onOk={handleCancel}
|
||||
onCancel={handleCancel}
|
||||
destroyOnClose>
|
||||
<JobReconciliationModalComponent job={job} invoices={invoices} />
|
||||
destroyOnClose
|
||||
>
|
||||
<JobReconciliationModalComponent job={job} bills={bills} />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -48,4 +44,4 @@ function InvoiceEnterModalContainer({
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(InvoiceEnterModalContainer);
|
||||
)(JobReconciliationModalContainer);
|
||||
|
||||
@@ -20,8 +20,8 @@ const mapStateToProps = createStructuredSelector({
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setScheduleContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "schedule" })),
|
||||
setInvoiceEnterContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "invoiceEnter" })),
|
||||
setBillEnterContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
||||
setPaymentContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "payment" })),
|
||||
setJobCostingContext: (context) =>
|
||||
@@ -33,7 +33,7 @@ export function JobsDetailHeaderActions({
|
||||
bodyshop,
|
||||
refetch,
|
||||
setScheduleContext,
|
||||
setInvoiceEnterContext,
|
||||
setBillEnterContext,
|
||||
setPaymentContext,
|
||||
setJobCostingContext,
|
||||
}) {
|
||||
@@ -147,11 +147,11 @@ export function JobsDetailHeaderActions({
|
||||
</Popconfirm>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
key="postinvoices"
|
||||
key="postbills"
|
||||
onClick={() => {
|
||||
logImEXEvent("job_header_enter_invoice");
|
||||
logImEXEvent("job_header_enter_bills");
|
||||
|
||||
setInvoiceEnterContext({
|
||||
setBillEnterContext({
|
||||
actions: { refetch: refetch },
|
||||
context: {
|
||||
job: job,
|
||||
@@ -159,7 +159,7 @@ export function JobsDetailHeaderActions({
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("jobs.actions.postInvoices")}
|
||||
{t("jobs.actions.postbills")}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
disabled={!!job.date_invoiced || !jobInPostProduction}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Col, Row } from "antd";
|
||||
import React from "react";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import InvoicesListTableComponent from "../invoices-list-table/invoices-list-table.component";
|
||||
import JobInvoicesTotalsComponent from "../job-invoices-total/job-invoices-total.component";
|
||||
import BillsListTable from "../bills-list-table/bills-list-table.component";
|
||||
import JobBillsTotal from "../job-bills-total/job-bills-total.component";
|
||||
import PartsOrderModal from "../parts-order-modal/parts-order-modal.container";
|
||||
import PartsOrderListTableComponent from "../parts-order-list-table/parts-order-list-table.component";
|
||||
const tableCol = {
|
||||
@@ -25,37 +25,33 @@ const totalsCol = {
|
||||
|
||||
export default function JobsDetailPliComponent({
|
||||
job,
|
||||
invoicesQuery,
|
||||
handleInvoiceOnRowClick,
|
||||
billsQuery,
|
||||
handleBillOnRowClick,
|
||||
handlePartsOrderOnRowClick,
|
||||
selectedInvoice,
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<PartsOrderModal />
|
||||
{invoicesQuery.error ? (
|
||||
<AlertComponent message={invoicesQuery.error.message} type="error" />
|
||||
{billsQuery.error ? (
|
||||
<AlertComponent message={billsQuery.error.message} type="error" />
|
||||
) : null}
|
||||
<Row>
|
||||
<Col {...tableCol}>
|
||||
<PartsOrderListTableComponent
|
||||
job={job}
|
||||
loading={invoicesQuery.loading}
|
||||
handleOnRowClick={handlePartsOrderOnRowClick}
|
||||
selectedInvoice={selectedInvoice}
|
||||
invoicesQuery={invoicesQuery}
|
||||
billsQuery={billsQuery}
|
||||
/>
|
||||
<InvoicesListTableComponent
|
||||
<BillsListTable
|
||||
job={job}
|
||||
loading={invoicesQuery.loading}
|
||||
handleOnRowClick={handleInvoiceOnRowClick}
|
||||
invoicesQuery={invoicesQuery}
|
||||
handleOnRowClick={handleBillOnRowClick}
|
||||
billsQuery={billsQuery}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...totalsCol}>
|
||||
<JobInvoicesTotalsComponent
|
||||
invoices={invoicesQuery.data ? invoicesQuery.data.invoices : []}
|
||||
loading={invoicesQuery.loading}
|
||||
<JobBillsTotal
|
||||
bills={billsQuery.data ? billsQuery.data.bills : []}
|
||||
loading={billsQuery.loading}
|
||||
jobTotals={job.job_totals}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
@@ -2,25 +2,25 @@ import { useQuery } from "@apollo/react-hooks";
|
||||
import queryString from "query-string";
|
||||
import React from "react";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import { QUERY_INVOICES_BY_JOBID } from "../../graphql/invoices.queries";
|
||||
import { QUERY_BILLS_BY_JOBID } from "../../graphql/bills.queries";
|
||||
import JobsDetailPliComponent from "./jobs-detail-pli.component";
|
||||
|
||||
export default function JobsDetailPliContainer({ job }) {
|
||||
const invoicesQuery = useQuery(QUERY_INVOICES_BY_JOBID, {
|
||||
const billsQuery = useQuery(QUERY_BILLS_BY_JOBID, {
|
||||
variables: { jobid: job.id },
|
||||
});
|
||||
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const history = useHistory();
|
||||
|
||||
const handleInvoiceOnRowClick = (record) => {
|
||||
const handleBillOnRowClick = (record) => {
|
||||
if (record) {
|
||||
if (record.id) {
|
||||
search.invoiceid = record.id;
|
||||
search.billid = record.id;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
}
|
||||
} else {
|
||||
delete search.invoiceid;
|
||||
delete search.billid;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
}
|
||||
};
|
||||
@@ -40,8 +40,8 @@ export default function JobsDetailPliContainer({ job }) {
|
||||
return (
|
||||
<JobsDetailPliComponent
|
||||
job={job}
|
||||
invoicesQuery={invoicesQuery}
|
||||
handleInvoiceOnRowClick={handleInvoiceOnRowClick}
|
||||
billsQuery={billsQuery}
|
||||
handleBillOnRowClick={handleBillOnRowClick}
|
||||
handlePartsOrderOnRowClick={handlePartsOrderOnRowClick}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -8,8 +8,8 @@ function JobsDocumentsComponent({
|
||||
data,
|
||||
jobId,
|
||||
refetch,
|
||||
invoiceId,
|
||||
invoicesCallback,
|
||||
billId,
|
||||
billsCallback,
|
||||
}) {
|
||||
const [galleryImages, setgalleryImages] = useState([]);
|
||||
|
||||
@@ -34,18 +34,18 @@ function JobsDocumentsComponent({
|
||||
}, [data, setgalleryImages]);
|
||||
|
||||
return (
|
||||
<div className='clearfix'>
|
||||
<div className="clearfix">
|
||||
<DocumentsUploadContainer
|
||||
jobId={jobId}
|
||||
invoiceId={invoiceId}
|
||||
callbackAfterUpload={invoicesCallback || refetch}
|
||||
billId={billId}
|
||||
callbackAfterUpload={billsCallback || refetch}
|
||||
tagsArray={["test"]}
|
||||
/>
|
||||
|
||||
<JobsDocumentsDownloadButton galleryImages={galleryImages} />
|
||||
<JobsDocumentsDeleteButton
|
||||
galleryImages={galleryImages}
|
||||
deletionCallback={invoicesCallback || refetch}
|
||||
deletionCallback={billsCallback || refetch}
|
||||
/>
|
||||
|
||||
<Gallery
|
||||
|
||||
@@ -7,14 +7,14 @@ import JobDocuments from "./jobs-documents-gallery.component";
|
||||
|
||||
export default function JobsDocumentsContainer({
|
||||
jobId,
|
||||
invoiceId,
|
||||
billId,
|
||||
documentsList,
|
||||
invoicesCallback,
|
||||
billsCallback,
|
||||
}) {
|
||||
const { loading, error, data, refetch } = useQuery(GET_DOCUMENTS_BY_JOB, {
|
||||
variables: { jobId: jobId },
|
||||
fetchPolicy: "network-only",
|
||||
skip: !!invoiceId,
|
||||
skip: !!billId,
|
||||
});
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
@@ -23,10 +23,10 @@ export default function JobsDocumentsContainer({
|
||||
return (
|
||||
<JobDocuments
|
||||
data={(data && data.documents) || documentsList || []}
|
||||
invoiceId={invoiceId}
|
||||
billId={billId}
|
||||
jobId={jobId}
|
||||
refetch={refetch}
|
||||
invoicesCallback={invoicesCallback}
|
||||
billsCallback={billsCallback}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -110,7 +110,6 @@ export function JobsExportAllButton({
|
||||
});
|
||||
}
|
||||
}
|
||||
//Set the list of selected invoices to be nothing.
|
||||
if (!!completedCallback) completedCallback([]);
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
|
||||
@@ -20,16 +20,16 @@ const mapStateToProps = createStructuredSelector({
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setInvoiceEnterContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "invoiceEnter" })),
|
||||
setBillEnterContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
||||
});
|
||||
|
||||
export function PartsOrderListTableComponent({
|
||||
setInvoiceEnterContext,
|
||||
setBillEnterContext,
|
||||
bodyshop,
|
||||
job,
|
||||
loading,
|
||||
invoicesQuery,
|
||||
|
||||
billsQuery,
|
||||
handleOnRowClick,
|
||||
}) {
|
||||
const responsibilityCenters = bodyshop.md_responsibility_centers;
|
||||
@@ -41,10 +41,8 @@ export function PartsOrderListTableComponent({
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const selectedpartsorder = search.partsorderid;
|
||||
|
||||
const parts_orders = invoicesQuery.data
|
||||
? invoicesQuery.data.parts_orders
|
||||
: [];
|
||||
const { refetch } = invoicesQuery;
|
||||
const parts_orders = billsQuery.data ? billsQuery.data.parts_orders : [];
|
||||
const { refetch } = billsQuery;
|
||||
const columns = [
|
||||
{
|
||||
title: t("vendors.fields.name"),
|
||||
@@ -93,15 +91,15 @@ export function PartsOrderListTableComponent({
|
||||
render: (text, record) => (
|
||||
<Button
|
||||
onClick={() => {
|
||||
logImEXEvent("parts_order_receive_invoice");
|
||||
logImEXEvent("parts_order_receive_bill");
|
||||
|
||||
setInvoiceEnterContext({
|
||||
setBillEnterContext({
|
||||
actions: { refetch: refetch },
|
||||
context: {
|
||||
job: job,
|
||||
invoice: {
|
||||
bill: {
|
||||
vendorid: record.vendor.id,
|
||||
invoicelines: record.parts_order_lines.map((pol) => {
|
||||
billlines: record.parts_order_lines.map((pol) => {
|
||||
return {
|
||||
joblineid: pol.job_line_id,
|
||||
line_desc: pol.line_desc,
|
||||
@@ -119,7 +117,7 @@ export function PartsOrderListTableComponent({
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("parts_orders.actions.receiveinvoice")}
|
||||
{t("parts_orders.actions.receivebill")}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
@@ -227,7 +225,7 @@ export function PartsOrderListTableComponent({
|
||||
{t("parts_orders.labels.parts_orders")}
|
||||
</Typography.Title>
|
||||
<Table
|
||||
loading={loading}
|
||||
loading={billsQuery.loading}
|
||||
size="small"
|
||||
title={() => (
|
||||
<div className="imex-table-header">
|
||||
|
||||
@@ -4,7 +4,7 @@ import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { INSERT_NEW_INVOICE } from "../../graphql/invoices.queries";
|
||||
import { INSERT_NEW_BILL } from "../../graphql/bills.queries";
|
||||
import { UPDATE_JOB_LINE_STATUS } from "../../graphql/jobs-lines.queries";
|
||||
import { INSERT_NEW_PARTS_ORDERS } from "../../graphql/parts-orders.queries";
|
||||
import { QUERY_ALL_VENDORS_FOR_ORDER } from "../../graphql/vendors.queries";
|
||||
@@ -68,7 +68,7 @@ export function PartsOrderModalContainer({
|
||||
|
||||
const [insertPartOrder] = useMutation(INSERT_NEW_PARTS_ORDERS);
|
||||
const [updateJobLines] = useMutation(UPDATE_JOB_LINE_STATUS);
|
||||
const [insertInvoice] = useMutation(INSERT_NEW_INVOICE);
|
||||
const [insertBill] = useMutation(INSERT_NEW_BILL);
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
logImEXEvent("parts_order_insert");
|
||||
@@ -120,9 +120,9 @@ export function PartsOrderModalContainer({
|
||||
jobid: jobId,
|
||||
total: 0,
|
||||
invoice_number: `${jobId}`,
|
||||
federal_tax_rate: bodyshop.invoice_tax_rates.federal_tax_rate || 0,
|
||||
state_tax_rate: bodyshop.invoice_tax_rates.state_tax_rate || 0,
|
||||
local_tax_rate: bodyshop.invoice_tax_rates.local_tax_rate || 0,
|
||||
federal_tax_rate: bodyshop.bill_tax_rates.federal_tax_rate || 0,
|
||||
state_tax_rate: bodyshop.bill_tax_rates.state_tax_rate || 0,
|
||||
local_tax_rate: bodyshop.bill_tax_rates.local_tax_rate || 0,
|
||||
invoicelines: {
|
||||
data: values.parts_order_lines.data.map((p) => {
|
||||
return {
|
||||
@@ -138,8 +138,8 @@ export function PartsOrderModalContainer({
|
||||
},
|
||||
};
|
||||
|
||||
await insertInvoice({
|
||||
variables: { invoice: invoiceToPost },
|
||||
await insertBill({
|
||||
variables: { bill: invoiceToPost },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { auth } from "../../firebase/firebase.utils";
|
||||
import { UPDATE_INVOICES } from "../../graphql/invoices.queries";
|
||||
import { UPDATE_BILLS } from "../../graphql/bills.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
|
||||
@@ -14,15 +14,15 @@ const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export function InvoiceExportAllButton({
|
||||
export function PayableExportAll({
|
||||
bodyshop,
|
||||
invoiceIds,
|
||||
billids,
|
||||
disabled,
|
||||
loadingCallback,
|
||||
completedCallback,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [updateInvoice] = useMutation(UPDATE_INVOICES);
|
||||
const [updateBill] = useMutation(UPDATE_BILLS);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleQbxml = async () => {
|
||||
@@ -35,7 +35,7 @@ export function InvoiceExportAllButton({
|
||||
try {
|
||||
QbXmlResponse = await axios.post(
|
||||
"/accounting/qbxml/payables",
|
||||
{ invoices: invoiceIds },
|
||||
{ bills: billids },
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`,
|
||||
@@ -45,7 +45,7 @@ export function InvoiceExportAllButton({
|
||||
} catch (error) {
|
||||
console.log("Error getting QBXML from Server.", error);
|
||||
notification["error"]({
|
||||
message: t("invoices.errors.exporting", {
|
||||
message: t("bills.errors.exporting", {
|
||||
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
||||
}),
|
||||
});
|
||||
@@ -64,7 +64,7 @@ export function InvoiceExportAllButton({
|
||||
} catch (error) {
|
||||
console.log("Error connecting to quickbooks or partner.", error);
|
||||
notification["error"]({
|
||||
message: t("invoices.errors.exporting-partner"),
|
||||
message: t("bills.errors.exporting-partner"),
|
||||
});
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
@@ -80,36 +80,35 @@ export function InvoiceExportAllButton({
|
||||
//Uh oh. At least one was no good.
|
||||
failedTransactions.map((ft) =>
|
||||
notification["error"]({
|
||||
message: t("invoices.errors.exporting", {
|
||||
message: t("bills.errors.exporting", {
|
||||
error: ft.errorMessage || "",
|
||||
}),
|
||||
})
|
||||
);
|
||||
}
|
||||
if (successfulTransactions.length > 0) {
|
||||
const invoiceUpdateResponse = await updateInvoice({
|
||||
const billUpdateResponse = await updateBill({
|
||||
variables: {
|
||||
invoiceIdList: successfulTransactions.map((st) => st.id),
|
||||
invoice: {
|
||||
billIdList: successfulTransactions.map((st) => st.id),
|
||||
bill: {
|
||||
exported: true,
|
||||
exported_at: new Date(),
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!!!invoiceUpdateResponse.errors) {
|
||||
if (!!!billUpdateResponse.errors) {
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.exported"),
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.exporting", {
|
||||
error: JSON.stringify(invoiceUpdateResponse.error),
|
||||
error: JSON.stringify(billUpdateResponse.error),
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//Set the list of selected invoices to be nothing.
|
||||
if (!!completedCallback) completedCallback([]);
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
@@ -127,4 +126,4 @@ export function InvoiceExportAllButton({
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null)(InvoiceExportAllButton);
|
||||
export default connect(mapStateToProps, null)(PayableExportAll);
|
||||
@@ -6,7 +6,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { auth } from "../../firebase/firebase.utils";
|
||||
import { UPDATE_INVOICES } from "../../graphql/invoices.queries";
|
||||
import { UPDATE_BILLS } from "../../graphql/bills.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
|
||||
@@ -14,14 +14,14 @@ const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export function InvoiceExportButton({
|
||||
export function PayableExportButton({
|
||||
bodyshop,
|
||||
invoiceId,
|
||||
billId,
|
||||
disabled,
|
||||
loadingCallback,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [updateInvoice] = useMutation(UPDATE_INVOICES);
|
||||
const [updateBill] = useMutation(UPDATE_BILLS);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleQbxml = async () => {
|
||||
@@ -34,7 +34,7 @@ export function InvoiceExportButton({
|
||||
try {
|
||||
QbXmlResponse = await axios.post(
|
||||
"/accounting/qbxml/payables",
|
||||
{ invoices: [invoiceId] },
|
||||
{ bills: [billId] },
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`,
|
||||
@@ -44,7 +44,7 @@ export function InvoiceExportButton({
|
||||
} catch (error) {
|
||||
console.log("Error getting QBXML from Server.", error);
|
||||
notification["error"]({
|
||||
message: t("invoices.errors.exporting", {
|
||||
message: t("bills.errors.exporting", {
|
||||
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
||||
}),
|
||||
});
|
||||
@@ -63,7 +63,7 @@ export function InvoiceExportButton({
|
||||
} catch (error) {
|
||||
console.log("Error connecting to quickbooks or partner.", error);
|
||||
notification["error"]({
|
||||
message: t("invoices.errors.exporting-partner"),
|
||||
message: t("bills.errors.exporting-partner"),
|
||||
});
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
@@ -79,30 +79,30 @@ export function InvoiceExportButton({
|
||||
//Uh oh. At least one was no good.
|
||||
failedTransactions.map((ft) =>
|
||||
notification["error"]({
|
||||
message: t("invoices.errors.exporting", {
|
||||
message: t("bills.errors.exporting", {
|
||||
error: ft.errorMessage || "",
|
||||
}),
|
||||
})
|
||||
);
|
||||
}
|
||||
if (successfulTransactions.length > 0) {
|
||||
const invoiceUpdateResponse = await updateInvoice({
|
||||
const billUpdateResponse = await updateBill({
|
||||
variables: {
|
||||
invoiceIdList: successfulTransactions.map((st) => st.id),
|
||||
invoice: {
|
||||
billIdList: successfulTransactions.map((st) => st.id),
|
||||
bill: {
|
||||
exported: true,
|
||||
exported_at: new Date(),
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!!!invoiceUpdateResponse.errors) {
|
||||
if (!!!billUpdateResponse.errors) {
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.exported"),
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.exporting", {
|
||||
error: JSON.stringify(invoiceUpdateResponse.error),
|
||||
error: JSON.stringify(billUpdateResponse.error),
|
||||
}),
|
||||
});
|
||||
}
|
||||
@@ -124,4 +124,4 @@ export function InvoiceExportButton({
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null)(InvoiceExportButton);
|
||||
export default connect(mapStateToProps, null)(PayableExportButton);
|
||||
@@ -25,7 +25,7 @@ export function PaymentFormComponent({ form, stripeStateArr, bodyshop }) {
|
||||
<div>
|
||||
<Form.Item
|
||||
name="jobid"
|
||||
label={t("invoices.fields.ro_number")}
|
||||
label={t("bills.fields.ro_number")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
|
||||
@@ -32,7 +32,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
toggleModalVisible: () => dispatch(toggleModalVisible("payment")),
|
||||
});
|
||||
|
||||
function InvoiceEnterModalContainer({
|
||||
function BillEnterModalContainer({
|
||||
paymentModal,
|
||||
toggleModalVisible,
|
||||
bodyshop,
|
||||
@@ -189,7 +189,7 @@ function InvoiceEnterModalContainer({
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(InvoiceEnterModalContainer);
|
||||
)(BillEnterModalContainer);
|
||||
|
||||
// const pr = stripe.paymentRequest({
|
||||
// country: "CA",
|
||||
|
||||
@@ -22,9 +22,9 @@ export default {
|
||||
"jobs:close": 5,
|
||||
"jobs:detail": 1,
|
||||
|
||||
"invoices:enter": 2,
|
||||
"invoices:view": 2,
|
||||
"invoices:list": 2,
|
||||
"bills:enter": 2,
|
||||
"bills:view": 2,
|
||||
"bills:list": 2,
|
||||
|
||||
"employees:page": 5,
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ export default function ShopInfoComponent({ form, saveLoading }) {
|
||||
<LayoutFormRow>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.invoice_federal_tax_rate")}
|
||||
name={["invoice_tax_rates", "federal_tax_rate"]}
|
||||
name={["bill_tax_rates", "federal_tax_rate"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
@@ -149,7 +149,7 @@ export default function ShopInfoComponent({ form, saveLoading }) {
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.invoice_state_tax_rate")}
|
||||
name={["invoice_tax_rates", "state_tax_rate"]}
|
||||
name={["bill_tax_rates", "state_tax_rate"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
@@ -161,7 +161,7 @@ export default function ShopInfoComponent({ form, saveLoading }) {
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.invoice_local_tax_rate")}
|
||||
name={["invoice_tax_rates", "local_tax_rate"]}
|
||||
name={["bill_tax_rates", "local_tax_rate"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
|
||||
@@ -238,38 +238,38 @@ export default function ShopInfoRbacComponent({ form }) {
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.rbac.invoices.enter")}
|
||||
label={t("bodyshop.fields.rbac.bills.enter")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
name={["md_rbac", "invoices:enter"]}
|
||||
name={["md_rbac", "bills:enter"]}
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.rbac.invoices.view")}
|
||||
label={t("bodyshop.fields.rbac.bills.view")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
name={["md_rbac", "invoices:view"]}
|
||||
name={["md_rbac", "bills:view"]}
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.rbac.invoices.list")}
|
||||
label={t("bodyshop.fields.rbac.bills.list")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
name={["md_rbac", "invoices:list"]}
|
||||
name={["md_rbac", "bills:list"]}
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
|
||||
@@ -58,7 +58,7 @@ export default function VendorsFormComponent({
|
||||
}}
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
{t("invoicelines.actions.newline")}
|
||||
{t("billlines.actions.newline")}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
@@ -28,9 +28,9 @@ export const QUERY_JOBS_FOR_EXPORT = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_INVOICES_FOR_EXPORT = gql`
|
||||
query QUERY_INVOICES_FOR_EXPORT {
|
||||
invoices(where: { exported: { _eq: false } }) {
|
||||
export const QUERY_BILLS_FOR_EXPORT = gql`
|
||||
query QUERY_BILLS_FOR_EXPORT {
|
||||
bills(where: { exported: { _eq: false } }) {
|
||||
id
|
||||
exported
|
||||
date
|
||||
|
||||
14
client/src/graphql/bill-lines.queries.js
Normal file
14
client/src/graphql/bill-lines.queries.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import gql from "graphql-tag";
|
||||
|
||||
export const UPDATE_BILL_LINE = gql`
|
||||
mutation UPDATE_BILL_LINE(
|
||||
$billLineId: uuid!
|
||||
$billLine: billlines_set_input!
|
||||
) {
|
||||
update_billlines(where: { id: { _eq: $billLineId } }, _set: $billLine) {
|
||||
returning {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -1,8 +1,8 @@
|
||||
import gql from "graphql-tag";
|
||||
|
||||
export const INSERT_NEW_INVOICE = gql`
|
||||
mutation INSERT_NEW_INVOICE($invoice: [invoices_insert_input!]!) {
|
||||
insert_invoices(objects: $invoice) {
|
||||
export const INSERT_NEW_BILL = gql`
|
||||
mutation INSERT_NEW_BILL($bill: [bills_insert_input!]!) {
|
||||
insert_bills(objects: $bill) {
|
||||
returning {
|
||||
id
|
||||
}
|
||||
@@ -10,14 +10,14 @@ export const INSERT_NEW_INVOICE = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_ALL_INVOICES_PAGINATED = gql`
|
||||
query QUERY_ALL_INVOICES_PAGINATED(
|
||||
export const QUERY_ALL_BILLS_PAGINATED = gql`
|
||||
query QUERY_ALL_BILLS_PAGINATED(
|
||||
$search: String
|
||||
$offset: Int
|
||||
$limit: Int
|
||||
$order: [invoices_order_by!]!
|
||||
$order: [bills_order_by!]!
|
||||
) {
|
||||
search_invoices(
|
||||
search_bills(
|
||||
args: { search: $search }
|
||||
offset: $offset
|
||||
limit: $limit
|
||||
@@ -39,7 +39,7 @@ export const QUERY_ALL_INVOICES_PAGINATED = gql`
|
||||
id
|
||||
ro_number
|
||||
}
|
||||
invoicelines {
|
||||
billlines {
|
||||
actual_price
|
||||
quantity
|
||||
actual_cost
|
||||
@@ -48,7 +48,7 @@ export const QUERY_ALL_INVOICES_PAGINATED = gql`
|
||||
line_desc
|
||||
}
|
||||
}
|
||||
search_invoices_aggregate(args: { search: $search }) {
|
||||
search_bills_aggregate(args: { search: $search }) {
|
||||
aggregate {
|
||||
count(distinct: true)
|
||||
}
|
||||
@@ -56,8 +56,8 @@ export const QUERY_ALL_INVOICES_PAGINATED = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_INVOICES_BY_JOBID = gql`
|
||||
query QUERY_PARTS_INVOICES_BY_JOBID($jobid: uuid!) {
|
||||
export const QUERY_BILLS_BY_JOBID = gql`
|
||||
query QUERY_PARTS_BILLS_BY_JOBID($jobid: uuid!) {
|
||||
parts_orders(
|
||||
where: { jobid: { _eq: $jobid } }
|
||||
order_by: { order_date: desc }
|
||||
@@ -89,7 +89,7 @@ export const QUERY_INVOICES_BY_JOBID = gql`
|
||||
order_number
|
||||
user_email
|
||||
}
|
||||
invoices(where: { jobid: { _eq: $jobid } }, order_by: { date: desc }) {
|
||||
bills(where: { jobid: { _eq: $jobid } }, order_by: { date: desc }) {
|
||||
id
|
||||
vendorid
|
||||
vendor {
|
||||
@@ -103,7 +103,7 @@ export const QUERY_INVOICES_BY_JOBID = gql`
|
||||
state_tax_rate
|
||||
local_tax_rate
|
||||
is_credit_memo
|
||||
invoicelines {
|
||||
billlines {
|
||||
actual_price
|
||||
quantity
|
||||
actual_cost
|
||||
@@ -117,57 +117,39 @@ export const QUERY_INVOICES_BY_JOBID = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_INVOICES_BY_VENDOR = gql`
|
||||
query QUERY_INVOICES_BY_VENDOR($vendorId: uuid!) {
|
||||
invoices(
|
||||
where: { vendorid: { _eq: $vendorId } }
|
||||
order_by: { date: desc }
|
||||
) {
|
||||
id
|
||||
job {
|
||||
id
|
||||
ro_number
|
||||
}
|
||||
total
|
||||
invoice_number
|
||||
date
|
||||
}
|
||||
}
|
||||
`;
|
||||
// export const QUERY_INVOICES_BY_VENDOR_PAGINATED = gql`
|
||||
// query QUERY_INVOICES_BY_VENDOR_PAGINATED(
|
||||
// $vendorId: uuid!
|
||||
// $offset: Int
|
||||
// $limit: Int
|
||||
// $order: [invoices_order_by!]!
|
||||
// ) {
|
||||
// invoices(
|
||||
// where: { vendorid: { _eq: $vendorId } }
|
||||
// offset: $offset
|
||||
// limit: $limit
|
||||
// order_by: $order
|
||||
// ) {
|
||||
// id
|
||||
// job {
|
||||
// id
|
||||
// ro_number
|
||||
// }
|
||||
// total
|
||||
// invoice_number
|
||||
// date
|
||||
// }
|
||||
// invoices_aggregate(where: { vendorid: { _eq: $vendorId } }) {
|
||||
// aggregate {
|
||||
// count(distinct: true)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// `;
|
||||
|
||||
export const QUERY_INVOICES_BY_VENDOR_PAGINATED = gql`
|
||||
query QUERY_INVOICES_BY_VENDOR_PAGINATED(
|
||||
$vendorId: uuid!
|
||||
$offset: Int
|
||||
$limit: Int
|
||||
$order: [invoices_order_by!]!
|
||||
) {
|
||||
invoices(
|
||||
where: { vendorid: { _eq: $vendorId } }
|
||||
offset: $offset
|
||||
limit: $limit
|
||||
order_by: $order
|
||||
) {
|
||||
id
|
||||
job {
|
||||
id
|
||||
ro_number
|
||||
}
|
||||
total
|
||||
invoice_number
|
||||
date
|
||||
}
|
||||
invoices_aggregate(where: { vendorid: { _eq: $vendorId } }) {
|
||||
aggregate {
|
||||
count(distinct: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_INVOICE_BY_PK = gql`
|
||||
query QUERY_INVOICE_BY_PK($invoiceid: uuid!) {
|
||||
invoices_by_pk(id: $invoiceid) {
|
||||
export const QUERY_BILL_BY_PK = gql`
|
||||
query QUERY_BILL_BY_PK($billid: uuid!) {
|
||||
bills_by_pk(id: $billid) {
|
||||
due_date
|
||||
exported
|
||||
exported_at
|
||||
@@ -187,7 +169,7 @@ export const QUERY_INVOICE_BY_PK = gql`
|
||||
name
|
||||
discount
|
||||
}
|
||||
invoicelines {
|
||||
billlines {
|
||||
id
|
||||
line_desc
|
||||
actual_price
|
||||
@@ -207,9 +189,9 @@ export const QUERY_INVOICE_BY_PK = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const UPDATE_INVOICE = gql`
|
||||
mutation UPDATE_INVOICE($invoiceId: uuid!, $invoice: invoices_set_input!) {
|
||||
update_invoices(where: { id: { _eq: $invoiceId } }, _set: $invoice) {
|
||||
export const UPDATE_BILL = gql`
|
||||
mutation UPDATE_BILL($billId: uuid!, $bill: bills_set_input!) {
|
||||
update_bills(where: { id: { _eq: $billId } }, _set: $bill) {
|
||||
returning {
|
||||
id
|
||||
exported
|
||||
@@ -219,12 +201,9 @@ export const UPDATE_INVOICE = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const UPDATE_INVOICES = gql`
|
||||
mutation UPDATE_INVOICES(
|
||||
$invoiceIdList: [uuid!]!
|
||||
$invoice: invoices_set_input!
|
||||
) {
|
||||
update_invoices(where: { id: { _in: $invoiceIdList } }, _set: $invoice) {
|
||||
export const UPDATE_BILLS = gql`
|
||||
mutation UPDATE_BILLS($billIdList: [uuid!]!, $bill: bills_set_input!) {
|
||||
update_bills(where: { id: { _in: $billIdList } }, _set: $bill) {
|
||||
returning {
|
||||
id
|
||||
exported
|
||||
@@ -48,7 +48,7 @@ export const QUERY_BODYSHOP = gql`
|
||||
template_header
|
||||
textid
|
||||
production_config
|
||||
invoice_tax_rates
|
||||
bill_tax_rates
|
||||
inhousevendorid
|
||||
accountingconfig
|
||||
appt_length
|
||||
@@ -118,7 +118,7 @@ export const UPDATE_SHOP = gql`
|
||||
template_header
|
||||
textid
|
||||
production_config
|
||||
invoice_tax_rates
|
||||
bill_tax_rates
|
||||
appt_length
|
||||
stripe_acct_id
|
||||
ssbuckets
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import gql from "graphql-tag";
|
||||
|
||||
export const UPDATE_INVOICE_LINE = gql`
|
||||
mutation UPDATE_INVOICE_LINE(
|
||||
$invoicelineId: uuid!
|
||||
$invoiceLine: invoicelines_set_input!
|
||||
) {
|
||||
update_invoicelines(
|
||||
where: { id: { _eq: $invoicelineId } }
|
||||
_set: $invoiceLine
|
||||
) {
|
||||
returning {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -124,8 +124,8 @@ export const UPDATE_JOB_LINE = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_JOB_LINES_TO_ENTER_INVOICE = gql`
|
||||
query GET_JOB_LINES_TO_ENTER_INVOICE($id: uuid!) {
|
||||
export const GET_JOB_LINES_TO_ENTER_BILL = gql`
|
||||
query GET_JOB_LINES_TO_ENTER_BILL($id: uuid!) {
|
||||
joblines(where: { jobid: { _eq: $id } }) {
|
||||
id
|
||||
line_desc
|
||||
|
||||
@@ -215,13 +215,13 @@ export const QUERY_JOB_COSTING_DETAILS = gql`
|
||||
lbr_amt
|
||||
op_code_desc
|
||||
}
|
||||
invoices {
|
||||
bills {
|
||||
id
|
||||
federal_tax_rate
|
||||
local_tax_rate
|
||||
state_tax_rate
|
||||
is_credit_memo
|
||||
invoicelines {
|
||||
billlines {
|
||||
actual_cost
|
||||
cost_center
|
||||
id
|
||||
|
||||
@@ -42,7 +42,7 @@ export const GLOBAL_SEARCH_QUERY = gql`
|
||||
memo
|
||||
transactionid
|
||||
}
|
||||
search_invoices(args: { search: $search }) {
|
||||
search_bills(args: { search: $search }) {
|
||||
id
|
||||
date
|
||||
invoice_number
|
||||
|
||||
@@ -5,13 +5,14 @@ import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import AccountingPayablesTable from "../../components/accounting-payables-table/accounting-payables-table.component";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import { QUERY_INVOICES_FOR_EXPORT } from "../../graphql/accounting.queries";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { QUERY_BILLS_FOR_EXPORT } from "../../graphql/accounting.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
@@ -20,6 +21,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function AccountingPayablesContainer({
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
@@ -38,7 +40,7 @@ export function AccountingPayablesContainer({
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
const { loading, error, data } = useQuery(QUERY_INVOICES_FOR_EXPORT);
|
||||
const { loading, error, data } = useQuery(QUERY_BILLS_FOR_EXPORT);
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
@@ -47,7 +49,7 @@ export function AccountingPayablesContainer({
|
||||
<RbacWrapper action="accounting:payables">
|
||||
<AccountingPayablesTable
|
||||
loadaing={loading}
|
||||
invoices={data ? data.invoices : []}
|
||||
bills={data ? data.bills : []}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
</div>
|
||||
|
||||
@@ -13,17 +13,17 @@ import { alphaSort } from "../../utils/sorters";
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setPartsOrderContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
||||
setInvoiceEnterContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "invoiceEnter" })),
|
||||
setBillEnterContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
||||
});
|
||||
|
||||
export function InvoicesListPage({
|
||||
export function BillsListPage({
|
||||
loading,
|
||||
data,
|
||||
refetch,
|
||||
total,
|
||||
setPartsOrderContext,
|
||||
setInvoiceEnterContext,
|
||||
setBillEnterContext,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [state, setState] = useState({
|
||||
@@ -35,7 +35,7 @@ export function InvoicesListPage({
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("invoices.fields.vendorname"),
|
||||
title: t("bills.fields.vendorname"),
|
||||
dataIndex: "vendorname",
|
||||
key: "vendorname",
|
||||
// sorter: (a, b) => alphaSort(a.vendor.name, b.vendor.name),
|
||||
@@ -44,7 +44,7 @@ export function InvoicesListPage({
|
||||
render: (text, record) => <span>{record.vendor.name}</span>,
|
||||
},
|
||||
{
|
||||
title: t("invoices.fields.invoice_number"),
|
||||
title: t("bills.fields.invoice_number"),
|
||||
dataIndex: "invoice_number",
|
||||
key: "invoice_number",
|
||||
sorter: (a, b) => alphaSort(a.invoice_number, b.invoice_number),
|
||||
@@ -53,7 +53,7 @@ export function InvoicesListPage({
|
||||
state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("invoices.fields.date"),
|
||||
title: t("bills.fields.date"),
|
||||
dataIndex: "date",
|
||||
key: "date",
|
||||
sorter: (a, b) => a.date - b.date,
|
||||
@@ -62,7 +62,7 @@ export function InvoicesListPage({
|
||||
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
|
||||
},
|
||||
{
|
||||
title: t("invoices.fields.total"),
|
||||
title: t("bills.fields.total"),
|
||||
dataIndex: "total",
|
||||
key: "total",
|
||||
sorter: (a, b) => a.total - b.total,
|
||||
@@ -73,7 +73,7 @@ export function InvoicesListPage({
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("invoices.fields.is_credit_memo"),
|
||||
title: t("bills.fields.is_credit_memo"),
|
||||
dataIndex: "is_credit_memo",
|
||||
key: "is_credit_memo",
|
||||
sorter: (a, b) => a.is_credit_memo - b.is_credit_memo,
|
||||
@@ -88,8 +88,8 @@ export function InvoicesListPage({
|
||||
key: "actions",
|
||||
render: (text, record) => (
|
||||
<div>
|
||||
<Link to={`/manage/invoices?invoiceid=${record.id}`}>
|
||||
<Button>{t("invoices.actions.edit")}</Button>
|
||||
<Link to={`/manage/bills?billid=${record.id}`}>
|
||||
<Button>{t("bills.actions.edit")}</Button>
|
||||
</Link>
|
||||
<Button
|
||||
disabled={record.is_credit_memo}
|
||||
@@ -99,9 +99,9 @@ export function InvoicesListPage({
|
||||
context: {
|
||||
jobId: record.jobid,
|
||||
vendorId: record.vendorid,
|
||||
returnFromInvoice: record.id,
|
||||
returnFromBill: record.id,
|
||||
invoiceNumber: record.invoice_number,
|
||||
linesToOrder: record.invoicelines.map((i) => {
|
||||
linesToOrder: record.billlines.map((i) => {
|
||||
return {
|
||||
line_desc: i.line_desc,
|
||||
db_price: i.actual_price,
|
||||
@@ -115,7 +115,7 @@ export function InvoicesListPage({
|
||||
})
|
||||
}
|
||||
>
|
||||
{t("invoices.actions.return")}
|
||||
{t("bills.actions.return")}
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
@@ -132,9 +132,7 @@ export function InvoicesListPage({
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Typography.Title level={4}>
|
||||
{t("invoices.labels.invoices")}
|
||||
</Typography.Title>
|
||||
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
|
||||
<Table
|
||||
loading={loading}
|
||||
size="small"
|
||||
@@ -145,13 +143,13 @@ export function InvoicesListPage({
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setInvoiceEnterContext({
|
||||
setBillEnterContext({
|
||||
actions: { refetch: refetch },
|
||||
context: {},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("jobs.actions.postInvoices")}
|
||||
{t("jobs.actions.postbills")}
|
||||
</Button>
|
||||
<div className="imex-table-header__search">
|
||||
<Input.Search
|
||||
@@ -179,4 +177,4 @@ export function InvoicesListPage({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(null, mapDispatchToProps)(InvoicesListPage);
|
||||
export default connect(null, mapDispatchToProps)(BillsListPage);
|
||||
@@ -4,13 +4,13 @@ import { useQuery } from "react-apollo";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import InvoiceDetailEditContainer from "../../components/invoice-detail-edit/invoice-detail-edit.container";
|
||||
import { QUERY_ALL_INVOICES_PAGINATED } from "../../graphql/invoices.queries";
|
||||
import BillDetailEditContainer from "../../components/bill-detail-edit/bill-detail-edit.container";
|
||||
import { QUERY_ALL_BILLS_PAGINATED } from "../../graphql/bills.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import InvoicesPageComponent from "./invoices.page.component";
|
||||
import BillsPageComponent from "./bills.page.component";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
@@ -19,21 +19,21 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function InvoicesPageContainer({ setBreadcrumbs, setSelectedHeader }) {
|
||||
export function BillsPageContainer({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { page, sortcolumn, sortorder, search } = searchParams;
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.invoices-list");
|
||||
setSelectedHeader("invoices");
|
||||
document.title = t("titles.bills-list");
|
||||
setSelectedHeader("bills");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/invoices", label: t("titles.bc.invoices-list") },
|
||||
{ link: "/manage/bills", label: t("titles.bc.bills-list") },
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(
|
||||
QUERY_ALL_INVOICES_PAGINATED,
|
||||
QUERY_ALL_BILLS_PAGINATED,
|
||||
{
|
||||
variables: {
|
||||
search: search || "",
|
||||
@@ -54,18 +54,18 @@ export function InvoicesPageContainer({ setBreadcrumbs, setSelectedHeader }) {
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
return (
|
||||
<RbacWrapper action="invoices:list">
|
||||
<RbacWrapper action="bills:list">
|
||||
<div>
|
||||
<InvoicesPageComponent
|
||||
data={data ? data.search_invoices : []}
|
||||
<BillsPageComponent
|
||||
data={data ? data.search_bills : []}
|
||||
loading={loading}
|
||||
refetch={refetch}
|
||||
total={data ? data.search_invoices_aggregate.aggregate.count : 0}
|
||||
total={data ? data.search_bills_aggregate.aggregate.count : 0}
|
||||
/>
|
||||
|
||||
<InvoiceDetailEditContainer />
|
||||
<BillDetailEditContainer />
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
export default connect(null, mapDispatchToProps)(InvoicesPageContainer);
|
||||
export default connect(null, mapDispatchToProps)(BillsPageContainer);
|
||||
@@ -74,15 +74,13 @@ const ContractDetailPage = lazy(() =>
|
||||
const ContractsList = lazy(() =>
|
||||
import("../contracts/contracts.page.container")
|
||||
);
|
||||
const InvoicesListPage = lazy(() =>
|
||||
import("../invoices/invoices.page.container")
|
||||
);
|
||||
const BillsListPage = lazy(() => import("../bills/bills.page.container"));
|
||||
|
||||
const JobCostingModal = lazy(() =>
|
||||
import("../../components/job-costing-modal/job-costing-modal.container")
|
||||
);
|
||||
const EnterInvoiceModalContainer = lazy(() =>
|
||||
import("../../components/invoice-enter-modal/invoice-enter-modal.container")
|
||||
const BillEnterModalContainer = lazy(() =>
|
||||
import("../../components/bill-enter-modal/bill-enter-modal.container")
|
||||
);
|
||||
const TimeTicketModalContainer = lazy(() =>
|
||||
import("../../components/time-ticket-modal/time-ticket-modal.container")
|
||||
@@ -174,7 +172,7 @@ export function Manage({ match, conflict }) {
|
||||
<PaymentModalContainer />
|
||||
</Elements>
|
||||
<BreadCrumbs />
|
||||
<EnterInvoiceModalContainer />
|
||||
<BillEnterModalContainer />
|
||||
<JobCostingModal />
|
||||
<EmailOverlayContainer />
|
||||
<TimeTicketModalContainer />
|
||||
@@ -280,8 +278,8 @@ export function Manage({ match, conflict }) {
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/invoices`}
|
||||
component={InvoicesListPage}
|
||||
path={`${match.path}/bills`}
|
||||
component={BillsListPage}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
|
||||
@@ -10,7 +10,7 @@ const baseModal = {
|
||||
|
||||
const INITIAL_STATE = {
|
||||
jobLineEdit: { ...baseModal },
|
||||
invoiceEnter: { ...baseModal },
|
||||
billEnter: { ...baseModal },
|
||||
courtesyCarReturn: { ...baseModal },
|
||||
noteUpsert: { ...baseModal },
|
||||
schedule: { ...baseModal },
|
||||
|
||||
@@ -7,9 +7,9 @@ export const selectJobLineEditModal = createSelector(
|
||||
(modals) => modals.jobLineEdit
|
||||
);
|
||||
|
||||
export const selectInvoiceEnterModal = createSelector(
|
||||
export const selectBillEnterModal = createSelector(
|
||||
[selectModals],
|
||||
(modals) => modals.invoiceEnter
|
||||
(modals) => modals.billEnter
|
||||
);
|
||||
|
||||
export const selectCourtesyCarReturn = createSelector(
|
||||
|
||||
@@ -73,6 +73,76 @@
|
||||
"values": "Values"
|
||||
}
|
||||
},
|
||||
"billlines": {
|
||||
"actions": {
|
||||
"newline": "New Line"
|
||||
},
|
||||
"fields": {
|
||||
"actual": "Actual",
|
||||
"actual_cost": "Actual Cost",
|
||||
"cost_center": "Cost Center",
|
||||
"federal_tax_applicable": "Fed. Tax?",
|
||||
"jobline": "Job Line",
|
||||
"line_desc": "Line Description",
|
||||
"local_tax_applicable": "Loc. Tax?",
|
||||
"quantity": "Quantity",
|
||||
"retail": "Retail",
|
||||
"state_tax_applicable": "St. Tax?"
|
||||
},
|
||||
"labels": {
|
||||
"entered": "Entered",
|
||||
"other": "--Not On Estimate--",
|
||||
"reconciled": "Reconciled!",
|
||||
"unreconciled": "Unreconciled"
|
||||
}
|
||||
},
|
||||
"bills": {
|
||||
"actions": {
|
||||
"edit": "Edit",
|
||||
"receive": "Receive Part",
|
||||
"return": "Return Items"
|
||||
},
|
||||
"errors": {
|
||||
"creating": "Error adding bill.",
|
||||
"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.",
|
||||
"invalidvendor": "Not a valid vendor.",
|
||||
"validation": "Please ensure all fields are entered correctly. "
|
||||
},
|
||||
"fields": {
|
||||
"allpartslocation": "Parts Bin",
|
||||
"date": "Bill Date",
|
||||
"federal_tax_rate": "Federal Tax Rate",
|
||||
"invoice_number": "Invoice Number",
|
||||
"is_credit_memo": "Credit Memo?",
|
||||
"local_tax_rate": "Local Tax Rate",
|
||||
"ro_number": "RO Number",
|
||||
"state_tax_rate": "State Tax Rate",
|
||||
"total": "Bill Total",
|
||||
"vendor": "Vendor",
|
||||
"vendorname": "Vendor Name"
|
||||
},
|
||||
"labels": {
|
||||
"actions": "Actions",
|
||||
"bill_lines": "Bill Lines",
|
||||
"bill_total": "Bill Total Amount",
|
||||
"bills": "Bills",
|
||||
"discrepancy": "Discrepancy",
|
||||
"entered_total": "Total of Entered Lines",
|
||||
"enteringcreditmemo": "You are entering a credit memo. Please ensure you are also entering positive values.",
|
||||
"federal_tax": "Federal Tax",
|
||||
"local_tax": "Local Tax",
|
||||
"new": "New Bill",
|
||||
"noneselected": "No bill selected.",
|
||||
"retailtotal": "Retail Total of Bill (Ex. Taxes)",
|
||||
"state_tax": "State Tax",
|
||||
"subtotal": "Subtotal"
|
||||
},
|
||||
"successes": {
|
||||
"created": "Invoice added successfully."
|
||||
}
|
||||
},
|
||||
"bodyshop": {
|
||||
"actions": {
|
||||
"addbucket": "Add Bucket",
|
||||
@@ -91,6 +161,9 @@
|
||||
"address1": "Address 1",
|
||||
"address2": "Address 2",
|
||||
"appt_length": "Default Appointment Length",
|
||||
"bill_federal_tax_rate": "Bills - Federal Tax Rate %",
|
||||
"bill_local_tax_rate": "Bill - State Tax Rate %",
|
||||
"bill_state_tax_rate": "Bill - State Tax Rate %",
|
||||
"city": "City",
|
||||
"country": "Country",
|
||||
"dailybodytarget": "Scoreboard - Daily Body Target",
|
||||
@@ -99,9 +172,6 @@
|
||||
"enforce_class": "Enforce Class on Conversion?",
|
||||
"federal_tax_id": "Federal Tax ID (GST/HST)",
|
||||
"insurance_vendor_id": "Insurance Vendor ID",
|
||||
"invoice_federal_tax_rate": "Invoices - Federal Tax Rate %",
|
||||
"invoice_local_tax_rate": "Invoices - State Tax Rate %",
|
||||
"invoice_state_tax_rate": "Invoices - State Tax Rate %",
|
||||
"lastnumberworkingdays": "Scoreboard - Last Number of Working Days",
|
||||
"logo_img_path": "Shop Logo",
|
||||
"md_categories": "Categories",
|
||||
@@ -121,6 +191,11 @@
|
||||
"payments": "Accounting -> Payments",
|
||||
"receivables": "Accounting -> Receivables"
|
||||
},
|
||||
"bills": {
|
||||
"enter": "Bills -> Enter",
|
||||
"list": "Bills -> List",
|
||||
"view": "Bills -> View"
|
||||
},
|
||||
"contracts": {
|
||||
"create": "Contracts -> Create",
|
||||
"detail": "Contracts -> Detail",
|
||||
@@ -138,11 +213,6 @@
|
||||
"employees": {
|
||||
"page": "Employees -> List"
|
||||
},
|
||||
"invoices": {
|
||||
"enter": "Invoices -> Enter",
|
||||
"list": "Invoices -> List",
|
||||
"view": "Invoices -> View"
|
||||
},
|
||||
"jobs": {
|
||||
"available-list": "Jobs -> Available List",
|
||||
"close": "Jobs -> Close",
|
||||
@@ -614,76 +684,6 @@
|
||||
"rescuetitle": "Rescue Me!"
|
||||
}
|
||||
},
|
||||
"invoicelines": {
|
||||
"actions": {
|
||||
"newline": "New Line"
|
||||
},
|
||||
"fields": {
|
||||
"actual": "Actual",
|
||||
"actual_cost": "Actual Cost",
|
||||
"cost_center": "Cost Center",
|
||||
"federal_tax_applicable": "Fed. Tax?",
|
||||
"jobline": "Job Line",
|
||||
"line_desc": "Invoice Line Description",
|
||||
"local_tax_applicable": "Loc. Tax?",
|
||||
"quantity": "Quantity",
|
||||
"retail": "Retail",
|
||||
"state_tax_applicable": "St. Tax?"
|
||||
},
|
||||
"labels": {
|
||||
"entered": "Entered",
|
||||
"other": "--Not On Estimate--",
|
||||
"reconciled": "Reconciled!",
|
||||
"unreconciled": "Unreconciled"
|
||||
}
|
||||
},
|
||||
"invoices": {
|
||||
"actions": {
|
||||
"edit": "Edit",
|
||||
"receive": "Receive Part",
|
||||
"return": "Return Items"
|
||||
},
|
||||
"errors": {
|
||||
"creating": "Error adding invoice.",
|
||||
"exporting": "Error exporting invoice(s). {{error}}",
|
||||
"exporting-partner": "Unable to connect to ImEX Partner. Please ensure it is running and logged in.",
|
||||
"invalidro": "Not a valid RO.",
|
||||
"invalidvendor": "Not a valid vendor.",
|
||||
"validation": "Please ensure all fields are entered correctly. "
|
||||
},
|
||||
"fields": {
|
||||
"allpartslocation": "Parts Bin",
|
||||
"date": "Invoice Date",
|
||||
"federal_tax_rate": "Federal Tax Rate",
|
||||
"invoice_number": "Invoice Number",
|
||||
"is_credit_memo": "Credit Memo?",
|
||||
"local_tax_rate": "Local Tax Rate",
|
||||
"ro_number": "RO Number",
|
||||
"state_tax_rate": "State Tax Rate",
|
||||
"total": "Invoice Total",
|
||||
"vendor": "Vendor",
|
||||
"vendorname": "Vendor Name"
|
||||
},
|
||||
"labels": {
|
||||
"actions": "Actions",
|
||||
"discrepancy": "Discrepancy",
|
||||
"entered_total": "Total of Entered Lines",
|
||||
"enteringcreditmemo": "You are entering a credit memo. Please ensure you are also entering positive values.",
|
||||
"federal_tax": "Federal Tax",
|
||||
"invoice_lines": "Invoice Lines",
|
||||
"invoice_total": "Invoice Total Amount",
|
||||
"invoices": "Invoices",
|
||||
"local_tax": "Local Tax",
|
||||
"new": "New Invoice",
|
||||
"noneselected": "No invoice selected.",
|
||||
"retailtotal": "Retail Total of Invoices (Ex. Taxes)",
|
||||
"state_tax": "State Tax",
|
||||
"subtotal": "Subtotal"
|
||||
},
|
||||
"successes": {
|
||||
"created": "Invoice added successfully."
|
||||
}
|
||||
},
|
||||
"joblines": {
|
||||
"actions": {
|
||||
"new": "New Line"
|
||||
@@ -778,7 +778,7 @@
|
||||
"intake": "Intake",
|
||||
"manualnew": "Create New Job Manually",
|
||||
"mark": "Mark",
|
||||
"postInvoices": "Post Invoices",
|
||||
"postbills": "Post Bills",
|
||||
"printCenter": "Print Center",
|
||||
"recalculate": "Recalculate",
|
||||
"reconcile": "Reconcile",
|
||||
@@ -1071,17 +1071,17 @@
|
||||
"alljobs": "All Jobs",
|
||||
"allpayments": "All Payments",
|
||||
"availablejobs": "Available Jobs",
|
||||
"bills": "Bills",
|
||||
"courtesycars": "Courtesy Cars",
|
||||
"courtesycars-all": "All Courtesy Cars",
|
||||
"courtesycars-contracts": "Contracts",
|
||||
"courtesycars-newcontract": "New Contract",
|
||||
"customers": "Customers",
|
||||
"enterinvoices": "Enter Invoices",
|
||||
"enterbills": "Enter Bills",
|
||||
"enterpayment": "Enter Payments",
|
||||
"entertimeticket": "Enter Time Tickets",
|
||||
"export": "Export",
|
||||
"home": "Home",
|
||||
"invoices": "Invoices",
|
||||
"jobs": "Jobs",
|
||||
"owners": "Owners",
|
||||
"productionboard": "Production Board - Visual",
|
||||
@@ -1221,7 +1221,7 @@
|
||||
"actions": {
|
||||
"backordered": "Backordered",
|
||||
"receive": "Receive",
|
||||
"receiveinvoice": "Receive Invoice"
|
||||
"receivebill": "Receive Bill"
|
||||
},
|
||||
"errors": {
|
||||
"backordering": "Error backordering part {{message}}.",
|
||||
@@ -1451,13 +1451,13 @@
|
||||
"accounting-payments": "Payments",
|
||||
"accounting-receivables": "Receivables",
|
||||
"availablejobs": "Available Jobs",
|
||||
"bills-list": "Bills",
|
||||
"contracts": "Contracts",
|
||||
"contracts-create": "New Contract",
|
||||
"contracts-detail": "Contract #{{number}}",
|
||||
"courtesycars": "Courtesy Cars",
|
||||
"courtesycars-detail": "Courtesy Car {{number}}",
|
||||
"courtesycars-new": "New Courtesy Car",
|
||||
"invoices-list": "Invoices",
|
||||
"jobs": "Jobs",
|
||||
"jobs-active": "Active Jobs",
|
||||
"jobs-all": "All Jobs",
|
||||
@@ -1481,13 +1481,13 @@
|
||||
"vehicle-details": "Vehicle: {{vehicle}}",
|
||||
"vehicles": "Vehicles"
|
||||
},
|
||||
"bills-list": "Bills | $t(titles.app)",
|
||||
"contracts": "Courtesy Car Contracts | $t(titles.app)",
|
||||
"contracts-create": "New Contract | $t(titles.app)",
|
||||
"contracts-detail": "Contract {{id}} | $t(titles.app)",
|
||||
"courtesycars": "Courtesy Cars | $t(titles.app)",
|
||||
"courtesycars-create": "New Courtesy Car | $t(titles.app)",
|
||||
"courtesycars-detail": "Courtesy Car {{id}} | $t(titles.app)",
|
||||
"invoices-list": "Invoices | $t(titles.app)",
|
||||
"jobs": "Active Jobs | $t(titles.app)",
|
||||
"jobs-all": "All Jobs | $t(titles.app)",
|
||||
"jobs-close": "Close Job {{number}} | $t(titles.app)",
|
||||
|
||||
@@ -73,6 +73,76 @@
|
||||
"values": ""
|
||||
}
|
||||
},
|
||||
"billlines": {
|
||||
"actions": {
|
||||
"newline": ""
|
||||
},
|
||||
"fields": {
|
||||
"actual": "",
|
||||
"actual_cost": "",
|
||||
"cost_center": "",
|
||||
"federal_tax_applicable": "",
|
||||
"jobline": "",
|
||||
"line_desc": "",
|
||||
"local_tax_applicable": "",
|
||||
"quantity": "",
|
||||
"retail": "",
|
||||
"state_tax_applicable": ""
|
||||
},
|
||||
"labels": {
|
||||
"entered": "",
|
||||
"other": "",
|
||||
"reconciled": "",
|
||||
"unreconciled": ""
|
||||
}
|
||||
},
|
||||
"bills": {
|
||||
"actions": {
|
||||
"edit": "",
|
||||
"receive": "",
|
||||
"return": ""
|
||||
},
|
||||
"errors": {
|
||||
"creating": "",
|
||||
"exporting": "",
|
||||
"exporting-partner": "",
|
||||
"invalidro": "",
|
||||
"invalidvendor": "",
|
||||
"validation": ""
|
||||
},
|
||||
"fields": {
|
||||
"allpartslocation": "",
|
||||
"date": "",
|
||||
"federal_tax_rate": "",
|
||||
"invoice_number": "",
|
||||
"is_credit_memo": "",
|
||||
"local_tax_rate": "",
|
||||
"ro_number": "",
|
||||
"state_tax_rate": "",
|
||||
"total": "",
|
||||
"vendor": "",
|
||||
"vendorname": ""
|
||||
},
|
||||
"labels": {
|
||||
"actions": "",
|
||||
"bill_lines": "",
|
||||
"bill_total": "",
|
||||
"bills": "",
|
||||
"discrepancy": "",
|
||||
"entered_total": "",
|
||||
"enteringcreditmemo": "",
|
||||
"federal_tax": "",
|
||||
"local_tax": "",
|
||||
"new": "",
|
||||
"noneselected": "",
|
||||
"retailtotal": "",
|
||||
"state_tax": "",
|
||||
"subtotal": ""
|
||||
},
|
||||
"successes": {
|
||||
"created": ""
|
||||
}
|
||||
},
|
||||
"bodyshop": {
|
||||
"actions": {
|
||||
"addbucket": "",
|
||||
@@ -91,6 +161,9 @@
|
||||
"address1": "",
|
||||
"address2": "",
|
||||
"appt_length": "",
|
||||
"bill_federal_tax_rate": "",
|
||||
"bill_local_tax_rate": "",
|
||||
"bill_state_tax_rate": "",
|
||||
"city": "",
|
||||
"country": "",
|
||||
"dailybodytarget": "",
|
||||
@@ -99,9 +172,6 @@
|
||||
"enforce_class": "",
|
||||
"federal_tax_id": "",
|
||||
"insurance_vendor_id": "",
|
||||
"invoice_federal_tax_rate": "",
|
||||
"invoice_local_tax_rate": "",
|
||||
"invoice_state_tax_rate": "",
|
||||
"lastnumberworkingdays": "",
|
||||
"logo_img_path": "",
|
||||
"md_categories": "",
|
||||
@@ -121,6 +191,11 @@
|
||||
"payments": "",
|
||||
"receivables": ""
|
||||
},
|
||||
"bills": {
|
||||
"enter": "",
|
||||
"list": "",
|
||||
"view": ""
|
||||
},
|
||||
"contracts": {
|
||||
"create": "",
|
||||
"detail": "",
|
||||
@@ -138,11 +213,6 @@
|
||||
"employees": {
|
||||
"page": ""
|
||||
},
|
||||
"invoices": {
|
||||
"enter": "",
|
||||
"list": "",
|
||||
"view": ""
|
||||
},
|
||||
"jobs": {
|
||||
"available-list": "",
|
||||
"close": "",
|
||||
@@ -614,76 +684,6 @@
|
||||
"rescuetitle": ""
|
||||
}
|
||||
},
|
||||
"invoicelines": {
|
||||
"actions": {
|
||||
"newline": ""
|
||||
},
|
||||
"fields": {
|
||||
"actual": "",
|
||||
"actual_cost": "",
|
||||
"cost_center": "",
|
||||
"federal_tax_applicable": "",
|
||||
"jobline": "",
|
||||
"line_desc": "",
|
||||
"local_tax_applicable": "",
|
||||
"quantity": "",
|
||||
"retail": "",
|
||||
"state_tax_applicable": ""
|
||||
},
|
||||
"labels": {
|
||||
"entered": "",
|
||||
"other": "",
|
||||
"reconciled": "",
|
||||
"unreconciled": ""
|
||||
}
|
||||
},
|
||||
"invoices": {
|
||||
"actions": {
|
||||
"edit": "",
|
||||
"receive": "",
|
||||
"return": ""
|
||||
},
|
||||
"errors": {
|
||||
"creating": "",
|
||||
"exporting": "",
|
||||
"exporting-partner": "",
|
||||
"invalidro": "",
|
||||
"invalidvendor": "",
|
||||
"validation": ""
|
||||
},
|
||||
"fields": {
|
||||
"allpartslocation": "",
|
||||
"date": "",
|
||||
"federal_tax_rate": "",
|
||||
"invoice_number": "",
|
||||
"is_credit_memo": "",
|
||||
"local_tax_rate": "",
|
||||
"ro_number": "",
|
||||
"state_tax_rate": "",
|
||||
"total": "",
|
||||
"vendor": "",
|
||||
"vendorname": ""
|
||||
},
|
||||
"labels": {
|
||||
"actions": "",
|
||||
"discrepancy": "",
|
||||
"entered_total": "",
|
||||
"enteringcreditmemo": "",
|
||||
"federal_tax": "",
|
||||
"invoice_lines": "",
|
||||
"invoice_total": "",
|
||||
"invoices": "",
|
||||
"local_tax": "",
|
||||
"new": "",
|
||||
"noneselected": "",
|
||||
"retailtotal": "",
|
||||
"state_tax": "",
|
||||
"subtotal": ""
|
||||
},
|
||||
"successes": {
|
||||
"created": ""
|
||||
}
|
||||
},
|
||||
"joblines": {
|
||||
"actions": {
|
||||
"new": ""
|
||||
@@ -778,7 +778,7 @@
|
||||
"intake": "",
|
||||
"manualnew": "",
|
||||
"mark": "",
|
||||
"postInvoices": "Contabilizar facturas",
|
||||
"postbills": "Contabilizar facturas",
|
||||
"printCenter": "Centro de impresión",
|
||||
"recalculate": "",
|
||||
"reconcile": "",
|
||||
@@ -1071,17 +1071,17 @@
|
||||
"alljobs": "",
|
||||
"allpayments": "",
|
||||
"availablejobs": "Trabajos disponibles",
|
||||
"bills": "",
|
||||
"courtesycars": "",
|
||||
"courtesycars-all": "",
|
||||
"courtesycars-contracts": "",
|
||||
"courtesycars-newcontract": "",
|
||||
"customers": "Clientes",
|
||||
"enterinvoices": "",
|
||||
"enterbills": "",
|
||||
"enterpayment": "",
|
||||
"entertimeticket": "",
|
||||
"export": "",
|
||||
"home": "Casa",
|
||||
"invoices": "",
|
||||
"jobs": "Trabajos",
|
||||
"owners": "propietarios",
|
||||
"productionboard": "",
|
||||
@@ -1221,7 +1221,7 @@
|
||||
"actions": {
|
||||
"backordered": "",
|
||||
"receive": "",
|
||||
"receiveinvoice": ""
|
||||
"receivebill": ""
|
||||
},
|
||||
"errors": {
|
||||
"backordering": "",
|
||||
@@ -1451,13 +1451,13 @@
|
||||
"accounting-payments": "",
|
||||
"accounting-receivables": "",
|
||||
"availablejobs": "",
|
||||
"bills-list": "",
|
||||
"contracts": "",
|
||||
"contracts-create": "",
|
||||
"contracts-detail": "",
|
||||
"courtesycars": "",
|
||||
"courtesycars-detail": "",
|
||||
"courtesycars-new": "",
|
||||
"invoices-list": "",
|
||||
"jobs": "",
|
||||
"jobs-active": "",
|
||||
"jobs-all": "",
|
||||
@@ -1481,13 +1481,13 @@
|
||||
"vehicle-details": "",
|
||||
"vehicles": ""
|
||||
},
|
||||
"bills-list": "",
|
||||
"contracts": "",
|
||||
"contracts-create": "",
|
||||
"contracts-detail": "",
|
||||
"courtesycars": "",
|
||||
"courtesycars-create": "",
|
||||
"courtesycars-detail": "",
|
||||
"invoices-list": "",
|
||||
"jobs": "Todos los trabajos | $t(titles.app)",
|
||||
"jobs-all": "",
|
||||
"jobs-close": "",
|
||||
|
||||
@@ -73,6 +73,76 @@
|
||||
"values": ""
|
||||
}
|
||||
},
|
||||
"billlines": {
|
||||
"actions": {
|
||||
"newline": ""
|
||||
},
|
||||
"fields": {
|
||||
"actual": "",
|
||||
"actual_cost": "",
|
||||
"cost_center": "",
|
||||
"federal_tax_applicable": "",
|
||||
"jobline": "",
|
||||
"line_desc": "",
|
||||
"local_tax_applicable": "",
|
||||
"quantity": "",
|
||||
"retail": "",
|
||||
"state_tax_applicable": ""
|
||||
},
|
||||
"labels": {
|
||||
"entered": "",
|
||||
"other": "",
|
||||
"reconciled": "",
|
||||
"unreconciled": ""
|
||||
}
|
||||
},
|
||||
"bills": {
|
||||
"actions": {
|
||||
"edit": "",
|
||||
"receive": "",
|
||||
"return": ""
|
||||
},
|
||||
"errors": {
|
||||
"creating": "",
|
||||
"exporting": "",
|
||||
"exporting-partner": "",
|
||||
"invalidro": "",
|
||||
"invalidvendor": "",
|
||||
"validation": ""
|
||||
},
|
||||
"fields": {
|
||||
"allpartslocation": "",
|
||||
"date": "",
|
||||
"federal_tax_rate": "",
|
||||
"invoice_number": "",
|
||||
"is_credit_memo": "",
|
||||
"local_tax_rate": "",
|
||||
"ro_number": "",
|
||||
"state_tax_rate": "",
|
||||
"total": "",
|
||||
"vendor": "",
|
||||
"vendorname": ""
|
||||
},
|
||||
"labels": {
|
||||
"actions": "",
|
||||
"bill_lines": "",
|
||||
"bill_total": "",
|
||||
"bills": "",
|
||||
"discrepancy": "",
|
||||
"entered_total": "",
|
||||
"enteringcreditmemo": "",
|
||||
"federal_tax": "",
|
||||
"local_tax": "",
|
||||
"new": "",
|
||||
"noneselected": "",
|
||||
"retailtotal": "",
|
||||
"state_tax": "",
|
||||
"subtotal": ""
|
||||
},
|
||||
"successes": {
|
||||
"created": ""
|
||||
}
|
||||
},
|
||||
"bodyshop": {
|
||||
"actions": {
|
||||
"addbucket": "",
|
||||
@@ -91,6 +161,9 @@
|
||||
"address1": "",
|
||||
"address2": "",
|
||||
"appt_length": "",
|
||||
"bill_federal_tax_rate": "",
|
||||
"bill_local_tax_rate": "",
|
||||
"bill_state_tax_rate": "",
|
||||
"city": "",
|
||||
"country": "",
|
||||
"dailybodytarget": "",
|
||||
@@ -99,9 +172,6 @@
|
||||
"enforce_class": "",
|
||||
"federal_tax_id": "",
|
||||
"insurance_vendor_id": "",
|
||||
"invoice_federal_tax_rate": "",
|
||||
"invoice_local_tax_rate": "",
|
||||
"invoice_state_tax_rate": "",
|
||||
"lastnumberworkingdays": "",
|
||||
"logo_img_path": "",
|
||||
"md_categories": "",
|
||||
@@ -121,6 +191,11 @@
|
||||
"payments": "",
|
||||
"receivables": ""
|
||||
},
|
||||
"bills": {
|
||||
"enter": "",
|
||||
"list": "",
|
||||
"view": ""
|
||||
},
|
||||
"contracts": {
|
||||
"create": "",
|
||||
"detail": "",
|
||||
@@ -138,11 +213,6 @@
|
||||
"employees": {
|
||||
"page": ""
|
||||
},
|
||||
"invoices": {
|
||||
"enter": "",
|
||||
"list": "",
|
||||
"view": ""
|
||||
},
|
||||
"jobs": {
|
||||
"available-list": "",
|
||||
"close": "",
|
||||
@@ -614,76 +684,6 @@
|
||||
"rescuetitle": ""
|
||||
}
|
||||
},
|
||||
"invoicelines": {
|
||||
"actions": {
|
||||
"newline": ""
|
||||
},
|
||||
"fields": {
|
||||
"actual": "",
|
||||
"actual_cost": "",
|
||||
"cost_center": "",
|
||||
"federal_tax_applicable": "",
|
||||
"jobline": "",
|
||||
"line_desc": "",
|
||||
"local_tax_applicable": "",
|
||||
"quantity": "",
|
||||
"retail": "",
|
||||
"state_tax_applicable": ""
|
||||
},
|
||||
"labels": {
|
||||
"entered": "",
|
||||
"other": "",
|
||||
"reconciled": "",
|
||||
"unreconciled": ""
|
||||
}
|
||||
},
|
||||
"invoices": {
|
||||
"actions": {
|
||||
"edit": "",
|
||||
"receive": "",
|
||||
"return": ""
|
||||
},
|
||||
"errors": {
|
||||
"creating": "",
|
||||
"exporting": "",
|
||||
"exporting-partner": "",
|
||||
"invalidro": "",
|
||||
"invalidvendor": "",
|
||||
"validation": ""
|
||||
},
|
||||
"fields": {
|
||||
"allpartslocation": "",
|
||||
"date": "",
|
||||
"federal_tax_rate": "",
|
||||
"invoice_number": "",
|
||||
"is_credit_memo": "",
|
||||
"local_tax_rate": "",
|
||||
"ro_number": "",
|
||||
"state_tax_rate": "",
|
||||
"total": "",
|
||||
"vendor": "",
|
||||
"vendorname": ""
|
||||
},
|
||||
"labels": {
|
||||
"actions": "",
|
||||
"discrepancy": "",
|
||||
"entered_total": "",
|
||||
"enteringcreditmemo": "",
|
||||
"federal_tax": "",
|
||||
"invoice_lines": "",
|
||||
"invoice_total": "",
|
||||
"invoices": "",
|
||||
"local_tax": "",
|
||||
"new": "",
|
||||
"noneselected": "",
|
||||
"retailtotal": "",
|
||||
"state_tax": "",
|
||||
"subtotal": ""
|
||||
},
|
||||
"successes": {
|
||||
"created": ""
|
||||
}
|
||||
},
|
||||
"joblines": {
|
||||
"actions": {
|
||||
"new": ""
|
||||
@@ -778,7 +778,7 @@
|
||||
"intake": "",
|
||||
"manualnew": "",
|
||||
"mark": "",
|
||||
"postInvoices": "Poster des factures",
|
||||
"postbills": "Poster des factures",
|
||||
"printCenter": "Centre d'impression",
|
||||
"recalculate": "",
|
||||
"reconcile": "",
|
||||
@@ -1071,17 +1071,17 @@
|
||||
"alljobs": "",
|
||||
"allpayments": "",
|
||||
"availablejobs": "Emplois disponibles",
|
||||
"bills": "",
|
||||
"courtesycars": "",
|
||||
"courtesycars-all": "",
|
||||
"courtesycars-contracts": "",
|
||||
"courtesycars-newcontract": "",
|
||||
"customers": "Les clients",
|
||||
"enterinvoices": "",
|
||||
"enterbills": "",
|
||||
"enterpayment": "",
|
||||
"entertimeticket": "",
|
||||
"export": "",
|
||||
"home": "Accueil",
|
||||
"invoices": "",
|
||||
"jobs": "Emplois",
|
||||
"owners": "Propriétaires",
|
||||
"productionboard": "",
|
||||
@@ -1221,7 +1221,7 @@
|
||||
"actions": {
|
||||
"backordered": "",
|
||||
"receive": "",
|
||||
"receiveinvoice": ""
|
||||
"receivebill": ""
|
||||
},
|
||||
"errors": {
|
||||
"backordering": "",
|
||||
@@ -1451,13 +1451,13 @@
|
||||
"accounting-payments": "",
|
||||
"accounting-receivables": "",
|
||||
"availablejobs": "",
|
||||
"bills-list": "",
|
||||
"contracts": "",
|
||||
"contracts-create": "",
|
||||
"contracts-detail": "",
|
||||
"courtesycars": "",
|
||||
"courtesycars-detail": "",
|
||||
"courtesycars-new": "",
|
||||
"invoices-list": "",
|
||||
"jobs": "",
|
||||
"jobs-active": "",
|
||||
"jobs-all": "",
|
||||
@@ -1481,13 +1481,13 @@
|
||||
"vehicle-details": "",
|
||||
"vehicles": ""
|
||||
},
|
||||
"bills-list": "",
|
||||
"contracts": "",
|
||||
"contracts-create": "",
|
||||
"contracts-detail": "",
|
||||
"courtesycars": "",
|
||||
"courtesycars-create": "",
|
||||
"courtesycars-detail": "",
|
||||
"invoices-list": "",
|
||||
"jobs": "Tous les emplois | $t(titles.app)",
|
||||
"jobs-all": "",
|
||||
"jobs-close": "",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: true
|
||||
read_only: false
|
||||
sql: DROP FUNCTION IF EXISTS search_invoices;
|
||||
type: run_sql
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: alter table "public"."bills" rename to "invoices";
|
||||
type: run_sql
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: alter table "public"."invoices" rename to "bills";
|
||||
type: run_sql
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: alter table "public"."billlines" rename to "invoicelines";
|
||||
type: run_sql
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: alter table "public"."invoicelines" rename to "billlines";
|
||||
type: run_sql
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: alter table "public"."billlines" rename column "billid" to "invoiceid";
|
||||
type: run_sql
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: alter table "public"."billlines" rename column "invoiceid" to "billid";
|
||||
type: run_sql
|
||||
@@ -0,0 +1,7 @@
|
||||
- args:
|
||||
name: bill
|
||||
new_name: invoice
|
||||
table:
|
||||
name: billlines
|
||||
schema: public
|
||||
type: rename_relationship
|
||||
@@ -0,0 +1,7 @@
|
||||
- args:
|
||||
name: invoice
|
||||
new_name: bill
|
||||
table:
|
||||
name: billlines
|
||||
schema: public
|
||||
type: rename_relationship
|
||||
@@ -0,0 +1,7 @@
|
||||
- args:
|
||||
name: bill
|
||||
new_name: invoice
|
||||
table:
|
||||
name: jobs
|
||||
schema: public
|
||||
type: rename_relationship
|
||||
@@ -0,0 +1,7 @@
|
||||
- args:
|
||||
name: invoice
|
||||
new_name: bill
|
||||
table:
|
||||
name: jobs
|
||||
schema: public
|
||||
type: rename_relationship
|
||||
@@ -0,0 +1,7 @@
|
||||
- args:
|
||||
name: bills
|
||||
new_name: invoices
|
||||
table:
|
||||
name: jobs
|
||||
schema: public
|
||||
type: rename_relationship
|
||||
@@ -0,0 +1,7 @@
|
||||
- args:
|
||||
name: invoices
|
||||
new_name: bills
|
||||
table:
|
||||
name: jobs
|
||||
schema: public
|
||||
type: rename_relationship
|
||||
@@ -0,0 +1,7 @@
|
||||
- args:
|
||||
name: bill
|
||||
new_name: invoice
|
||||
table:
|
||||
name: documents
|
||||
schema: public
|
||||
type: rename_relationship
|
||||
@@ -0,0 +1,7 @@
|
||||
- args:
|
||||
name: invoice
|
||||
new_name: bill
|
||||
table:
|
||||
name: documents
|
||||
schema: public
|
||||
type: rename_relationship
|
||||
@@ -0,0 +1,7 @@
|
||||
- args:
|
||||
name: bill
|
||||
new_name: invoice
|
||||
table:
|
||||
name: parts_orders
|
||||
schema: public
|
||||
type: rename_relationship
|
||||
@@ -0,0 +1,7 @@
|
||||
- args:
|
||||
name: invoice
|
||||
new_name: bill
|
||||
table:
|
||||
name: parts_orders
|
||||
schema: public
|
||||
type: rename_relationship
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -0,0 +1,8 @@
|
||||
- args:
|
||||
cascade: true
|
||||
read_only: false
|
||||
sql: "CREATE OR REPLACE FUNCTION public.search_bills(search text)\n RETURNS SETOF
|
||||
bills\n LANGUAGE plpgsql\n STABLE\nAS $function$\n\nBEGIN\n if search = ''
|
||||
then\n return query select * from bills ;\n else \n return query SELECT\n
|
||||
\ *\nFROM\n bills\nWHERE\n search <% (invoice_number);\n end if;\n\n\tEND\n$function$;"
|
||||
type: run_sql
|
||||
@@ -0,0 +1,7 @@
|
||||
- args:
|
||||
name: billlines
|
||||
new_name: invoicelines
|
||||
table:
|
||||
name: bills
|
||||
schema: public
|
||||
type: rename_relationship
|
||||
@@ -0,0 +1,7 @@
|
||||
- args:
|
||||
name: invoicelines
|
||||
new_name: billlines
|
||||
table:
|
||||
name: bills
|
||||
schema: public
|
||||
type: rename_relationship
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -0,0 +1,8 @@
|
||||
- args:
|
||||
cascade: true
|
||||
read_only: false
|
||||
sql: "CREATE OR REPLACE FUNCTION public.search_bills(search text)\n RETURNS SETOF
|
||||
bills\n LANGUAGE plpgsql\n STABLE\nAS $function$\n\nBEGIN\n if search = ''
|
||||
then\n return query select * from bills ;\n else \n return query SELECT\n
|
||||
\ *\nFROM\n bills\nWHERE\n search <% (invoice_number);\n end if;\n\n\tEND\n$function$;"
|
||||
type: run_sql
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
12
hasura/migrations/1600814334466_run_sql_migration/up.yaml
Normal file
12
hasura/migrations/1600814334466_run_sql_migration/up.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
- args:
|
||||
cascade: true
|
||||
read_only: false
|
||||
sql: "CREATE OR REPLACE FUNCTION public.search_bills(search text)\n RETURNS SETOF
|
||||
bills\n LANGUAGE plpgsql\n STABLE\nAS $function$\n\nBEGIN\n if search = ''
|
||||
then\n return query select * from bills ;\n else \n return query SELECT\n
|
||||
\ *\nFROM\n bills\nWHERE\n search <% (invoice_number);\n end if;\n\n\tEND\n$function$;"
|
||||
type: run_sql
|
||||
- args:
|
||||
name: search_bills
|
||||
schema: public
|
||||
type: track_function
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: alter table "public"."documents" rename column "billid" to "invoiceid";
|
||||
type: run_sql
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: alter table "public"."documents" rename column "invoiceid" to "billid";
|
||||
type: run_sql
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: alter table "public"."bodyshops" rename column "bill_tax_rates" to "invoice_tax_rates";
|
||||
type: run_sql
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: alter table "public"."bodyshops" rename column "invoice_tax_rates" to "bill_tax_rates";
|
||||
type: run_sql
|
||||
@@ -348,6 +348,227 @@ tables:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
- table:
|
||||
schema: public
|
||||
name: billlines
|
||||
object_relationships:
|
||||
- name: bill
|
||||
using:
|
||||
foreign_key_constraint_on: billid
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
bill:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- actual_cost
|
||||
- actual_price
|
||||
- applicable_taxes
|
||||
- cost_center
|
||||
- created_at
|
||||
- id
|
||||
- billid
|
||||
- joblineid
|
||||
- line_desc
|
||||
- quantity
|
||||
- updated_at
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- actual_cost
|
||||
- actual_price
|
||||
- applicable_taxes
|
||||
- cost_center
|
||||
- created_at
|
||||
- id
|
||||
- billid
|
||||
- joblineid
|
||||
- line_desc
|
||||
- quantity
|
||||
- updated_at
|
||||
filter:
|
||||
bill:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- actual_cost
|
||||
- actual_price
|
||||
- applicable_taxes
|
||||
- cost_center
|
||||
- created_at
|
||||
- id
|
||||
- billid
|
||||
- joblineid
|
||||
- line_desc
|
||||
- quantity
|
||||
- updated_at
|
||||
filter:
|
||||
bill:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
check: null
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
bill:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
- table:
|
||||
schema: public
|
||||
name: bills
|
||||
object_relationships:
|
||||
- name: job
|
||||
using:
|
||||
foreign_key_constraint_on: jobid
|
||||
- name: vendor
|
||||
using:
|
||||
foreign_key_constraint_on: vendorid
|
||||
array_relationships:
|
||||
- name: billlines
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: billid
|
||||
table:
|
||||
schema: public
|
||||
name: billlines
|
||||
- name: documents
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: billid
|
||||
table:
|
||||
schema: public
|
||||
name: documents
|
||||
- name: parts_orders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: returnfrominvoice
|
||||
table:
|
||||
schema: public
|
||||
name: parts_orders
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- created_at
|
||||
- date
|
||||
- due_date
|
||||
- exported
|
||||
- exported_at
|
||||
- federal_tax_rate
|
||||
- id
|
||||
- invoice_number
|
||||
- is_credit_memo
|
||||
- jobid
|
||||
- local_tax_rate
|
||||
- state_tax_rate
|
||||
- total
|
||||
- updated_at
|
||||
- vendorid
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- created_at
|
||||
- date
|
||||
- due_date
|
||||
- exported
|
||||
- exported_at
|
||||
- federal_tax_rate
|
||||
- id
|
||||
- invoice_number
|
||||
- is_credit_memo
|
||||
- jobid
|
||||
- local_tax_rate
|
||||
- state_tax_rate
|
||||
- total
|
||||
- updated_at
|
||||
- vendorid
|
||||
filter:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
allow_aggregations: true
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- created_at
|
||||
- date
|
||||
- due_date
|
||||
- exported
|
||||
- exported_at
|
||||
- federal_tax_rate
|
||||
- id
|
||||
- invoice_number
|
||||
- is_credit_memo
|
||||
- jobid
|
||||
- local_tax_rate
|
||||
- state_tax_rate
|
||||
- total
|
||||
- updated_at
|
||||
- vendorid
|
||||
filter:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
check: null
|
||||
- table:
|
||||
schema: public
|
||||
name: bodyshops
|
||||
@@ -483,7 +704,7 @@ tables:
|
||||
- inhousevendorid
|
||||
- insurance_vendor_id
|
||||
- intakechecklist
|
||||
- invoice_tax_rates
|
||||
- bill_tax_rates
|
||||
- logo_img_path
|
||||
- md_categories
|
||||
- md_classes
|
||||
@@ -541,7 +762,7 @@ tables:
|
||||
- inhousevendorid
|
||||
- insurance_vendor_id
|
||||
- intakechecklist
|
||||
- invoice_tax_rates
|
||||
- bill_tax_rates
|
||||
- logo_img_path
|
||||
- md_categories
|
||||
- md_classes
|
||||
@@ -1170,9 +1391,9 @@ tables:
|
||||
schema: public
|
||||
name: documents
|
||||
object_relationships:
|
||||
- name: invoice
|
||||
- name: bill
|
||||
using:
|
||||
foreign_key_constraint_on: invoiceid
|
||||
foreign_key_constraint_on: billid
|
||||
- name: job
|
||||
using:
|
||||
foreign_key_constraint_on: jobid
|
||||
@@ -1197,7 +1418,7 @@ tables:
|
||||
- jobid
|
||||
- name
|
||||
- key
|
||||
- invoiceid
|
||||
- billid
|
||||
- type
|
||||
select_permissions:
|
||||
- role: user
|
||||
@@ -1210,7 +1431,7 @@ tables:
|
||||
- created_at
|
||||
- updated_at
|
||||
- id
|
||||
- invoiceid
|
||||
- billid
|
||||
- jobid
|
||||
filter:
|
||||
job:
|
||||
@@ -1393,227 +1614,6 @@ tables:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
- table:
|
||||
schema: public
|
||||
name: invoicelines
|
||||
object_relationships:
|
||||
- name: invoice
|
||||
using:
|
||||
foreign_key_constraint_on: invoiceid
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
invoice:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- actual_cost
|
||||
- actual_price
|
||||
- applicable_taxes
|
||||
- cost_center
|
||||
- created_at
|
||||
- id
|
||||
- invoiceid
|
||||
- joblineid
|
||||
- line_desc
|
||||
- quantity
|
||||
- updated_at
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- actual_cost
|
||||
- actual_price
|
||||
- applicable_taxes
|
||||
- cost_center
|
||||
- created_at
|
||||
- id
|
||||
- invoiceid
|
||||
- joblineid
|
||||
- line_desc
|
||||
- quantity
|
||||
- updated_at
|
||||
filter:
|
||||
invoice:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- actual_cost
|
||||
- actual_price
|
||||
- applicable_taxes
|
||||
- cost_center
|
||||
- created_at
|
||||
- id
|
||||
- invoiceid
|
||||
- joblineid
|
||||
- line_desc
|
||||
- quantity
|
||||
- updated_at
|
||||
filter:
|
||||
invoice:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
check: null
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
invoice:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
- table:
|
||||
schema: public
|
||||
name: invoices
|
||||
object_relationships:
|
||||
- name: job
|
||||
using:
|
||||
foreign_key_constraint_on: jobid
|
||||
- name: vendor
|
||||
using:
|
||||
foreign_key_constraint_on: vendorid
|
||||
array_relationships:
|
||||
- name: documents
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: invoiceid
|
||||
table:
|
||||
schema: public
|
||||
name: documents
|
||||
- name: invoicelines
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: invoiceid
|
||||
table:
|
||||
schema: public
|
||||
name: invoicelines
|
||||
- name: parts_orders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: returnfrominvoice
|
||||
table:
|
||||
schema: public
|
||||
name: parts_orders
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- created_at
|
||||
- date
|
||||
- due_date
|
||||
- exported
|
||||
- exported_at
|
||||
- federal_tax_rate
|
||||
- id
|
||||
- invoice_number
|
||||
- is_credit_memo
|
||||
- jobid
|
||||
- local_tax_rate
|
||||
- state_tax_rate
|
||||
- total
|
||||
- updated_at
|
||||
- vendorid
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- created_at
|
||||
- date
|
||||
- due_date
|
||||
- exported
|
||||
- exported_at
|
||||
- federal_tax_rate
|
||||
- id
|
||||
- invoice_number
|
||||
- is_credit_memo
|
||||
- jobid
|
||||
- local_tax_rate
|
||||
- state_tax_rate
|
||||
- total
|
||||
- updated_at
|
||||
- vendorid
|
||||
filter:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
allow_aggregations: true
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- created_at
|
||||
- date
|
||||
- due_date
|
||||
- exported
|
||||
- exported_at
|
||||
- federal_tax_rate
|
||||
- id
|
||||
- invoice_number
|
||||
- is_credit_memo
|
||||
- jobid
|
||||
- local_tax_rate
|
||||
- state_tax_rate
|
||||
- total
|
||||
- updated_at
|
||||
- vendorid
|
||||
filter:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
check: null
|
||||
- table:
|
||||
schema: public
|
||||
name: job_conversations
|
||||
@@ -1961,6 +1961,14 @@ tables:
|
||||
schema: public
|
||||
name: jobs
|
||||
object_relationships:
|
||||
- name: bill
|
||||
using:
|
||||
manual_configuration:
|
||||
remote_table:
|
||||
schema: public
|
||||
name: bills
|
||||
column_mapping:
|
||||
id: jobid
|
||||
- name: bodyshop
|
||||
using:
|
||||
foreign_key_constraint_on: shopid
|
||||
@@ -1973,14 +1981,6 @@ tables:
|
||||
- name: employee_refinish_rel
|
||||
using:
|
||||
foreign_key_constraint_on: employee_refinish
|
||||
- name: invoice
|
||||
using:
|
||||
manual_configuration:
|
||||
remote_table:
|
||||
schema: public
|
||||
name: invoices
|
||||
column_mapping:
|
||||
id: jobid
|
||||
- name: owner
|
||||
using:
|
||||
foreign_key_constraint_on: ownerid
|
||||
@@ -2002,6 +2002,13 @@ tables:
|
||||
table:
|
||||
schema: public
|
||||
name: available_jobs
|
||||
- name: bills
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: jobid
|
||||
table:
|
||||
schema: public
|
||||
name: bills
|
||||
- name: cccontracts
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
@@ -2023,13 +2030,6 @@ tables:
|
||||
table:
|
||||
schema: public
|
||||
name: documents
|
||||
- name: invoices
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: jobid
|
||||
table:
|
||||
schema: public
|
||||
name: invoices
|
||||
- name: job_conversations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
@@ -3298,7 +3298,7 @@ tables:
|
||||
schema: public
|
||||
name: parts_orders
|
||||
object_relationships:
|
||||
- name: invoice
|
||||
- name: bill
|
||||
using:
|
||||
foreign_key_constraint_on: returnfrominvoice
|
||||
- name: job
|
||||
@@ -4019,7 +4019,7 @@ tables:
|
||||
column: vendorid
|
||||
table:
|
||||
schema: public
|
||||
name: invoices
|
||||
name: bills
|
||||
- name: parts_orders
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
@@ -4142,7 +4142,7 @@ tables:
|
||||
functions:
|
||||
- function:
|
||||
schema: public
|
||||
name: search_invoices
|
||||
name: search_bills
|
||||
- function:
|
||||
schema: public
|
||||
name: search_jobs
|
||||
|
||||
@@ -14,7 +14,7 @@ require("dotenv").config({
|
||||
|
||||
exports.default = async (req, res) => {
|
||||
const BearerToken = req.headers.authorization;
|
||||
const { invoices: invoicesToQuery } = req.body;
|
||||
const { bills: billsToQuery } = req.body;
|
||||
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
@@ -25,13 +25,13 @@ exports.default = async (req, res) => {
|
||||
try {
|
||||
const result = await client
|
||||
.setHeaders({ Authorization: BearerToken })
|
||||
.request(queries.QUERY_INVOICES_FOR_PAYABLES_EXPORT, {
|
||||
invoices: invoicesToQuery,
|
||||
.request(queries.QUERY_BILLS_FOR_PAYABLES_EXPORT, {
|
||||
bills: billsToQuery,
|
||||
});
|
||||
const { invoices } = result;
|
||||
const { bills } = result;
|
||||
|
||||
const QbXmlToExecute = [];
|
||||
invoices.map((i) => {
|
||||
bills.map((i) => {
|
||||
QbXmlToExecute.push({
|
||||
id: i.id,
|
||||
okStatusCodes: ["0"],
|
||||
@@ -48,27 +48,24 @@ exports.default = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
const generateBill = (invoice) => {
|
||||
const generateBill = (bill) => {
|
||||
const billQbxmlObj = {
|
||||
QBXML: {
|
||||
QBXMLMsgsRq: {
|
||||
"@onError": "continueOnError",
|
||||
[`${invoice.is_credit_memo ? "VendorCreditAddRq" : "BillAddRq"}`]: {
|
||||
[`${invoice.is_credit_memo ? "VendorCreditAdd" : "BillAdd"}`]: {
|
||||
[`${bill.is_credit_memo ? "VendorCreditAddRq" : "BillAddRq"}`]: {
|
||||
[`${bill.is_credit_memo ? "VendorCreditAdd" : "BillAdd"}`]: {
|
||||
VendorRef: {
|
||||
FullName: invoice.vendor.name,
|
||||
FullName: bill.vendor.name,
|
||||
},
|
||||
TxnDate: invoice.date,
|
||||
DueDate: invoice.due_date,
|
||||
RefNumber: invoice.invoice_number,
|
||||
Memo: `RO ${invoice.job.ro_number || ""} OWNER ${
|
||||
invoice.job.ownr_fn || ""
|
||||
} ${invoice.job.ownr_ln || ""} ${invoice.job.ownr_co_nm || ""}`,
|
||||
ExpenseLineAdd: invoice.invoicelines.map((il) =>
|
||||
generateBillLine(
|
||||
il,
|
||||
invoice.job.bodyshop.md_responsibility_centers
|
||||
)
|
||||
TxnDate: bill.date,
|
||||
DueDate: bill.due_date,
|
||||
RefNumber: bill.bill_number,
|
||||
Memo: `RO ${bill.job.ro_number || ""} OWNER ${
|
||||
bill.job.ownr_fn || ""
|
||||
} ${bill.job.ownr_ln || ""} ${bill.job.ownr_co_nm || ""}`,
|
||||
ExpenseLineAdd: bill.billlines.map((il) =>
|
||||
generateBillLine(il, bill.job.bodyshop.md_responsibility_centers)
|
||||
),
|
||||
},
|
||||
},
|
||||
@@ -90,15 +87,15 @@ const generateBill = (invoice) => {
|
||||
return billQbxml_Full;
|
||||
};
|
||||
|
||||
const generateBillLine = (invoiceLine, responsibilityCenters) => {
|
||||
const generateBillLine = (billLine, responsibilityCenters) => {
|
||||
return {
|
||||
AccountRef: {
|
||||
FullName: responsibilityCenters.costs.find(
|
||||
(c) => c.name === invoiceLine.cost_center
|
||||
(c) => c.name === billLine.cost_center
|
||||
).accountname,
|
||||
},
|
||||
Amount: Dinero({
|
||||
amount: Math.round(invoiceLine.actual_cost * 100),
|
||||
amount: Math.round(billLine.actual_cost * 100),
|
||||
}).toFormat(DineroQbFormat),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -108,9 +108,9 @@ query QUERY_JOBS_FOR_RECEIVABLES_EXPORT($ids: [uuid!]!) {
|
||||
}
|
||||
`;
|
||||
|
||||
exports.QUERY_INVOICES_FOR_PAYABLES_EXPORT = `
|
||||
query QUERY_INVOICES_FOR_PAYABLES_EXPORT($invoices: [uuid!]!) {
|
||||
invoices(where: {id: {_in: $invoices}}) {
|
||||
exports.QUERY_BILLS_FOR_PAYABLES_EXPORT = `
|
||||
query QUERY_BILLS_FOR_PAYABLES_EXPORT($bills: [uuid!]!) {
|
||||
bills(where: {id: {_in: $bills}}) {
|
||||
id
|
||||
date
|
||||
due_date
|
||||
@@ -128,7 +128,7 @@ query QUERY_INVOICES_FOR_PAYABLES_EXPORT($invoices: [uuid!]!) {
|
||||
md_responsibility_centers
|
||||
}
|
||||
}
|
||||
invoicelines{
|
||||
billlines{
|
||||
id
|
||||
cost_center
|
||||
actual_cost
|
||||
|
||||
Reference in New Issue
Block a user