MAJOR CHANGE: Renamed invoices to bills BOD-410

This commit is contained in:
Patrick Fic
2020-09-22 17:01:03 -07:00
parent 95ee3ae2bc
commit f84520c260
91 changed files with 2510 additions and 2624 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -4,18 +4,15 @@ import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
import InvoiceExportButton from "../invoice-export-button/invoice-export-button.component"; import PayableExportButton from "../payable-export-button/payable-export-button.component";
import InvoiceExportAllButton from "../invoice-export-all-button/invoice-export-all-button.component"; import PayableExportAll from "../payable-export-all-button/payable-export-all-button.component";
import { DateFormatter } from "../../utils/DateFormatter"; import { DateFormatter } from "../../utils/DateFormatter";
import queryString from "query-string"; import queryString from "query-string";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
export default function AccountingPayablesTableComponent({ export default function AccountingPayablesTableComponent({ loading, bills }) {
loading,
invoices,
}) {
const { t } = useTranslation(); const { t } = useTranslation();
const [selectedInvoices, setSelectedInvoices] = useState([]); const [selectedBills, setSelectedBills] = useState([]);
const [transInProgress, setTransInProgress] = useState(false); const [transInProgress, setTransInProgress] = useState(false);
const [state, setState] = useState({ const [state, setState] = useState({
sortedInfo: {}, sortedInfo: {},
@@ -28,7 +25,7 @@ export default function AccountingPayablesTableComponent({
const columns = [ const columns = [
{ {
title: t("invoices.fields.vendorname"), title: t("bills.fields.vendorname"),
dataIndex: "vendorname", dataIndex: "vendorname",
key: "vendorname", key: "vendorname",
sorter: (a, b) => alphaSort(a.vendor.name, b.vendor.name), 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", dataIndex: "invoice_number",
key: "invoice_number", key: "invoice_number",
sorter: (a, b) => alphaSort(a.invoice_number, b.invoice_number), sorter: (a, b) => alphaSort(a.invoice_number, b.invoice_number),
@@ -56,9 +53,9 @@ export default function AccountingPayablesTableComponent({
render: (text, record) => ( render: (text, record) => (
<Link <Link
to={{ to={{
pathname: `/manage/invoices`, pathname: `/manage/bills`,
search: queryString.stringify({ search: queryString.stringify({
invoiceid: record.id, billid: record.id,
vendorid: record.vendor.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", dataIndex: "date",
key: "date", key: "date",
@@ -89,7 +86,7 @@ export default function AccountingPayablesTableComponent({
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>, render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
}, },
{ {
title: t("invoices.fields.total"), title: t("bills.fields.total"),
dataIndex: "total", dataIndex: "total",
key: "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", dataIndex: "is_credit_memo",
key: "is_credit_memo", key: "is_credit_memo",
sorter: (a, b) => a.is_credit_memo - b.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) => ( render: (text, record) => (
<div> <div>
<InvoiceExportButton <PayableExportButton
invoiceId={record.id} billId={record.id}
disabled={transInProgress || !!record.exported} disabled={transInProgress || !!record.exported}
loadingCallback={setTransInProgress} loadingCallback={setTransInProgress}
/> />
@@ -136,7 +133,7 @@ export default function AccountingPayablesTableComponent({
}; };
const dataSource = state.search const dataSource = state.search
? invoices.filter( ? bills.filter(
(v) => (v) =>
(v.vendor.name || "") (v.vendor.name || "")
.toLowerCase() .toLowerCase()
@@ -145,7 +142,7 @@ export default function AccountingPayablesTableComponent({
.toLowerCase() .toLowerCase()
.includes(state.search.toLowerCase()) .includes(state.search.toLowerCase())
) )
: invoices; : bills;
return ( return (
<div> <div>
@@ -160,11 +157,11 @@ export default function AccountingPayablesTableComponent({
placeholder={t("general.labels.search")} placeholder={t("general.labels.search")}
allowClear allowClear
/> />
<InvoiceExportAllButton <PayableExportAll
invoiceIds={selectedInvoices} billIds={selectedBills}
disabled={transInProgress || selectedInvoices.length === 0} disabled={transInProgress || selectedBills.length === 0}
loadingCallback={setTransInProgress} loadingCallback={setTransInProgress}
completedCallback={setSelectedInvoices} completedCallback={setSelectedBills}
/> />
</div> </div>
); );
@@ -177,14 +174,14 @@ export default function AccountingPayablesTableComponent({
onChange={handleTableChange} onChange={handleTableChange}
rowSelection={{ rowSelection={{
onSelectAll: (selected, selectedRows) => onSelectAll: (selected, selectedRows) =>
setSelectedInvoices(selectedRows.map((i) => i.id)), setSelectedBills(selectedRows.map((i) => i.id)),
onSelect: (record, selected, selectedRows, nativeEvent) => { onSelect: (record, selected, selectedRows, nativeEvent) => {
setSelectedInvoices(selectedRows.map((i) => i.id)); setSelectedBills(selectedRows.map((i) => i.id));
}, },
getCheckboxProps: (record) => ({ getCheckboxProps: (record) => ({
disabled: record.exported, disabled: record.exported,
}), }),
selectedRowKeys: selectedInvoices, selectedRowKeys: selectedBills,
type: "checkbox", type: "checkbox",
}} }}
/> />

View File

@@ -1,57 +1,47 @@
import { useMutation, useQuery } from "@apollo/react-hooks"; import { useMutation, useQuery } from "@apollo/react-hooks";
import { Form, Button } from "antd"; import { Button, Form } from "antd";
import moment from "moment"; import moment from "moment";
import queryString from "query-string"; import queryString from "query-string";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { UPDATE_BILL_LINE } from "../../graphql/bill-lines.queries";
import { import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
QUERY_INVOICE_BY_PK,
UPDATE_INVOICE,
} from "../../graphql/invoices.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import InvoiceFormContainer from "../invoice-form/invoice-form.container"; import BillFormContainer from "../bill-form/bill-form.container";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import { UPDATE_INVOICE_LINE } from "../../graphql/invoice-lines.queries";
import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container"; import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
const mapStateToProps = createStructuredSelector({ export default function BillDetailEditcontainer() {
bodyshop: selectBodyshop,
});
export function InvoiceDetailEditContainer({ bodyshop }) {
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const { t } = useTranslation(); const { t } = useTranslation();
const [form] = Form.useForm(); const [form] = Form.useForm();
const [updateLoading, setUpdateLoading] = useState(false); const [updateLoading, setUpdateLoading] = useState(false);
const [updateInvoice] = useMutation(UPDATE_INVOICE); const [update_bill] = useMutation(UPDATE_BILL);
const [updateInvoiceLine] = useMutation(UPDATE_INVOICE_LINE); const [updateBillLine] = useMutation(UPDATE_BILL_LINE);
const { loading, error, data, refetch } = useQuery(QUERY_INVOICE_BY_PK, { const { loading, error, data, refetch } = useQuery(QUERY_BILL_BY_PK, {
variables: { invoiceid: search.invoiceid }, variables: { billid: search.billid },
skip: !!!search.invoiceid, skip: !!!search.billid,
}); });
const handleFinish = async (values) => { const handleFinish = async (values) => {
setUpdateLoading(true); setUpdateLoading(true);
const { invoicelines, upload, ...invoice } = values; const { billlines, upload, ...bill } = values;
const updates = []; const updates = [];
updates.push( updates.push(
updateInvoice({ update_bill({
variables: { invoiceId: search.invoiceid, invoice: invoice }, variables: { billId: search.billid, bill: bill },
}) })
); );
invoicelines.forEach((il) => { billlines.forEach((il) => {
delete il.__typename; delete il.__typename;
updates.push( updates.push(
updateInvoiceLine({ updateBillLine({
variables: { variables: {
invoicelineId: il.id, billLineId: il.id,
invoiceLine: { billLine: {
...il, ...il,
joblineid: il.joblineid === "noline" ? null : il.joblineid, joblineid: il.joblineid === "noline" ? null : il.joblineid,
}, },
@@ -65,14 +55,13 @@ export function InvoiceDetailEditContainer({ bodyshop }) {
}; };
useEffect(() => { useEffect(() => {
if (search.invoiceid) { if (search.billid) {
form.resetFields(); form.resetFields();
} }
}, [form, search.invoiceid]); }, [form, search.billid]);
if (error) return <AlertComponent message={error.message} type="error" />; if (error) return <AlertComponent message={error.message} type="error" />;
if (!!!search.invoiceid) if (!!!search.billid) return <div>{t("bills.labels.noneselected")}</div>;
return <div>{t("invoices.labels.noneselected")}</div>;
return ( return (
<LoadingSkeleton loading={loading}> <LoadingSkeleton loading={loading}>
<Form <Form
@@ -81,9 +70,9 @@ export function InvoiceDetailEditContainer({ bodyshop }) {
initialValues={ initialValues={
data 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 { return {
...i, ...i,
joblineid: !!i.joblineid ? i.joblineid : "noline", joblineid: !!i.joblineid ? i.joblineid : "noline",
@@ -100,9 +89,7 @@ export function InvoiceDetailEditContainer({ bodyshop }) {
}, },
}; };
}), }),
date: data.invoices_by_pk date: data.bills_by_pk ? moment(data.bills_by_pk.date) : null,
? moment(data.invoices_by_pk.date)
: null,
} }
: {} : {}
} }
@@ -110,15 +97,14 @@ export function InvoiceDetailEditContainer({ bodyshop }) {
<Button htmlType="submit" loading={updateLoading} type="primary"> <Button htmlType="submit" loading={updateLoading} type="primary">
{t("general.actions.save")} {t("general.actions.save")}
</Button> </Button>
<InvoiceFormContainer form={form} invoiceEdit /> <BillFormContainer form={form} billEdit />
<JobDocumentsGallery <JobDocumentsGallery
jobId={data ? data.invoices_by_pk.jobid : null} jobId={data ? data.bills_by_pk.jobid : null}
invoiceId={search.invoiceid} billId={search.billid}
documentsList={data ? data.invoices_by_pk.documents : []} documentsList={data ? data.bills_by_pk.documents : []}
invoicesCallback={refetch} billsCallback={refetch}
/> />
</Form> </Form>
</LoadingSkeleton> </LoadingSkeleton>
); );
} }
export default connect(mapStateToProps, null)(InvoiceDetailEditContainer);

View File

@@ -4,28 +4,28 @@ import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { INSERT_NEW_INVOICE } from "../../graphql/invoices.queries"; import { INSERT_NEW_BILL } from "../../graphql/bills.queries";
import { toggleModalVisible } from "../../redux/modals/modals.actions"; import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectInvoiceEnterModal } from "../../redux/modals/modals.selectors"; import { selectBillEnterModal } from "../../redux/modals/modals.selectors";
import { import {
selectBodyshop, selectBodyshop,
selectCurrentUser, selectCurrentUser,
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import { handleUpload } from "../documents-upload/documents-upload.utility"; 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"; import { UPDATE_JOB_LINE_STATUS } from "../../graphql/jobs-lines.queries";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
invoiceEnterModal: selectInvoiceEnterModal, billEnterModal: selectBillEnterModal,
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
currentUser: selectCurrentUser, currentUser: selectCurrentUser,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("invoiceEnter")), toggleModalVisible: () => dispatch(toggleModalVisible("billEnter")),
}); });
function InvoiceEnterModalContainer({ function BillEnterModalContainer({
invoiceEnterModal, billEnterModal,
toggleModalVisible, toggleModalVisible,
bodyshop, bodyshop,
currentUser, currentUser,
@@ -33,21 +33,21 @@ function InvoiceEnterModalContainer({
const [form] = Form.useForm(); const [form] = Form.useForm();
const { t } = useTranslation(); const { t } = useTranslation();
const [enterAgain, setEnterAgain] = useState(false); 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 [updateJobLines] = useMutation(UPDATE_JOB_LINE_STATUS);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const handleFinish = (values) => { const handleFinish = (values) => {
setLoading(true); setLoading(true);
const { upload, location, ...remainingValues } = values; const { upload, location, ...remainingValues } = values;
insertInvoice({ insertBill({
variables: { variables: {
invoice: [ bill: [
Object.assign({}, remainingValues, { Object.assign({}, remainingValues, {
invoicelines: { billlines: {
data: data:
remainingValues.invoicelines && remainingValues.billlines &&
remainingValues.invoicelines.map((i) => { remainingValues.billlines.map((i) => {
return { return {
...i, ...i,
joblineid: i.joblineid === "noline" ? null : i.joblineid, joblineid: i.joblineid === "noline" ? null : i.joblineid,
@@ -59,11 +59,11 @@ function InvoiceEnterModalContainer({
}, },
}) })
.then((r) => { .then((r) => {
const invoiceId = r.data.insert_invoices.returning[0].id; const billId = r.data.insert_bills.returning[0].id;
updateJobLines({ updateJobLines({
variables: { variables: {
ids: remainingValues.invoicelines ids: remainingValues.billlines
.filter((il) => il.joblineid !== "noline") .filter((il) => il.joblineid !== "noline")
.map((li) => li.joblineid), .map((li) => li.joblineid),
status: bodyshop.md_order_statuses.default_received || "Received*", status: bodyshop.md_order_statuses.default_received || "Received*",
@@ -80,7 +80,7 @@ function InvoiceEnterModalContainer({
bodyshop: bodyshop, bodyshop: bodyshop,
uploaded_by: currentUser.email, uploaded_by: currentUser.email,
jobId: values.jobid, jobId: values.jobid,
invoiceId: invoiceId, billId: billId,
tagsArray: null, tagsArray: null,
callback: null, callback: null,
} }
@@ -90,14 +90,13 @@ function InvoiceEnterModalContainer({
/////////////////////////// ///////////////////////////
setLoading(false); setLoading(false);
notification["success"]({ notification["success"]({
message: t("invoices.successes.created"), message: t("bills.successes.created"),
}); });
if (invoiceEnterModal.actions.refetch) if (billEnterModal.actions.refetch) billEnterModal.actions.refetch();
invoiceEnterModal.actions.refetch();
if (enterAgain) { if (enterAgain) {
form.resetFields(); form.resetFields();
form.setFieldsValue({ invoicelines: [] }); form.setFieldsValue({ billlines: [] });
} else { } else {
toggleModalVisible(); toggleModalVisible();
} }
@@ -108,7 +107,7 @@ function InvoiceEnterModalContainer({
setLoading(false); setLoading(false);
setEnterAgain(false); setEnterAgain(false);
notification["error"]({ notification["error"]({
message: t("invoices.errors.creating", { message: t("bills.errors.creating", {
message: JSON.stringify(error), message: JSON.stringify(error),
}), }),
}); });
@@ -124,14 +123,14 @@ function InvoiceEnterModalContainer({
}, [enterAgain, form]); }, [enterAgain, form]);
useEffect(() => { useEffect(() => {
if (invoiceEnterModal.visible) form.resetFields(); if (billEnterModal.visible) form.resetFields();
}, [invoiceEnterModal.visible, form]); }, [billEnterModal.visible, form]);
return ( return (
<Modal <Modal
title={t("invoices.labels.new")} title={t("bills.labels.new")}
width={"90%"} width={"90%"}
visible={invoiceEnterModal.visible} visible={billEnterModal.visible}
okText={t("general.actions.save")} okText={t("general.actions.save")}
onOk={() => form.submit()} onOk={() => form.submit()}
onCancel={handleCancel} onCancel={handleCancel}
@@ -142,7 +141,7 @@ function InvoiceEnterModalContainer({
<Button loading={loading} onClick={() => form.submit()}> <Button loading={loading} onClick={() => form.submit()}>
{t("general.actions.save")} {t("general.actions.save")}
</Button> </Button>
{invoiceEnterModal.context && invoiceEnterModal.context.id ? null : ( {billEnterModal.context && billEnterModal.context.id ? null : (
<Button <Button
type="primary" type="primary"
loading={loading} loading={loading}
@@ -166,26 +165,25 @@ function InvoiceEnterModalContainer({
setEnterAgain(false); setEnterAgain(false);
}} }}
initialValues={{ initialValues={{
...invoiceEnterModal.context.invoice, ...billEnterModal.context.bill,
jobid: jobid:
(invoiceEnterModal.context.job && (billEnterModal.context.job && billEnterModal.context.job.id) ||
invoiceEnterModal.context.job.id) ||
null, null,
federal_tax_rate: federal_tax_rate:
(bodyshop.invoice_tax_rates && (bodyshop.bill_tax_rates &&
bodyshop.invoice_tax_rates.federal_tax_rate) || bodyshop.bill_tax_rates.federal_tax_rate) ||
0, 0,
state_tax_rate: state_tax_rate:
(bodyshop.invoice_tax_rates && (bodyshop.bill_tax_rates &&
bodyshop.invoice_tax_rates.state_tax_rate) || bodyshop.bill_tax_rates.state_tax_rate) ||
0, 0,
local_tax_rate: local_tax_rate:
(bodyshop.invoice_tax_rates && (bodyshop.bill_tax_rates &&
bodyshop.invoice_tax_rates.local_tax_rate) || bodyshop.bill_tax_rates.local_tax_rate) ||
0, 0,
}} }}
> >
<InvoiceFormContainer form={form} /> <BillFormContainer form={form} />
</Form> </Form>
</Modal> </Modal>
); );
@@ -194,4 +192,4 @@ function InvoiceEnterModalContainer({
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps
)(InvoiceEnterModalContainer); )(BillEnterModalContainer);

View File

@@ -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 JobSearchSelect from "../job-search-select/job-search-select.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component"; import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
import InvoiceFormLines from "./invoice-form.lines.component"; import BillFormLines from "./bill-form.lines.component";
import "./invoice-form.styles.scss"; import { CalculateBillTotal } from "./bill-form.totals.utility";
import { CalculateInvoiceTotal } from "./invoice-form.totals.utility";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({});
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export function InvoiceFormComponent({ export function BillFormComponent({
bodyshop, bodyshop,
form, form,
vendorAutoCompleteOptions, vendorAutoCompleteOptions,
lineData, lineData,
responsibilityCenters, responsibilityCenters,
loadLines, loadLines,
invoiceEdit, billEdit,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -69,7 +65,7 @@ export function InvoiceFormComponent({
<LayoutFormRow> <LayoutFormRow>
<Form.Item <Form.Item
name="jobid" name="jobid"
label={t("invoices.fields.ro_number")} label={t("bills.fields.ro_number")}
rules={[ rules={[
{ {
required: true, required: true,
@@ -78,7 +74,7 @@ export function InvoiceFormComponent({
]} ]}
> >
<JobSearchSelect <JobSearchSelect
disabled={invoiceEdit} disabled={billEdit}
onBlur={() => { onBlur={() => {
if (form.getFieldValue("jobid") !== null) { if (form.getFieldValue("jobid") !== null) {
loadLines({ variables: { id: form.getFieldValue("jobid") } }); loadLines({ variables: { id: form.getFieldValue("jobid") } });
@@ -87,9 +83,9 @@ export function InvoiceFormComponent({
/> />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("invoices.fields.vendor")} label={t("bills.fields.vendor")}
name="vendorid" name="vendorid"
style={{ display: invoiceEdit ? "none" : null }} style={{ display: billEdit ? "none" : null }}
rules={[ rules={[
{ {
required: true, required: true,
@@ -106,7 +102,7 @@ export function InvoiceFormComponent({
<LayoutFormRow> <LayoutFormRow>
<Form.Item <Form.Item
label={t("invoices.fields.invoice_number")} label={t("bills.fields.invoice_number")}
name="invoice_number" name="invoice_number"
rules={[ rules={[
{ {
@@ -118,7 +114,7 @@ export function InvoiceFormComponent({
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("invoices.fields.date")} label={t("bills.fields.date")}
name="date" name="date"
rules={[ rules={[
{ {
@@ -130,14 +126,14 @@ export function InvoiceFormComponent({
<FormDatePicker /> <FormDatePicker />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("invoices.fields.is_credit_memo")} label={t("bills.fields.is_credit_memo")}
name="is_credit_memo" name="is_credit_memo"
valuePropName="checked" valuePropName="checked"
> >
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("invoices.fields.total")} label={t("bills.fields.total")}
name="total" name="total"
rules={[ rules={[
{ {
@@ -149,28 +145,25 @@ export function InvoiceFormComponent({
<CurrencyInput min={0} /> <CurrencyInput min={0} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("invoices.fields.federal_tax_rate")} label={t("bills.fields.federal_tax_rate")}
name="federal_tax_rate" name="federal_tax_rate"
> >
<CurrencyInput min={0} /> <CurrencyInput min={0} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("invoices.fields.state_tax_rate")} label={t("bills.fields.state_tax_rate")}
name="state_tax_rate" name="state_tax_rate"
> >
<CurrencyInput min={0} /> <CurrencyInput min={0} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("invoices.fields.local_tax_rate")} label={t("bills.fields.local_tax_rate")}
name="local_tax_rate" name="local_tax_rate"
> >
<CurrencyInput min={0} /> <CurrencyInput min={0} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item label={t("bills.fields.allpartslocation")} name="location">
label={t("invoices.fields.allpartslocation")}
name="location"
>
<Select style={{ width: "10rem" }}> <Select style={{ width: "10rem" }}>
{bodyshop.md_parts_locations.map((loc, idx) => ( {bodyshop.md_parts_locations.map((loc, idx) => (
<Select.Option key={idx} value={loc}> <Select.Option key={idx} value={loc}>
@@ -181,9 +174,9 @@ export function InvoiceFormComponent({
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<Typography.Title level={4}> <Typography.Title level={4}>
{t("invoices.labels.invoice_lines")} {t("bills.labels.bill_lines")}
</Typography.Title> </Typography.Title>
<InvoiceFormLines <BillFormLines
lineData={lineData} lineData={lineData}
discount={discount} discount={discount}
form={form} form={form}
@@ -193,7 +186,7 @@ export function InvoiceFormComponent({
<Form.Item <Form.Item
name="upload" name="upload"
label="Upload" label="Upload"
style={{ display: invoiceEdit ? "none" : null }} style={{ display: billEdit ? "none" : null }}
valuePropName="fileList" valuePropName="fileList"
getValueFromEvent={(e) => { getValueFromEvent={(e) => {
if (Array.isArray(e)) { if (Array.isArray(e)) {
@@ -210,7 +203,7 @@ export function InvoiceFormComponent({
<Form.Item shouldUpdate> <Form.Item shouldUpdate>
{() => { {() => {
const values = form.getFieldsValue([ const values = form.getFieldsValue([
"invoicelines", "billlines",
"total", "total",
"federal_tax_rate", "federal_tax_rate",
"state_tax_rate", "state_tax_rate",
@@ -219,46 +212,46 @@ export function InvoiceFormComponent({
let totals; let totals;
if ( if (
!!values.total && !!values.total &&
!!values.invoicelines && !!values.billlines &&
values.invoicelines.length > 0 values.billlines.length > 0
) )
totals = CalculateInvoiceTotal(values); totals = CalculateBillTotal(values);
if (!!totals) if (!!totals)
return ( return (
<div> <div>
<Space> <Space>
<Statistic <Statistic
title={t("invoices.labels.subtotal")} title={t("bills.labels.subtotal")}
value={totals.subtotal.toFormat()} value={totals.subtotal.toFormat()}
precision={2} precision={2}
/> />
<Statistic <Statistic
title={t("invoices.labels.federal_tax")} title={t("bills.labels.federal_tax")}
value={totals.federalTax.toFormat()} value={totals.federalTax.toFormat()}
precision={2} precision={2}
/> />
<Statistic <Statistic
title={t("invoices.labels.state_tax")} title={t("bills.labels.state_tax")}
value={totals.stateTax.toFormat()} value={totals.stateTax.toFormat()}
precision={2} precision={2}
/> />
<Statistic <Statistic
title={t("invoices.labels.local_tax")} title={t("bills.labels.local_tax")}
value={totals.localTax.toFormat()} value={totals.localTax.toFormat()}
precision={2} precision={2}
/> />
<Statistic <Statistic
title={t("invoices.labels.entered_total")} title={t("bills.labels.entered_total")}
value={totals.enteredTotal.toFormat()} value={totals.enteredTotal.toFormat()}
precision={2} precision={2}
/> />
<Statistic <Statistic
title={t("invoices.labels.invoice_total")} title={t("bills.labels.bill_total")}
value={totals.invoiceTotal.toFormat()} value={totals.invoiceTotal.toFormat()}
precision={2} precision={2}
/> />
<Statistic <Statistic
title={t("invoices.labels.discrepancy")} title={t("bills.labels.discrepancy")}
valueStyle={{ valueStyle={{
color: color:
totals.discrepancy.getAmount() === 0 ? "green" : "red", totals.discrepancy.getAmount() === 0 ? "green" : "red",
@@ -270,7 +263,7 @@ export function InvoiceFormComponent({
{form.getFieldValue("is_credit_memo") ? ( {form.getFieldValue("is_credit_memo") ? (
<AlertComponent <AlertComponent
type="warning" type="warning"
message={t("invoices.labels.enteringcreditmemo")} message={t("bills.labels.enteringcreditmemo")}
/> />
) : null} ) : null}
</div> </div>
@@ -282,7 +275,4 @@ export function InvoiceFormComponent({
); );
} }
export default connect( export default connect(mapStateToProps, mapDispatchToProps)(BillFormComponent);
mapStateToProps,
mapDispatchToProps
)(InvoiceFormComponent);

View File

@@ -2,27 +2,27 @@ import { useLazyQuery, useQuery } from "@apollo/react-hooks";
import React from "react"; import React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; 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 { SEARCH_VENDOR_AUTOCOMPLETE } from "../../graphql/vendors.queries";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import InvoiceFormComponent from "./invoice-form.component"; import BillFormComponent from "./bill-form.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
export function InvoiceFormContainer({ bodyshop, form, invoiceEdit }) { export function BillFormContainer({ bodyshop, form, billEdit }) {
const { data: VendorAutoCompleteData } = useQuery(SEARCH_VENDOR_AUTOCOMPLETE); const { data: VendorAutoCompleteData } = useQuery(SEARCH_VENDOR_AUTOCOMPLETE);
const [loadLines, { data: lineData }] = useLazyQuery( const [loadLines, { data: lineData }] = useLazyQuery(
GET_JOB_LINES_TO_ENTER_INVOICE GET_JOB_LINES_TO_ENTER_BILL
); );
return ( return (
<div> <div>
<InvoiceFormComponent <BillFormComponent
form={form} form={form}
invoiceEdit={invoiceEdit} billEdit={billEdit}
vendorAutoCompleteOptions={ vendorAutoCompleteOptions={
VendorAutoCompleteData && VendorAutoCompleteData.vendors VendorAutoCompleteData && VendorAutoCompleteData.vendors
} }
@@ -33,4 +33,4 @@ export function InvoiceFormContainer({ bodyshop, form, invoiceEdit }) {
</div> </div>
); );
} }
export default connect(mapStateToProps, null)(InvoiceFormContainer); export default connect(mapStateToProps, null)(BillFormContainer);

View File

@@ -11,10 +11,10 @@ import {
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import CurrencyInput from "../form-items-formatted/currency-form-item.component"; 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 FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
export default function InvoiceEnterModalLinesComponent({ export default function BillEnterModalLinesComponent({
lineData, lineData,
discount, discount,
form, form,
@@ -24,7 +24,7 @@ export default function InvoiceEnterModalLinesComponent({
const { setFieldsValue, getFieldsValue } = form; const { setFieldsValue, getFieldsValue } = form;
return ( return (
<Form.List name="invoicelines"> <Form.List name="billlines">
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<div className="invoice-form-lines-wrapper"> <div className="invoice-form-lines-wrapper">
@@ -34,7 +34,7 @@ export default function InvoiceEnterModalLinesComponent({
<div style={{ display: "flex", alignItems: "center" }}> <div style={{ display: "flex", alignItems: "center" }}>
<LayoutFormRow style={{ flex: 1 }} grow> <LayoutFormRow style={{ flex: 1 }} grow>
<Form.Item <Form.Item
label={t("invoicelines.fields.jobline")} label={t("billlines.fields.jobline")}
key={`${index}joblinename`} key={`${index}joblinename`}
name={[field.name, "joblineid"]} name={[field.name, "joblineid"]}
rules={[ rules={[
@@ -44,13 +44,13 @@ export default function InvoiceEnterModalLinesComponent({
}, },
]} ]}
> >
<InvoiceLineSearchSelect <BillLineSearchSelect
options={lineData} options={lineData}
onSelect={(value, opt) => { onSelect={(value, opt) => {
setFieldsValue({ setFieldsValue({
invoicelines: getFieldsValue([ billlines: getFieldsValue([
"invoicelines", "billlines",
]).invoicelines.map((item, idx) => { ]).billlines.map((item, idx) => {
if (idx === index) { if (idx === index) {
return { return {
...item, ...item,
@@ -72,7 +72,7 @@ export default function InvoiceEnterModalLinesComponent({
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("invoicelines.fields.line_desc")} label={t("billlines.fields.line_desc")}
key={`${index}line_desc`} key={`${index}line_desc`}
name={[field.name, "line_desc"]} name={[field.name, "line_desc"]}
rules={[ rules={[
@@ -85,7 +85,7 @@ export default function InvoiceEnterModalLinesComponent({
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("invoicelines.fields.quantity")} label={t("billlines.fields.quantity")}
key={`${index}quantity`} key={`${index}quantity`}
name={[field.name, "quantity"]} name={[field.name, "quantity"]}
rules={[ rules={[
@@ -98,7 +98,7 @@ export default function InvoiceEnterModalLinesComponent({
<InputNumber precision={0} min={0} /> <InputNumber precision={0} min={0} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("invoicelines.fields.actual")} label={t("billlines.fields.actual")}
key={`${index}actual_price`} key={`${index}actual_price`}
name={[field.name, "actual_price"]} name={[field.name, "actual_price"]}
rules={[ rules={[
@@ -112,9 +112,9 @@ export default function InvoiceEnterModalLinesComponent({
min={0} min={0}
onBlur={(e) => { onBlur={(e) => {
setFieldsValue({ setFieldsValue({
invoicelines: getFieldsValue( billlines: getFieldsValue(
"invoicelines" "billlines"
).invoicelines.map((item, idx) => { ).billlines.map((item, idx) => {
if (idx === index) { if (idx === index) {
return { return {
...item, ...item,
@@ -131,7 +131,7 @@ export default function InvoiceEnterModalLinesComponent({
/> />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("invoicelines.fields.actual_cost")} label={t("billlines.fields.actual_cost")}
key={`${index}actual_cost`} key={`${index}actual_cost`}
name={[field.name, "actual_cost"]} name={[field.name, "actual_cost"]}
rules={[ rules={[
@@ -145,8 +145,9 @@ export default function InvoiceEnterModalLinesComponent({
</Form.Item> </Form.Item>
<Form.Item shouldUpdate> <Form.Item shouldUpdate>
{() => { {() => {
const line = getFieldsValue(["invoicelines"]) const line = getFieldsValue(["billlines"]).billlines[
.invoicelines[index]; index
];
if (!!!line) return null; if (!!!line) return null;
const lineDiscount = ( const lineDiscount = (
1 - 1 -
@@ -161,7 +162,7 @@ export default function InvoiceEnterModalLinesComponent({
}} }}
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("invoicelines.fields.cost_center")} label={t("billlines.fields.cost_center")}
key={`${index}cost_center`} key={`${index}cost_center`}
name={[field.name, "cost_center"]} name={[field.name, "cost_center"]}
rules={[ rules={[
@@ -180,7 +181,7 @@ export default function InvoiceEnterModalLinesComponent({
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("invoicelines.fields.federal_tax_applicable")} label={t("billlines.fields.federal_tax_applicable")}
key={`${index}fedtax`} key={`${index}fedtax`}
initialValue={true} initialValue={true}
valuePropName="checked" valuePropName="checked"
@@ -189,7 +190,7 @@ export default function InvoiceEnterModalLinesComponent({
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("invoicelines.fields.state_tax_applicable")} label={t("billlines.fields.state_tax_applicable")}
key={`${index}statetax`} key={`${index}statetax`}
valuePropName="checked" valuePropName="checked"
name={[field.name, "applicable_taxes", "state"]} name={[field.name, "applicable_taxes", "state"]}
@@ -197,7 +198,7 @@ export default function InvoiceEnterModalLinesComponent({
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("invoicelines.fields.local_tax_applicable")} label={t("billlines.fields.local_tax_applicable")}
key={`${index}localtax`} key={`${index}localtax`}
valuePropName="checked" valuePropName="checked"
name={[field.name, "applicable_taxes", "local"]} name={[field.name, "applicable_taxes", "local"]}
@@ -227,7 +228,7 @@ export default function InvoiceEnterModalLinesComponent({
}} }}
style={{ width: "100%" }} style={{ width: "100%" }}
> >
{t("invoicelines.actions.newline")} {t("billlines.actions.newline")}
</Button> </Button>
</Form.Item> </Form.Item>
</div> </div>

View File

@@ -1,13 +1,12 @@
import Dinero from "dinero.js"; import Dinero from "dinero.js";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
export const CalculateInvoiceTotal = (invoice) => { export const CalculateBillTotal = (invoice) => {
logImEXEvent("invoice_calculate_total"); logImEXEvent("invoice_calculate_total");
const { const {
total, total,
billlines,
invoicelines,
federal_tax_rate, federal_tax_rate,
local_tax_rate, local_tax_rate,
state_tax_rate, state_tax_rate,
@@ -17,8 +16,10 @@ export const CalculateInvoiceTotal = (invoice) => {
let federalTax = Dinero({ amount: 0 }); let federalTax = Dinero({ amount: 0 });
let stateTax = Dinero({ amount: 0 }); let stateTax = Dinero({ amount: 0 });
let localTax = Dinero({ amount: 0 }); let localTax = Dinero({ amount: 0 });
if (!!!invoicelines) return null;
invoicelines.forEach((i) => { if (!!!billlines) return null;
billlines.forEach((i) => {
if (!!i) { if (!!i) {
const itemTotal = Dinero({ const itemTotal = Dinero({
amount: Math.round((i.actual_cost || 0) * 100) || 0, amount: Math.round((i.actual_cost || 0) * 100) || 0,

View File

@@ -5,7 +5,7 @@ import CurrencyFormatter from "../../utils/CurrencyFormatter";
//To be used as a form element only. //To be used as a form element only.
const { Option } = Select; const { Option } = Select;
const InvoiceLineSearchSelect = ( const BillLineSearchSelect = (
{ value, onChange, options, onBlur, onSelect }, { value, onChange, options, onBlur, onSelect },
ref ref
) => { ) => {
@@ -33,7 +33,7 @@ const InvoiceLineSearchSelect = (
onSelect={onSelect} onSelect={onSelect}
> >
<Select.Option key={null} value={"noline"} cost={0} line_desc={""}> <Select.Option key={null} value={"noline"} cost={0} line_desc={""}>
{t("invoicelines.labels.other")} {t("billlines.labels.other")}
</Select.Option> </Select.Option>
{options {options
? options.map((item) => ( ? options.map((item) => (
@@ -68,4 +68,4 @@ const InvoiceLineSearchSelect = (
</Select> </Select>
); );
}; };
export default forwardRef(InvoiceLineSearchSelect); export default forwardRef(BillLineSearchSelect);

View File

@@ -14,38 +14,34 @@ import { useLocation } from "react-router-dom";
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setPartsOrderContext: (context) => setPartsOrderContext: (context) =>
dispatch(setModalContext({ context: context, modal: "partsOrder" })), dispatch(setModalContext({ context: context, modal: "partsOrder" })),
setInvoiceEnterContext: (context) => setBillEnterContext: (context) =>
dispatch(setModalContext({ context: context, modal: "invoiceEnter" })), dispatch(setModalContext({ context: context, modal: "billEnter" })),
setReconciliationContext: (context) => setReconciliationContext: (context) =>
dispatch(setModalContext({ context: context, modal: "reconciliation" })), dispatch(setModalContext({ context: context, modal: "reconciliation" })),
}); });
export function InvoicesListTableComponent({ export function BillsListTableComponent({
job, job,
loading, billsQuery,
invoicesQuery,
handleOnRowClick, handleOnRowClick,
setPartsOrderContext, setPartsOrderContext,
setInvoiceEnterContext, setBillEnterContext,
setReconciliationContext, setReconciliationContext,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [ const [selectedBillLinesByBill, setSelectedBillLinesByBill] = useState({});
selectedInvoiceLinesByInvoice,
setSelectedInvoiceLinesByInvoice,
] = useState({});
const [state, setState] = useState({ const [state, setState] = useState({
sortedInfo: {}, sortedInfo: {},
}); });
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const selectedInvoice = search.invoiceid; const selectedBill = search.billid;
const invoices = invoicesQuery.data ? invoicesQuery.data.invoices : []; const bills = billsQuery.data ? billsQuery.data.bills : [];
const { refetch } = invoicesQuery; const { refetch } = billsQuery;
const columns = [ const columns = [
{ {
title: t("invoices.fields.vendorname"), title: t("bills.fields.vendorname"),
dataIndex: "vendorname", dataIndex: "vendorname",
key: "vendorname", key: "vendorname",
sorter: (a, b) => alphaSort(a.vendor.name, b.vendor.name), 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>, render: (text, record) => <span>{record.vendor.name}</span>,
}, },
{ {
title: t("invoices.fields.invoice_number"), title: t("bills.fields.invoice_number"),
dataIndex: "invoice_number", dataIndex: "invoice_number",
key: "invoice_number", key: "invoice_number",
sorter: (a, b) => alphaSort(a.invoice_number, b.invoice_number), sorter: (a, b) => alphaSort(a.invoice_number, b.invoice_number),
@@ -63,7 +59,7 @@ export function InvoicesListTableComponent({
state.sortedInfo.order, state.sortedInfo.order,
}, },
{ {
title: t("invoices.fields.date"), title: t("bills.fields.date"),
dataIndex: "date", dataIndex: "date",
key: "date", key: "date",
sorter: (a, b) => a.date - b.date, sorter: (a, b) => a.date - b.date,
@@ -72,7 +68,7 @@ export function InvoicesListTableComponent({
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>, render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
}, },
{ {
title: t("invoices.fields.total"), title: t("bills.fields.total"),
dataIndex: "total", dataIndex: "total",
key: "total", key: "total",
sorter: (a, b) => a.total - b.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", dataIndex: "is_credit_memo",
key: "is_credit_memo", key: "is_credit_memo",
sorter: (a, b) => a.is_credit_memo - b.is_credit_memo, sorter: (a, b) => a.is_credit_memo - b.is_credit_memo,
@@ -99,9 +95,9 @@ export function InvoicesListTableComponent({
render: (text, record) => ( render: (text, record) => (
<div> <div>
<Link <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> </Link>
</div> </div>
), ),
@@ -115,7 +111,7 @@ export function InvoicesListTableComponent({
const rowExpander = (record) => { const rowExpander = (record) => {
const columns = [ const columns = [
{ {
title: t("invoicelines.fields.line_desc"), title: t("billlines.fields.line_desc"),
dataIndex: "line_desc", dataIndex: "line_desc",
key: "line_desc", key: "line_desc",
sorter: (a, b) => alphaSort(a.line_desc, b.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, state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
}, },
{ {
title: t("invoicelines.fields.retail"), title: t("billlines.fields.retail"),
dataIndex: "actual_price", dataIndex: "actual_price",
key: "actual_price", key: "actual_price",
sorter: (a, b) => a.actual_price - b.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", dataIndex: "actual_cost",
key: "actual_cost", key: "actual_cost",
sorter: (a, b) => a.actual_cost - b.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", dataIndex: "quantity",
key: "quantity", key: "quantity",
sorter: (a, b) => a.quantity - b.quantity, sorter: (a, b) => a.quantity - b.quantity,
@@ -155,7 +151,7 @@ export function InvoicesListTableComponent({
state.sortedInfo.columnKey === "quantity" && state.sortedInfo.order, state.sortedInfo.columnKey === "quantity" && state.sortedInfo.order,
}, },
{ {
title: t("invoicelines.fields.cost_center"), title: t("billlines.fields.cost_center"),
dataIndex: "cost_center", dataIndex: "cost_center",
key: "cost_center", key: "cost_center",
sorter: (a, b) => alphaSort(a.cost_center, b.cost_center), sorter: (a, b) => alphaSort(a.cost_center, b.cost_center),
@@ -164,7 +160,7 @@ export function InvoicesListTableComponent({
state.sortedInfo.order, state.sortedInfo.order,
}, },
{ {
title: t("invoicelines.fields.federal_tax_applicable"), title: t("billlines.fields.federal_tax_applicable"),
dataIndex: "applicable_taxes.federal", dataIndex: "applicable_taxes.federal",
key: "applicable_taxes.federal", key: "applicable_taxes.federal",
render: (text, record) => ( 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", dataIndex: "applicable_taxes.state",
key: "applicable_taxes.state", key: "applicable_taxes.state",
render: (text, record) => ( 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", dataIndex: "applicable_taxes.local",
key: "applicable_taxes.local", key: "applicable_taxes.local",
render: (text, record) => ( render: (text, record) => (
@@ -207,26 +203,26 @@ export function InvoicesListTableComponent({
}, },
]; ];
const handleOnInvoiceRowclick = (selectedRows) => { const handleOnBillrowclick = (selectedRows) => {
setSelectedInvoiceLinesByInvoice({ setSelectedBillLinesByBill({
...selectedInvoiceLinesByInvoice, ...selectedBillLinesByBill,
[record.id]: selectedRows.map((r) => r.id), [record.id]: selectedRows.map((r) => r.id),
}); });
}; };
return ( return (
<div> <div>
<Typography.Title level={3}>{`${t("invoices.fields.invoice_number")} ${ <Typography.Title level={3}>{`${t("bills.fields.invoice_number")} ${
record.invoice_number record.invoice_number
}`}</Typography.Title> }`}</Typography.Title>
<Descriptions> <Descriptions>
<Descriptions.Item label={t("invoices.fields.federal_tax_rate")}> <Descriptions.Item label={t("bills.fields.federal_tax_rate")}>
{`${record.federal_tax_rate}%` || ""} {`${record.federal_tax_rate}%` || ""}
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label={t("invoices.fields.state_tax_rate")}> <Descriptions.Item label={t("bills.fields.state_tax_rate")}>
{`${record.state_tax_rate}%` || ""} {`${record.state_tax_rate}%` || ""}
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label={t("invoices.fields.local_tax_rate")}> <Descriptions.Item label={t("bills.fields.local_tax_rate")}>
{`${record.local_tax_rate}%` || ""} {`${record.local_tax_rate}%` || ""}
</Descriptions.Item> </Descriptions.Item>
</Descriptions> </Descriptions>
@@ -238,11 +234,11 @@ export function InvoicesListTableComponent({
context: { context: {
jobId: job.id, jobId: job.id,
vendorId: record.vendorid, vendorId: record.vendorid,
returnFromInvoice: record.id, returnFromBill: record.id,
invoiceNumber: record.invoice_number, invoiceNumber: record.invoice_number,
linesToOrder: record.invoicelines linesToOrder: record.billlines
.filter((il) => .filter((il) =>
selectedInvoiceLinesByInvoice[record.id].includes(il.id) selectedBillLinesByBill[record.id].includes(il.id)
) )
.map((i) => { .map((i) => {
return { return {
@@ -258,7 +254,7 @@ export function InvoicesListTableComponent({
}) })
} }
> >
{t("invoices.actions.return")} {t("bills.actions.return")}
</Button> </Button>
<Table <Table
size="small" size="small"
@@ -266,15 +262,15 @@ export function InvoicesListTableComponent({
pagination={{ position: "top", defaultPageSize: 25 }} pagination={{ position: "top", defaultPageSize: 25 }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={record.invoicelines} dataSource={record.billlines}
rowSelection={{ rowSelection={{
onSelect: (record, selected, selectedRows) => { onSelect: (record, selected, selectedRows) => {
handleOnInvoiceRowclick(selectedRows); handleOnBillrowclick(selectedRows);
}, },
onSelectAll: (selected, selectedRows, changeRows) => { onSelectAll: (selected, selectedRows, changeRows) => {
handleOnInvoiceRowclick(selectedRows); handleOnBillrowclick(selectedRows);
}, },
selectedRowKeys: selectedInvoiceLinesByInvoice[record.id], selectedRowKeys: selectedBillLinesByBill[record.id],
type: "checkbox", type: "checkbox",
}} }}
/> />
@@ -284,11 +280,9 @@ export function InvoicesListTableComponent({
return ( return (
<div> <div>
<Typography.Title level={4}> <Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
{t("invoices.labels.invoices")}
</Typography.Title>
<Table <Table
loading={loading} loading={billsQuery.loading}
size="small" size="small"
title={() => ( title={() => (
<div className="imex-table-header"> <div className="imex-table-header">
@@ -299,25 +293,23 @@ export function InvoicesListTableComponent({
<div> <div>
<Button <Button
onClick={() => { onClick={() => {
setInvoiceEnterContext({ setBillEnterContext({
actions: { refetch: invoicesQuery.refetch }, actions: { refetch: billsQuery.refetch },
context: { context: {
job, job,
}, },
}); });
}} }}
> >
{t("jobs.actions.postInvoices")} {t("jobs.actions.postbills")}
</Button> </Button>
<Button <Button
onClick={() => { onClick={() => {
setReconciliationContext({ setReconciliationContext({
actions: { refetch: invoicesQuery.refetch }, actions: { refetch: billsQuery.refetch },
context: { context: {
job, job,
invoices: bills: (billsQuery.data && billsQuery.data.bills) || [],
(invoicesQuery.data && invoicesQuery.data.invoices) ||
[],
}, },
}); });
}} }}
@@ -342,10 +334,10 @@ export function InvoicesListTableComponent({
pagination={{ position: "top", defaultPageSize: 25 }} pagination={{ position: "top", defaultPageSize: 25 }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={invoices} dataSource={bills}
onChange={handleTableChange} onChange={handleTableChange}
expandable={{ expandable={{
expandedRowKeys: [selectedInvoice], expandedRowKeys: [selectedBill],
onExpand: (expanded, record) => { onExpand: (expanded, record) => {
handleOnRowClick(expanded ? record : null); handleOnRowClick(expanded ? record : null);
}, },
@@ -354,7 +346,7 @@ export function InvoicesListTableComponent({
onSelect: (record) => { onSelect: (record) => {
handleOnRowClick(record); handleOnRowClick(record);
}, },
selectedRowKeys: [selectedInvoice], selectedRowKeys: [selectedBill],
type: "radio", type: "radio",
}} }}
onRow={(record, rowIndex) => { onRow={(record, rowIndex) => {
@@ -372,4 +364,4 @@ export function InvoicesListTableComponent({
</div> </div>
); );
} }
export default connect(null, mapDispatchToProps)(InvoicesListTableComponent); export default connect(null, mapDispatchToProps)(BillsListTableComponent);

View File

@@ -8,7 +8,7 @@ import { useTranslation } from "react-i18next";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
export default function InvoicesVendorsList() { export default function BillsVendorsList() {
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const history = useHistory(); const history = useHistory();
@@ -51,7 +51,7 @@ export default function InvoicesVendorsList() {
const handleOnRowClick = (record) => { const handleOnRowClick = (record) => {
if (record) { if (record) {
delete search.invoiceid; delete search.billid;
if (record.id) { if (record.id) {
search.vendorid = record.id; search.vendorid = record.id;
history.push({ search: queryString.stringify(search) }); history.push({ search: queryString.stringify(search) });

View File

@@ -116,9 +116,9 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract }) {
shopid: bodyshop.id, shopid: bodyshop.id,
ownerid: contract.job.ownerid, ownerid: contract.job.ownerid,
vehicleid: contract.job.vehicleid, vehicleid: contract.job.vehicleid,
federal_tax_rate: bodyshop.invoice_tax_rates.federal_tax_rate / 100, federal_tax_rate: bodyshop.bill_tax_rates.federal_tax_rate / 100,
state_tax_rate: bodyshop.invoice_tax_rates.state_tax_rate / 100, state_tax_rate: bodyshop.bill_tax_rates.state_tax_rate / 100,
local_tax_rate: bodyshop.invoice_tax_rates.local_tax_rate / 100, local_tax_rate: bodyshop.bill_tax_rates.local_tax_rate / 100,
clm_no: `${contract.job.clm_no}-CC`, clm_no: `${contract.job.clm_no}-CC`,
clm_total: 1234, //TODO clm_total: 1234, //TODO
ownr_fn: contract.job.owner.ownr_fn, ownr_fn: contract.job.owner.ownr_fn,

View File

@@ -20,7 +20,7 @@ const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("courtesyCarReturn")), toggleModalVisible: () => dispatch(toggleModalVisible("courtesyCarReturn")),
}); });
export function InvoiceEnterModalContainer({ export function BillEnterModalContainer({
courtesyCarReturnModal, courtesyCarReturnModal,
toggleModalVisible, toggleModalVisible,
bodyshop, bodyshop,
@@ -84,4 +84,4 @@ export function InvoiceEnterModalContainer({
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps
)(InvoiceEnterModalContainer); )(BillEnterModalContainer);

View File

@@ -1,7 +1,10 @@
import React from "react"; import React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; 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 DocumentsUploadComponent from "./documents-upload.component";
import { handleUpload } from "./documents-upload.utility"; import { handleUpload } from "./documents-upload.utility";
@@ -13,7 +16,7 @@ const mapStateToProps = createStructuredSelector({
export function DocumentsUploadContainer({ export function DocumentsUploadContainer({
jobId, jobId,
tagsArray, tagsArray,
invoiceId, billId,
currentUser, currentUser,
bodyshop, bodyshop,
callbackAfterUpload, callbackAfterUpload,
@@ -26,7 +29,7 @@ export function DocumentsUploadContainer({
bodyshop: bodyshop, bodyshop: bodyshop,
uploaded_by: currentUser.email, uploaded_by: currentUser.email,
jobId: jobId, jobId: jobId,
invoiceId: invoiceId, billId: billId,
tagsArray: tagsArray, tagsArray: tagsArray,
callback: callbackAfterUpload, callback: callbackAfterUpload,
}) })

View File

@@ -67,14 +67,7 @@ export const uploadToS3 = (
onProgress, onProgress,
context context
) => { ) => {
const { const { bodyshop, jobId, billId, uploaded_by, callback, tagsArray } = context;
bodyshop,
jobId,
invoiceId,
uploaded_by,
callback,
tagsArray,
} = context;
let timestamp = Math.floor(Date.now() / 1000); let timestamp = Math.floor(Date.now() / 1000);
let public_id = fileName; let public_id = fileName;
@@ -121,7 +114,7 @@ export const uploadToS3 = (
jobid: jobId, jobid: jobId,
uploaded_by: uploaded_by, uploaded_by: uploaded_by,
key: fileName, key: fileName,
invoiceid: invoiceId, billid: billId,
type: fileType, type: fileType,
}, },
], ],

View File

@@ -132,16 +132,16 @@ export default function GlobalSearch() {
}), }),
}, },
{ {
label: renderTitle(t("menus.header.search.invoices")), label: renderTitle(t("menus.header.search.bills")),
options: data.search_invoices.map((invoice) => { options: data.search_bills.map((bill) => {
return { return {
value: `${invoice.invoice_number}`, value: `${bill.invoice_number}`,
label: ( label: (
<Link to={`/manage/invoices?invoiceid=${invoice.id}`}> <Link to={`/manage/bills?billid=${bill.id}`}>
<div className="imex-flex-row"> <div className="imex-flex-row">
<span className="imex-flex-row__margin-large">{`${invoice.invoice_number}`}</span> <span className="imex-flex-row__margin-large">{`${bill.invoice_number}`}</span>
<span className="imex-flex-row__margin-large">{`${invoice.vendor.name}`}</span> <span className="imex-flex-row__margin-large">{`${bill.vendor.name}`}</span>
<span className="imex-flex-row__margin-large">{`${invoice.date}`}</span> <span className="imex-flex-row__margin-large">{`${bill.date}`}</span>
</div> </div>
</Link> </Link>
), ),

View File

@@ -43,8 +43,8 @@ const mapStateToProps = createStructuredSelector({
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setInvoiceEnterContext: (context) => setBillEnterContext: (context) =>
dispatch(setModalContext({ context: context, modal: "invoiceEnter" })), dispatch(setModalContext({ context: context, modal: "billEnter" })),
setTimeTicketContext: (context) => setTimeTicketContext: (context) =>
dispatch(setModalContext({ context: context, modal: "timeTicket" })), dispatch(setModalContext({ context: context, modal: "timeTicket" })),
setPaymentContext: (context) => setPaymentContext: (context) =>
@@ -57,7 +57,7 @@ function Header({
currentUser, currentUser,
selectedHeader, selectedHeader,
signOutStart, signOutStart,
setInvoiceEnterContext, setBillEnterContext,
setTimeTicketContext, setTimeTicketContext,
setPaymentContext, setPaymentContext,
recentItems, recentItems,
@@ -183,20 +183,20 @@ function Header({
</span> </span>
} }
> >
<Menu.Item key="invoices"> <Menu.Item key="bills">
<Link to="/manage/invoices">{t("menus.header.invoices")}</Link> <Link to="/manage/bills">{t("menus.header.bills")}</Link>
</Menu.Item> </Menu.Item>
<Menu.Item <Menu.Item
key="enterinvoices" key="enterbills"
onClick={() => { onClick={() => {
setInvoiceEnterContext({ setBillEnterContext({
actions: {}, actions: {},
context: {}, context: {},
}); });
}} }}
> >
<Icon component={FaFileInvoiceDollar} /> <Icon component={FaFileInvoiceDollar} />
{t("menus.header.enterinvoices")} {t("menus.header.enterbills")}
</Menu.Item> </Menu.Item>
<Menu.Divider /> <Menu.Divider />
<Menu.Item key="allpayments"> <Menu.Item key="allpayments">

View File

@@ -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;
// }
// }

View File

@@ -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
// };
// }}
// />
// );
// }

View File

@@ -4,9 +4,9 @@ import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.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(); const { t } = useTranslation();
if (loading) return <LoadingSkeleton />; if (loading) return <LoadingSkeleton />;
@@ -17,10 +17,10 @@ export default function JobInvoiceTotals({ loading, invoices, jobTotals }) {
const totals = jobTotals; const totals = jobTotals;
let invoiceTotals = Dinero({ amount: 0 }); let billTotals = Dinero({ amount: 0 });
invoices.forEach((i) => bills.forEach((i) =>
i.invoicelines.forEach((il) => { i.billlines.forEach((il) => {
invoiceTotals = invoiceTotals.add( billTotals = billTotals.add(
Dinero({ Dinero({
amount: Math.round( amount: Math.round(
(il.actual_cost || 0) * (i.is_credit_memo ? -1 : 1) * 100 (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 ( return (
<div className="job-invoices-totals-container"> <div className="job-bills-totals-container">
<Statistic <Statistic
title={t("jobs.labels.partstotal")} title={t("jobs.labels.partstotal")}
value={Dinero(totals.parts.parts.total).toFormat()} 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()} value={Dinero(totals.parts.sublets.total).toFormat()}
/> />
<Statistic <Statistic
title={t("invoices.labels.retailtotal")} title={t("bills.labels.retailtotal")}
value={invoiceTotals.toFormat()} value={billTotals.toFormat()}
/> />
<Statistic <Statistic
title={t("invoices.labels.discrepancy")} title={t("bills.labels.discrepancy")}
valueStyle={{ valueStyle={{
color: discrepancy.getAmount === 0 ? "green" : "red", color: discrepancy.getAmount === 0 ? "green" : "red",
}} }}

View File

@@ -1,4 +1,4 @@
.job-invoices-totals-container { .job-bills-totals-container {
margin: 0rem 2rem; margin: 0rem 2rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@@ -51,28 +51,25 @@ export function JobCostingModalComponent({ bodyshop, job }) {
{ parts: {}, labor: {} } { parts: {}, labor: {} }
); );
const invoiceTotalsByProfitCenter = job.invoices.reduce( const billTotalsByProfitCenter = job.bills.reduce((bill_acc, bill_val) => {
(inv_acc, inv_val) => { //At the invoice level.
//At the invoice level. bill_val.billlines.map((line_val) => {
inv_val.invoicelines.map((line_val) => { //At the invoice line level.
//At the invoice line level. //console.log("JobCostingPartsTable -> line_val", line_val);
//console.log("JobCostingPartsTable -> line_val", line_val); if (!!!bill_acc[line_val.cost_center])
if (!!!inv_acc[line_val.cost_center]) bill_acc[line_val.cost_center] = Dinero();
inv_acc[line_val.cost_center] = Dinero();
inv_acc[line_val.cost_center] = inv_acc[line_val.cost_center].add( bill_acc[line_val.cost_center] = bill_acc[line_val.cost_center].add(
Dinero({ Dinero({
amount: Math.round((line_val.actual_cost || 0) * 100), amount: Math.round((line_val.actual_cost || 0) * 100),
}) })
.multiply(line_val.quantity) .multiply(line_val.quantity)
.multiply(inv_val.is_credit_memo ? -1 : 1) .multiply(bill_val.is_credit_memo ? -1 : 1)
); );
return null; return null;
}); });
return inv_acc; return bill_acc;
}, }, {});
{}
);
const ticketTotalsByProfitCenter = job.timetickets.reduce( const ticketTotalsByProfitCenter = job.timetickets.reduce(
(ticket_acc, ticket_val) => { (ticket_acc, ticket_val) => {
@@ -114,12 +111,11 @@ export function JobCostingModalComponent({ bodyshop, job }) {
const cost_labor = const cost_labor =
ticketTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 }); ticketTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 });
const cost_parts = const cost_parts = billTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 });
invoiceTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 });
const cost = ( const cost = (billTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 })).add(
invoiceTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 }) ticketTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 })
).add(ticketTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 })); );
const totalSales = sale_labor.add(sale_parts); const totalSales = sale_labor.add(sale_parts);
const gpdollars = totalSales.subtract(cost); const gpdollars = totalSales.subtract(cost);
const gppercent = ( const gppercent = (

View File

@@ -5,8 +5,8 @@ import { useTranslation } from "react-i18next";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
export default function JobReconciliationInvoiceTable({ export default function JobReconciliationBillsTable({
invoiceLineState, billLineState,
invoiceLineData, invoiceLineData,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -15,12 +15,12 @@ export default function JobReconciliationInvoiceTable({
sortedInfo: {}, sortedInfo: {},
}); });
const [selectedLines, setSelectedLines] = invoiceLineState; const [selectedLines, setSelectedLines] = billLineState;
const [total, setTotal] = useState(Dinero({ amount: 0 }).toFormat()); const [total, setTotal] = useState(Dinero({ amount: 0 }).toFormat());
const columns = [ const columns = [
{ {
title: t("invoicelines.fields.line_desc"), title: t("billlines.fields.line_desc"),
dataIndex: "line_desc", dataIndex: "line_desc",
key: "line_desc", key: "line_desc",
sorter: (a, b) => alphaSort(a.line_desc, b.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, state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
}, },
{ {
title: t("invoicelines.fields.retail"), title: t("billlines.fields.retail"),
dataIndex: "actual_price", dataIndex: "actual_price",
key: "actual_price", key: "actual_price",
sorter: (a, b) => a.actual_price - b.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", dataIndex: "actual_cost",
key: "actual_cost", key: "actual_cost",
sorter: (a, b) => a.actual_cost - b.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", dataIndex: "quantity",
key: "quantity", key: "quantity",
sorter: (a, b) => a.quantity - b.quantity, sorter: (a, b) => a.quantity - b.quantity,
@@ -58,7 +58,7 @@ export default function JobReconciliationInvoiceTable({
state.sortedInfo.columnKey === "quantity" && state.sortedInfo.order, 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", dataIndex: "is_credit_memo",
key: "is_credit_memo", key: "is_credit_memo",
sorter: (a, b) => a.is_credit_memo - b.is_credit_memo, sorter: (a, b) => a.is_credit_memo - b.is_credit_memo,
@@ -95,11 +95,11 @@ export default function JobReconciliationInvoiceTable({
return ( return (
<div> <div>
<Table <Table
size='small' size="small"
title={() => <div></div>} title={() => <div></div>}
pagination={{ position: "top", defaultPageSize: 25 }} pagination={{ position: "top", defaultPageSize: 25 }}
columns={columns} columns={columns}
rowKey='id' rowKey="id"
dataSource={invoiceLineData} dataSource={invoiceLineData}
onChange={handleTableChange} onChange={handleTableChange}
rowSelection={{ rowSelection={{
@@ -107,7 +107,7 @@ export default function JobReconciliationInvoiceTable({
selectedRowKeys: selectedLines, selectedRowKeys: selectedLines,
}} }}
/> />
<Statistic value={total} title='total' /> <Statistic value={total} title="total" />
</div> </div>
); );
} }

View File

@@ -1,16 +1,16 @@
import { Col, Row } from "antd"; import { Col, Row } from "antd";
import React, { useState } from "react"; 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"; 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 jobLineState = useState([]);
const invoiceLineState = useState([]); const billLineState = useState([]);
const invoiceLineData = const invoiceLineData =
invoices bills
.map((i) => .map((i) =>
i.invoicelines.map((il) => { i.billlines.map((il) => {
return { ...il, is_credit_memo: i.is_credit_memo }; return { ...il, is_credit_memo: i.is_credit_memo };
}) })
) )
@@ -28,9 +28,9 @@ export default function JobReconciliationModalComponent({ job, invoices }) {
/> />
</Col> </Col>
<Col span={12}> <Col span={12}>
<JobReconciliationInvoicesTable <JobReconciliationBillsTable
invoiceLineData={invoiceLineData} invoiceLineData={invoiceLineData}
invoiceLineState={invoiceLineState} billLineState={billLineState}
/> />
</Col> </Col>
</Row> </Row>

View File

@@ -5,27 +5,22 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { toggleModalVisible } from "../../redux/modals/modals.actions"; import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectReconciliation } from "../../redux/modals/modals.selectors"; import { selectReconciliation } from "../../redux/modals/modals.selectors";
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
import JobReconciliationModalComponent from "./job-reconciliation-modal.component"; import JobReconciliationModalComponent from "./job-reconciliation-modal.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
reconciliationModal: selectReconciliation, reconciliationModal: selectReconciliation,
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("reconciliation")), toggleModalVisible: () => dispatch(toggleModalVisible("reconciliation")),
}); });
function InvoiceEnterModalContainer({ function JobReconciliationModalContainer({
reconciliationModal, reconciliationModal,
toggleModalVisible, toggleModalVisible,
bodyshop,
currentUser,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { context, visible } = reconciliationModal; const { context, visible } = reconciliationModal;
const { job, invoices } = context; const { job, bills } = context;
const handleCancel = () => { const handleCancel = () => {
toggleModalVisible(); toggleModalVisible();
@@ -39,8 +34,9 @@ function InvoiceEnterModalContainer({
okText={t("general.actions.save")} okText={t("general.actions.save")}
onOk={handleCancel} onOk={handleCancel}
onCancel={handleCancel} onCancel={handleCancel}
destroyOnClose> destroyOnClose
<JobReconciliationModalComponent job={job} invoices={invoices} /> >
<JobReconciliationModalComponent job={job} bills={bills} />
</Modal> </Modal>
); );
} }
@@ -48,4 +44,4 @@ function InvoiceEnterModalContainer({
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps
)(InvoiceEnterModalContainer); )(JobReconciliationModalContainer);

View File

@@ -20,8 +20,8 @@ const mapStateToProps = createStructuredSelector({
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setScheduleContext: (context) => setScheduleContext: (context) =>
dispatch(setModalContext({ context: context, modal: "schedule" })), dispatch(setModalContext({ context: context, modal: "schedule" })),
setInvoiceEnterContext: (context) => setBillEnterContext: (context) =>
dispatch(setModalContext({ context: context, modal: "invoiceEnter" })), dispatch(setModalContext({ context: context, modal: "billEnter" })),
setPaymentContext: (context) => setPaymentContext: (context) =>
dispatch(setModalContext({ context: context, modal: "payment" })), dispatch(setModalContext({ context: context, modal: "payment" })),
setJobCostingContext: (context) => setJobCostingContext: (context) =>
@@ -33,7 +33,7 @@ export function JobsDetailHeaderActions({
bodyshop, bodyshop,
refetch, refetch,
setScheduleContext, setScheduleContext,
setInvoiceEnterContext, setBillEnterContext,
setPaymentContext, setPaymentContext,
setJobCostingContext, setJobCostingContext,
}) { }) {
@@ -147,11 +147,11 @@ export function JobsDetailHeaderActions({
</Popconfirm> </Popconfirm>
</Menu.Item> </Menu.Item>
<Menu.Item <Menu.Item
key="postinvoices" key="postbills"
onClick={() => { onClick={() => {
logImEXEvent("job_header_enter_invoice"); logImEXEvent("job_header_enter_bills");
setInvoiceEnterContext({ setBillEnterContext({
actions: { refetch: refetch }, actions: { refetch: refetch },
context: { context: {
job: job, job: job,
@@ -159,7 +159,7 @@ export function JobsDetailHeaderActions({
}); });
}} }}
> >
{t("jobs.actions.postInvoices")} {t("jobs.actions.postbills")}
</Menu.Item> </Menu.Item>
<Menu.Item <Menu.Item
disabled={!!job.date_invoiced || !jobInPostProduction} disabled={!!job.date_invoiced || !jobInPostProduction}

View File

@@ -1,8 +1,8 @@
import { Col, Row } from "antd"; import { Col, Row } from "antd";
import React from "react"; import React from "react";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import InvoicesListTableComponent from "../invoices-list-table/invoices-list-table.component"; import BillsListTable from "../bills-list-table/bills-list-table.component";
import JobInvoicesTotalsComponent from "../job-invoices-total/job-invoices-total.component"; import JobBillsTotal from "../job-bills-total/job-bills-total.component";
import PartsOrderModal from "../parts-order-modal/parts-order-modal.container"; import PartsOrderModal from "../parts-order-modal/parts-order-modal.container";
import PartsOrderListTableComponent from "../parts-order-list-table/parts-order-list-table.component"; import PartsOrderListTableComponent from "../parts-order-list-table/parts-order-list-table.component";
const tableCol = { const tableCol = {
@@ -25,37 +25,33 @@ const totalsCol = {
export default function JobsDetailPliComponent({ export default function JobsDetailPliComponent({
job, job,
invoicesQuery, billsQuery,
handleInvoiceOnRowClick, handleBillOnRowClick,
handlePartsOrderOnRowClick, handlePartsOrderOnRowClick,
selectedInvoice,
}) { }) {
return ( return (
<div> <div>
<PartsOrderModal /> <PartsOrderModal />
{invoicesQuery.error ? ( {billsQuery.error ? (
<AlertComponent message={invoicesQuery.error.message} type="error" /> <AlertComponent message={billsQuery.error.message} type="error" />
) : null} ) : null}
<Row> <Row>
<Col {...tableCol}> <Col {...tableCol}>
<PartsOrderListTableComponent <PartsOrderListTableComponent
job={job} job={job}
loading={invoicesQuery.loading}
handleOnRowClick={handlePartsOrderOnRowClick} handleOnRowClick={handlePartsOrderOnRowClick}
selectedInvoice={selectedInvoice} billsQuery={billsQuery}
invoicesQuery={invoicesQuery}
/> />
<InvoicesListTableComponent <BillsListTable
job={job} job={job}
loading={invoicesQuery.loading} handleOnRowClick={handleBillOnRowClick}
handleOnRowClick={handleInvoiceOnRowClick} billsQuery={billsQuery}
invoicesQuery={invoicesQuery}
/> />
</Col> </Col>
<Col {...totalsCol}> <Col {...totalsCol}>
<JobInvoicesTotalsComponent <JobBillsTotal
invoices={invoicesQuery.data ? invoicesQuery.data.invoices : []} bills={billsQuery.data ? billsQuery.data.bills : []}
loading={invoicesQuery.loading} loading={billsQuery.loading}
jobTotals={job.job_totals} jobTotals={job.job_totals}
/> />
</Col> </Col>

View File

@@ -2,25 +2,25 @@ import { useQuery } from "@apollo/react-hooks";
import queryString from "query-string"; import queryString from "query-string";
import React from "react"; import React from "react";
import { useHistory, useLocation } from "react-router-dom"; 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"; import JobsDetailPliComponent from "./jobs-detail-pli.component";
export default function JobsDetailPliContainer({ job }) { export default function JobsDetailPliContainer({ job }) {
const invoicesQuery = useQuery(QUERY_INVOICES_BY_JOBID, { const billsQuery = useQuery(QUERY_BILLS_BY_JOBID, {
variables: { jobid: job.id }, variables: { jobid: job.id },
}); });
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const history = useHistory(); const history = useHistory();
const handleInvoiceOnRowClick = (record) => { const handleBillOnRowClick = (record) => {
if (record) { if (record) {
if (record.id) { if (record.id) {
search.invoiceid = record.id; search.billid = record.id;
history.push({ search: queryString.stringify(search) }); history.push({ search: queryString.stringify(search) });
} }
} else { } else {
delete search.invoiceid; delete search.billid;
history.push({ search: queryString.stringify(search) }); history.push({ search: queryString.stringify(search) });
} }
}; };
@@ -40,8 +40,8 @@ export default function JobsDetailPliContainer({ job }) {
return ( return (
<JobsDetailPliComponent <JobsDetailPliComponent
job={job} job={job}
invoicesQuery={invoicesQuery} billsQuery={billsQuery}
handleInvoiceOnRowClick={handleInvoiceOnRowClick} handleBillOnRowClick={handleBillOnRowClick}
handlePartsOrderOnRowClick={handlePartsOrderOnRowClick} handlePartsOrderOnRowClick={handlePartsOrderOnRowClick}
/> />
); );

View File

@@ -8,8 +8,8 @@ function JobsDocumentsComponent({
data, data,
jobId, jobId,
refetch, refetch,
invoiceId, billId,
invoicesCallback, billsCallback,
}) { }) {
const [galleryImages, setgalleryImages] = useState([]); const [galleryImages, setgalleryImages] = useState([]);
@@ -34,18 +34,18 @@ function JobsDocumentsComponent({
}, [data, setgalleryImages]); }, [data, setgalleryImages]);
return ( return (
<div className='clearfix'> <div className="clearfix">
<DocumentsUploadContainer <DocumentsUploadContainer
jobId={jobId} jobId={jobId}
invoiceId={invoiceId} billId={billId}
callbackAfterUpload={invoicesCallback || refetch} callbackAfterUpload={billsCallback || refetch}
tagsArray={["test"]} tagsArray={["test"]}
/> />
<JobsDocumentsDownloadButton galleryImages={galleryImages} /> <JobsDocumentsDownloadButton galleryImages={galleryImages} />
<JobsDocumentsDeleteButton <JobsDocumentsDeleteButton
galleryImages={galleryImages} galleryImages={galleryImages}
deletionCallback={invoicesCallback || refetch} deletionCallback={billsCallback || refetch}
/> />
<Gallery <Gallery

View File

@@ -7,14 +7,14 @@ import JobDocuments from "./jobs-documents-gallery.component";
export default function JobsDocumentsContainer({ export default function JobsDocumentsContainer({
jobId, jobId,
invoiceId, billId,
documentsList, documentsList,
invoicesCallback, billsCallback,
}) { }) {
const { loading, error, data, refetch } = useQuery(GET_DOCUMENTS_BY_JOB, { const { loading, error, data, refetch } = useQuery(GET_DOCUMENTS_BY_JOB, {
variables: { jobId: jobId }, variables: { jobId: jobId },
fetchPolicy: "network-only", fetchPolicy: "network-only",
skip: !!invoiceId, skip: !!billId,
}); });
if (loading) return <LoadingSpinner />; if (loading) return <LoadingSpinner />;
@@ -23,10 +23,10 @@ export default function JobsDocumentsContainer({
return ( return (
<JobDocuments <JobDocuments
data={(data && data.documents) || documentsList || []} data={(data && data.documents) || documentsList || []}
invoiceId={invoiceId} billId={billId}
jobId={jobId} jobId={jobId}
refetch={refetch} refetch={refetch}
invoicesCallback={invoicesCallback} billsCallback={billsCallback}
/> />
); );
} }

View File

@@ -110,7 +110,6 @@ export function JobsExportAllButton({
}); });
} }
} }
//Set the list of selected invoices to be nothing.
if (!!completedCallback) completedCallback([]); if (!!completedCallback) completedCallback([]);
if (!!loadingCallback) loadingCallback(false); if (!!loadingCallback) loadingCallback(false);
setLoading(false); setLoading(false);

View File

@@ -20,16 +20,16 @@ const mapStateToProps = createStructuredSelector({
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setInvoiceEnterContext: (context) => setBillEnterContext: (context) =>
dispatch(setModalContext({ context: context, modal: "invoiceEnter" })), dispatch(setModalContext({ context: context, modal: "billEnter" })),
}); });
export function PartsOrderListTableComponent({ export function PartsOrderListTableComponent({
setInvoiceEnterContext, setBillEnterContext,
bodyshop, bodyshop,
job, job,
loading,
invoicesQuery, billsQuery,
handleOnRowClick, handleOnRowClick,
}) { }) {
const responsibilityCenters = bodyshop.md_responsibility_centers; const responsibilityCenters = bodyshop.md_responsibility_centers;
@@ -41,10 +41,8 @@ export function PartsOrderListTableComponent({
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const selectedpartsorder = search.partsorderid; const selectedpartsorder = search.partsorderid;
const parts_orders = invoicesQuery.data const parts_orders = billsQuery.data ? billsQuery.data.parts_orders : [];
? invoicesQuery.data.parts_orders const { refetch } = billsQuery;
: [];
const { refetch } = invoicesQuery;
const columns = [ const columns = [
{ {
title: t("vendors.fields.name"), title: t("vendors.fields.name"),
@@ -93,15 +91,15 @@ export function PartsOrderListTableComponent({
render: (text, record) => ( render: (text, record) => (
<Button <Button
onClick={() => { onClick={() => {
logImEXEvent("parts_order_receive_invoice"); logImEXEvent("parts_order_receive_bill");
setInvoiceEnterContext({ setBillEnterContext({
actions: { refetch: refetch }, actions: { refetch: refetch },
context: { context: {
job: job, job: job,
invoice: { bill: {
vendorid: record.vendor.id, vendorid: record.vendor.id,
invoicelines: record.parts_order_lines.map((pol) => { billlines: record.parts_order_lines.map((pol) => {
return { return {
joblineid: pol.job_line_id, joblineid: pol.job_line_id,
line_desc: pol.line_desc, line_desc: pol.line_desc,
@@ -119,7 +117,7 @@ export function PartsOrderListTableComponent({
}); });
}} }}
> >
{t("parts_orders.actions.receiveinvoice")} {t("parts_orders.actions.receivebill")}
</Button> </Button>
), ),
}, },
@@ -227,7 +225,7 @@ export function PartsOrderListTableComponent({
{t("parts_orders.labels.parts_orders")} {t("parts_orders.labels.parts_orders")}
</Typography.Title> </Typography.Title>
<Table <Table
loading={loading} loading={billsQuery.loading}
size="small" size="small"
title={() => ( title={() => (
<div className="imex-table-header"> <div className="imex-table-header">

View File

@@ -4,7 +4,7 @@ import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { 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 { UPDATE_JOB_LINE_STATUS } from "../../graphql/jobs-lines.queries";
import { INSERT_NEW_PARTS_ORDERS } from "../../graphql/parts-orders.queries"; import { INSERT_NEW_PARTS_ORDERS } from "../../graphql/parts-orders.queries";
import { QUERY_ALL_VENDORS_FOR_ORDER } from "../../graphql/vendors.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 [insertPartOrder] = useMutation(INSERT_NEW_PARTS_ORDERS);
const [updateJobLines] = useMutation(UPDATE_JOB_LINE_STATUS); const [updateJobLines] = useMutation(UPDATE_JOB_LINE_STATUS);
const [insertInvoice] = useMutation(INSERT_NEW_INVOICE); const [insertBill] = useMutation(INSERT_NEW_BILL);
const handleFinish = async (values) => { const handleFinish = async (values) => {
logImEXEvent("parts_order_insert"); logImEXEvent("parts_order_insert");
@@ -120,9 +120,9 @@ export function PartsOrderModalContainer({
jobid: jobId, jobid: jobId,
total: 0, total: 0,
invoice_number: `${jobId}`, invoice_number: `${jobId}`,
federal_tax_rate: bodyshop.invoice_tax_rates.federal_tax_rate || 0, federal_tax_rate: bodyshop.bill_tax_rates.federal_tax_rate || 0,
state_tax_rate: bodyshop.invoice_tax_rates.state_tax_rate || 0, state_tax_rate: bodyshop.bill_tax_rates.state_tax_rate || 0,
local_tax_rate: bodyshop.invoice_tax_rates.local_tax_rate || 0, local_tax_rate: bodyshop.bill_tax_rates.local_tax_rate || 0,
invoicelines: { invoicelines: {
data: values.parts_order_lines.data.map((p) => { data: values.parts_order_lines.data.map((p) => {
return { return {
@@ -138,8 +138,8 @@ export function PartsOrderModalContainer({
}, },
}; };
await insertInvoice({ await insertBill({
variables: { invoice: invoiceToPost }, variables: { bill: invoiceToPost },
}); });
} }

View File

@@ -6,7 +6,7 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { auth } from "../../firebase/firebase.utils"; 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 { selectBodyshop } from "../../redux/user/user.selectors";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
@@ -14,15 +14,15 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
export function InvoiceExportAllButton({ export function PayableExportAll({
bodyshop, bodyshop,
invoiceIds, billids,
disabled, disabled,
loadingCallback, loadingCallback,
completedCallback, completedCallback,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [updateInvoice] = useMutation(UPDATE_INVOICES); const [updateBill] = useMutation(UPDATE_BILLS);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const handleQbxml = async () => { const handleQbxml = async () => {
@@ -35,7 +35,7 @@ export function InvoiceExportAllButton({
try { try {
QbXmlResponse = await axios.post( QbXmlResponse = await axios.post(
"/accounting/qbxml/payables", "/accounting/qbxml/payables",
{ invoices: invoiceIds }, { bills: billids },
{ {
headers: { headers: {
Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`, Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`,
@@ -45,7 +45,7 @@ export function InvoiceExportAllButton({
} catch (error) { } catch (error) {
console.log("Error getting QBXML from Server.", error); console.log("Error getting QBXML from Server.", error);
notification["error"]({ notification["error"]({
message: t("invoices.errors.exporting", { message: t("bills.errors.exporting", {
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message), error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
}), }),
}); });
@@ -64,7 +64,7 @@ export function InvoiceExportAllButton({
} catch (error) { } catch (error) {
console.log("Error connecting to quickbooks or partner.", error); console.log("Error connecting to quickbooks or partner.", error);
notification["error"]({ notification["error"]({
message: t("invoices.errors.exporting-partner"), message: t("bills.errors.exporting-partner"),
}); });
if (!!loadingCallback) loadingCallback(false); if (!!loadingCallback) loadingCallback(false);
setLoading(false); setLoading(false);
@@ -80,36 +80,35 @@ export function InvoiceExportAllButton({
//Uh oh. At least one was no good. //Uh oh. At least one was no good.
failedTransactions.map((ft) => failedTransactions.map((ft) =>
notification["error"]({ notification["error"]({
message: t("invoices.errors.exporting", { message: t("bills.errors.exporting", {
error: ft.errorMessage || "", error: ft.errorMessage || "",
}), }),
}) })
); );
} }
if (successfulTransactions.length > 0) { if (successfulTransactions.length > 0) {
const invoiceUpdateResponse = await updateInvoice({ const billUpdateResponse = await updateBill({
variables: { variables: {
invoiceIdList: successfulTransactions.map((st) => st.id), billIdList: successfulTransactions.map((st) => st.id),
invoice: { bill: {
exported: true, exported: true,
exported_at: new Date(), exported_at: new Date(),
}, },
}, },
}); });
if (!!!invoiceUpdateResponse.errors) { if (!!!billUpdateResponse.errors) {
notification["success"]({ notification["success"]({
message: t("jobs.successes.exported"), message: t("jobs.successes.exported"),
}); });
} else { } else {
notification["error"]({ notification["error"]({
message: t("jobs.errors.exporting", { 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 (!!completedCallback) completedCallback([]);
if (!!loadingCallback) loadingCallback(false); if (!!loadingCallback) loadingCallback(false);
setLoading(false); setLoading(false);
@@ -127,4 +126,4 @@ export function InvoiceExportAllButton({
); );
} }
export default connect(mapStateToProps, null)(InvoiceExportAllButton); export default connect(mapStateToProps, null)(PayableExportAll);

View File

@@ -6,7 +6,7 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { auth } from "../../firebase/firebase.utils"; 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 { selectBodyshop } from "../../redux/user/user.selectors";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
@@ -14,14 +14,14 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
export function InvoiceExportButton({ export function PayableExportButton({
bodyshop, bodyshop,
invoiceId, billId,
disabled, disabled,
loadingCallback, loadingCallback,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [updateInvoice] = useMutation(UPDATE_INVOICES); const [updateBill] = useMutation(UPDATE_BILLS);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const handleQbxml = async () => { const handleQbxml = async () => {
@@ -34,7 +34,7 @@ export function InvoiceExportButton({
try { try {
QbXmlResponse = await axios.post( QbXmlResponse = await axios.post(
"/accounting/qbxml/payables", "/accounting/qbxml/payables",
{ invoices: [invoiceId] }, { bills: [billId] },
{ {
headers: { headers: {
Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`, Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`,
@@ -44,7 +44,7 @@ export function InvoiceExportButton({
} catch (error) { } catch (error) {
console.log("Error getting QBXML from Server.", error); console.log("Error getting QBXML from Server.", error);
notification["error"]({ notification["error"]({
message: t("invoices.errors.exporting", { message: t("bills.errors.exporting", {
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message), error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
}), }),
}); });
@@ -63,7 +63,7 @@ export function InvoiceExportButton({
} catch (error) { } catch (error) {
console.log("Error connecting to quickbooks or partner.", error); console.log("Error connecting to quickbooks or partner.", error);
notification["error"]({ notification["error"]({
message: t("invoices.errors.exporting-partner"), message: t("bills.errors.exporting-partner"),
}); });
if (!!loadingCallback) loadingCallback(false); if (!!loadingCallback) loadingCallback(false);
setLoading(false); setLoading(false);
@@ -79,30 +79,30 @@ export function InvoiceExportButton({
//Uh oh. At least one was no good. //Uh oh. At least one was no good.
failedTransactions.map((ft) => failedTransactions.map((ft) =>
notification["error"]({ notification["error"]({
message: t("invoices.errors.exporting", { message: t("bills.errors.exporting", {
error: ft.errorMessage || "", error: ft.errorMessage || "",
}), }),
}) })
); );
} }
if (successfulTransactions.length > 0) { if (successfulTransactions.length > 0) {
const invoiceUpdateResponse = await updateInvoice({ const billUpdateResponse = await updateBill({
variables: { variables: {
invoiceIdList: successfulTransactions.map((st) => st.id), billIdList: successfulTransactions.map((st) => st.id),
invoice: { bill: {
exported: true, exported: true,
exported_at: new Date(), exported_at: new Date(),
}, },
}, },
}); });
if (!!!invoiceUpdateResponse.errors) { if (!!!billUpdateResponse.errors) {
notification["success"]({ notification["success"]({
message: t("jobs.successes.exported"), message: t("jobs.successes.exported"),
}); });
} else { } else {
notification["error"]({ notification["error"]({
message: t("jobs.errors.exporting", { 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);

View File

@@ -25,7 +25,7 @@ export function PaymentFormComponent({ form, stripeStateArr, bodyshop }) {
<div> <div>
<Form.Item <Form.Item
name="jobid" name="jobid"
label={t("invoices.fields.ro_number")} label={t("bills.fields.ro_number")}
rules={[ rules={[
{ {
required: true, required: true,

View File

@@ -32,7 +32,7 @@ const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("payment")), toggleModalVisible: () => dispatch(toggleModalVisible("payment")),
}); });
function InvoiceEnterModalContainer({ function BillEnterModalContainer({
paymentModal, paymentModal,
toggleModalVisible, toggleModalVisible,
bodyshop, bodyshop,
@@ -189,7 +189,7 @@ function InvoiceEnterModalContainer({
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps
)(InvoiceEnterModalContainer); )(BillEnterModalContainer);
// const pr = stripe.paymentRequest({ // const pr = stripe.paymentRequest({
// country: "CA", // country: "CA",

View File

@@ -22,9 +22,9 @@ export default {
"jobs:close": 5, "jobs:close": 5,
"jobs:detail": 1, "jobs:detail": 1,
"invoices:enter": 2, "bills:enter": 2,
"invoices:view": 2, "bills:view": 2,
"invoices:list": 2, "bills:list": 2,
"employees:page": 5, "employees:page": 5,

View File

@@ -137,7 +137,7 @@ export default function ShopInfoComponent({ form, saveLoading }) {
<LayoutFormRow> <LayoutFormRow>
<Form.Item <Form.Item
label={t("bodyshop.fields.invoice_federal_tax_rate")} label={t("bodyshop.fields.invoice_federal_tax_rate")}
name={["invoice_tax_rates", "federal_tax_rate"]} name={["bill_tax_rates", "federal_tax_rate"]}
rules={[ rules={[
{ {
required: true, required: true,
@@ -149,7 +149,7 @@ export default function ShopInfoComponent({ form, saveLoading }) {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.invoice_state_tax_rate")} label={t("bodyshop.fields.invoice_state_tax_rate")}
name={["invoice_tax_rates", "state_tax_rate"]} name={["bill_tax_rates", "state_tax_rate"]}
rules={[ rules={[
{ {
required: true, required: true,
@@ -161,7 +161,7 @@ export default function ShopInfoComponent({ form, saveLoading }) {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.invoice_local_tax_rate")} label={t("bodyshop.fields.invoice_local_tax_rate")}
name={["invoice_tax_rates", "local_tax_rate"]} name={["bill_tax_rates", "local_tax_rate"]}
rules={[ rules={[
{ {
required: true, required: true,

View File

@@ -238,38 +238,38 @@ export default function ShopInfoRbacComponent({ form }) {
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.invoices.enter")} label={t("bodyshop.fields.rbac.bills.enter")}
rules={[ rules={[
{ {
required: true, required: true,
message: t("general.validation.required"), message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "invoices:enter"]} name={["md_rbac", "bills:enter"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.invoices.view")} label={t("bodyshop.fields.rbac.bills.view")}
rules={[ rules={[
{ {
required: true, required: true,
message: t("general.validation.required"), message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "invoices:view"]} name={["md_rbac", "bills:view"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.invoices.list")} label={t("bodyshop.fields.rbac.bills.list")}
rules={[ rules={[
{ {
required: true, required: true,
message: t("general.validation.required"), message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "invoices:list"]} name={["md_rbac", "bills:list"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>

View File

@@ -58,7 +58,7 @@ export default function VendorsFormComponent({
}} }}
style={{ width: "100%" }} style={{ width: "100%" }}
> >
{t("invoicelines.actions.newline")} {t("billlines.actions.newline")}
</Button> </Button>
</Form.Item> </Form.Item>
</div> </div>

View File

@@ -28,9 +28,9 @@ export const QUERY_JOBS_FOR_EXPORT = gql`
} }
`; `;
export const QUERY_INVOICES_FOR_EXPORT = gql` export const QUERY_BILLS_FOR_EXPORT = gql`
query QUERY_INVOICES_FOR_EXPORT { query QUERY_BILLS_FOR_EXPORT {
invoices(where: { exported: { _eq: false } }) { bills(where: { exported: { _eq: false } }) {
id id
exported exported
date date

View 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
}
}
}
`;

View File

@@ -1,8 +1,8 @@
import gql from "graphql-tag"; import gql from "graphql-tag";
export const INSERT_NEW_INVOICE = gql` export const INSERT_NEW_BILL = gql`
mutation INSERT_NEW_INVOICE($invoice: [invoices_insert_input!]!) { mutation INSERT_NEW_BILL($bill: [bills_insert_input!]!) {
insert_invoices(objects: $invoice) { insert_bills(objects: $bill) {
returning { returning {
id id
} }
@@ -10,14 +10,14 @@ export const INSERT_NEW_INVOICE = gql`
} }
`; `;
export const QUERY_ALL_INVOICES_PAGINATED = gql` export const QUERY_ALL_BILLS_PAGINATED = gql`
query QUERY_ALL_INVOICES_PAGINATED( query QUERY_ALL_BILLS_PAGINATED(
$search: String $search: String
$offset: Int $offset: Int
$limit: Int $limit: Int
$order: [invoices_order_by!]! $order: [bills_order_by!]!
) { ) {
search_invoices( search_bills(
args: { search: $search } args: { search: $search }
offset: $offset offset: $offset
limit: $limit limit: $limit
@@ -39,7 +39,7 @@ export const QUERY_ALL_INVOICES_PAGINATED = gql`
id id
ro_number ro_number
} }
invoicelines { billlines {
actual_price actual_price
quantity quantity
actual_cost actual_cost
@@ -48,7 +48,7 @@ export const QUERY_ALL_INVOICES_PAGINATED = gql`
line_desc line_desc
} }
} }
search_invoices_aggregate(args: { search: $search }) { search_bills_aggregate(args: { search: $search }) {
aggregate { aggregate {
count(distinct: true) count(distinct: true)
} }
@@ -56,8 +56,8 @@ export const QUERY_ALL_INVOICES_PAGINATED = gql`
} }
`; `;
export const QUERY_INVOICES_BY_JOBID = gql` export const QUERY_BILLS_BY_JOBID = gql`
query QUERY_PARTS_INVOICES_BY_JOBID($jobid: uuid!) { query QUERY_PARTS_BILLS_BY_JOBID($jobid: uuid!) {
parts_orders( parts_orders(
where: { jobid: { _eq: $jobid } } where: { jobid: { _eq: $jobid } }
order_by: { order_date: desc } order_by: { order_date: desc }
@@ -89,7 +89,7 @@ export const QUERY_INVOICES_BY_JOBID = gql`
order_number order_number
user_email user_email
} }
invoices(where: { jobid: { _eq: $jobid } }, order_by: { date: desc }) { bills(where: { jobid: { _eq: $jobid } }, order_by: { date: desc }) {
id id
vendorid vendorid
vendor { vendor {
@@ -103,7 +103,7 @@ export const QUERY_INVOICES_BY_JOBID = gql`
state_tax_rate state_tax_rate
local_tax_rate local_tax_rate
is_credit_memo is_credit_memo
invoicelines { billlines {
actual_price actual_price
quantity quantity
actual_cost actual_cost
@@ -117,57 +117,39 @@ export const QUERY_INVOICES_BY_JOBID = gql`
} }
`; `;
export const QUERY_INVOICES_BY_VENDOR = gql` // export const QUERY_INVOICES_BY_VENDOR_PAGINATED = gql`
query QUERY_INVOICES_BY_VENDOR($vendorId: uuid!) { // query QUERY_INVOICES_BY_VENDOR_PAGINATED(
invoices( // $vendorId: uuid!
where: { vendorid: { _eq: $vendorId } } // $offset: Int
order_by: { date: desc } // $limit: Int
) { // $order: [invoices_order_by!]!
id // ) {
job { // invoices(
id // where: { vendorid: { _eq: $vendorId } }
ro_number // offset: $offset
} // limit: $limit
total // order_by: $order
invoice_number // ) {
date // 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` export const QUERY_BILL_BY_PK = gql`
query QUERY_INVOICES_BY_VENDOR_PAGINATED( query QUERY_BILL_BY_PK($billid: uuid!) {
$vendorId: uuid! bills_by_pk(id: $billid) {
$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) {
due_date due_date
exported exported
exported_at exported_at
@@ -187,7 +169,7 @@ export const QUERY_INVOICE_BY_PK = gql`
name name
discount discount
} }
invoicelines { billlines {
id id
line_desc line_desc
actual_price actual_price
@@ -207,9 +189,9 @@ export const QUERY_INVOICE_BY_PK = gql`
} }
`; `;
export const UPDATE_INVOICE = gql` export const UPDATE_BILL = gql`
mutation UPDATE_INVOICE($invoiceId: uuid!, $invoice: invoices_set_input!) { mutation UPDATE_BILL($billId: uuid!, $bill: bills_set_input!) {
update_invoices(where: { id: { _eq: $invoiceId } }, _set: $invoice) { update_bills(where: { id: { _eq: $billId } }, _set: $bill) {
returning { returning {
id id
exported exported
@@ -219,12 +201,9 @@ export const UPDATE_INVOICE = gql`
} }
`; `;
export const UPDATE_INVOICES = gql` export const UPDATE_BILLS = gql`
mutation UPDATE_INVOICES( mutation UPDATE_BILLS($billIdList: [uuid!]!, $bill: bills_set_input!) {
$invoiceIdList: [uuid!]! update_bills(where: { id: { _in: $billIdList } }, _set: $bill) {
$invoice: invoices_set_input!
) {
update_invoices(where: { id: { _in: $invoiceIdList } }, _set: $invoice) {
returning { returning {
id id
exported exported

View File

@@ -48,7 +48,7 @@ export const QUERY_BODYSHOP = gql`
template_header template_header
textid textid
production_config production_config
invoice_tax_rates bill_tax_rates
inhousevendorid inhousevendorid
accountingconfig accountingconfig
appt_length appt_length
@@ -118,7 +118,7 @@ export const UPDATE_SHOP = gql`
template_header template_header
textid textid
production_config production_config
invoice_tax_rates bill_tax_rates
appt_length appt_length
stripe_acct_id stripe_acct_id
ssbuckets ssbuckets

View File

@@ -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
}
}
}
`;

View File

@@ -124,8 +124,8 @@ export const UPDATE_JOB_LINE = gql`
} }
`; `;
export const GET_JOB_LINES_TO_ENTER_INVOICE = gql` export const GET_JOB_LINES_TO_ENTER_BILL = gql`
query GET_JOB_LINES_TO_ENTER_INVOICE($id: uuid!) { query GET_JOB_LINES_TO_ENTER_BILL($id: uuid!) {
joblines(where: { jobid: { _eq: $id } }) { joblines(where: { jobid: { _eq: $id } }) {
id id
line_desc line_desc

View File

@@ -215,13 +215,13 @@ export const QUERY_JOB_COSTING_DETAILS = gql`
lbr_amt lbr_amt
op_code_desc op_code_desc
} }
invoices { bills {
id id
federal_tax_rate federal_tax_rate
local_tax_rate local_tax_rate
state_tax_rate state_tax_rate
is_credit_memo is_credit_memo
invoicelines { billlines {
actual_cost actual_cost
cost_center cost_center
id id

View File

@@ -42,7 +42,7 @@ export const GLOBAL_SEARCH_QUERY = gql`
memo memo
transactionid transactionid
} }
search_invoices(args: { search: $search }) { search_bills(args: { search: $search }) {
id id
date date
invoice_number invoice_number

View File

@@ -5,13 +5,14 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import AccountingPayablesTable from "../../components/accounting-payables-table/accounting-payables-table.component"; import AccountingPayablesTable from "../../components/accounting-payables-table/accounting-payables-table.component";
import AlertComponent from "../../components/alert/alert.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 { import {
setBreadcrumbs, setBreadcrumbs,
setSelectedHeader, setSelectedHeader,
} from "../../redux/application/application.actions"; } from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
@@ -20,6 +21,7 @@ const mapDispatchToProps = (dispatch) => ({
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)), setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)), setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
}); });
export function AccountingPayablesContainer({ export function AccountingPayablesContainer({
bodyshop, bodyshop,
setBreadcrumbs, setBreadcrumbs,
@@ -38,7 +40,7 @@ export function AccountingPayablesContainer({
]); ]);
}, [t, setBreadcrumbs, setSelectedHeader]); }, [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" />; if (error) return <AlertComponent message={error.message} type="error" />;
@@ -47,7 +49,7 @@ export function AccountingPayablesContainer({
<RbacWrapper action="accounting:payables"> <RbacWrapper action="accounting:payables">
<AccountingPayablesTable <AccountingPayablesTable
loadaing={loading} loadaing={loading}
invoices={data ? data.invoices : []} bills={data ? data.bills : []}
/> />
</RbacWrapper> </RbacWrapper>
</div> </div>

View File

@@ -13,17 +13,17 @@ import { alphaSort } from "../../utils/sorters";
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setPartsOrderContext: (context) => setPartsOrderContext: (context) =>
dispatch(setModalContext({ context: context, modal: "partsOrder" })), dispatch(setModalContext({ context: context, modal: "partsOrder" })),
setInvoiceEnterContext: (context) => setBillEnterContext: (context) =>
dispatch(setModalContext({ context: context, modal: "invoiceEnter" })), dispatch(setModalContext({ context: context, modal: "billEnter" })),
}); });
export function InvoicesListPage({ export function BillsListPage({
loading, loading,
data, data,
refetch, refetch,
total, total,
setPartsOrderContext, setPartsOrderContext,
setInvoiceEnterContext, setBillEnterContext,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [state, setState] = useState({ const [state, setState] = useState({
@@ -35,7 +35,7 @@ export function InvoicesListPage({
const columns = [ const columns = [
{ {
title: t("invoices.fields.vendorname"), title: t("bills.fields.vendorname"),
dataIndex: "vendorname", dataIndex: "vendorname",
key: "vendorname", key: "vendorname",
// sorter: (a, b) => alphaSort(a.vendor.name, b.vendor.name), // 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>, render: (text, record) => <span>{record.vendor.name}</span>,
}, },
{ {
title: t("invoices.fields.invoice_number"), title: t("bills.fields.invoice_number"),
dataIndex: "invoice_number", dataIndex: "invoice_number",
key: "invoice_number", key: "invoice_number",
sorter: (a, b) => alphaSort(a.invoice_number, b.invoice_number), sorter: (a, b) => alphaSort(a.invoice_number, b.invoice_number),
@@ -53,7 +53,7 @@ export function InvoicesListPage({
state.sortedInfo.order, state.sortedInfo.order,
}, },
{ {
title: t("invoices.fields.date"), title: t("bills.fields.date"),
dataIndex: "date", dataIndex: "date",
key: "date", key: "date",
sorter: (a, b) => a.date - b.date, sorter: (a, b) => a.date - b.date,
@@ -62,7 +62,7 @@ export function InvoicesListPage({
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>, render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
}, },
{ {
title: t("invoices.fields.total"), title: t("bills.fields.total"),
dataIndex: "total", dataIndex: "total",
key: "total", key: "total",
sorter: (a, b) => a.total - b.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", dataIndex: "is_credit_memo",
key: "is_credit_memo", key: "is_credit_memo",
sorter: (a, b) => a.is_credit_memo - b.is_credit_memo, sorter: (a, b) => a.is_credit_memo - b.is_credit_memo,
@@ -88,8 +88,8 @@ export function InvoicesListPage({
key: "actions", key: "actions",
render: (text, record) => ( render: (text, record) => (
<div> <div>
<Link to={`/manage/invoices?invoiceid=${record.id}`}> <Link to={`/manage/bills?billid=${record.id}`}>
<Button>{t("invoices.actions.edit")}</Button> <Button>{t("bills.actions.edit")}</Button>
</Link> </Link>
<Button <Button
disabled={record.is_credit_memo} disabled={record.is_credit_memo}
@@ -99,9 +99,9 @@ export function InvoicesListPage({
context: { context: {
jobId: record.jobid, jobId: record.jobid,
vendorId: record.vendorid, vendorId: record.vendorid,
returnFromInvoice: record.id, returnFromBill: record.id,
invoiceNumber: record.invoice_number, invoiceNumber: record.invoice_number,
linesToOrder: record.invoicelines.map((i) => { linesToOrder: record.billlines.map((i) => {
return { return {
line_desc: i.line_desc, line_desc: i.line_desc,
db_price: i.actual_price, db_price: i.actual_price,
@@ -115,7 +115,7 @@ export function InvoicesListPage({
}) })
} }
> >
{t("invoices.actions.return")} {t("bills.actions.return")}
</Button> </Button>
</div> </div>
), ),
@@ -132,9 +132,7 @@ export function InvoicesListPage({
return ( return (
<div> <div>
<Typography.Title level={4}> <Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
{t("invoices.labels.invoices")}
</Typography.Title>
<Table <Table
loading={loading} loading={loading}
size="small" size="small"
@@ -145,13 +143,13 @@ export function InvoicesListPage({
</Button> </Button>
<Button <Button
onClick={() => { onClick={() => {
setInvoiceEnterContext({ setBillEnterContext({
actions: { refetch: refetch }, actions: { refetch: refetch },
context: {}, context: {},
}); });
}} }}
> >
{t("jobs.actions.postInvoices")} {t("jobs.actions.postbills")}
</Button> </Button>
<div className="imex-table-header__search"> <div className="imex-table-header__search">
<Input.Search <Input.Search
@@ -179,4 +177,4 @@ export function InvoicesListPage({
</div> </div>
); );
} }
export default connect(null, mapDispatchToProps)(InvoicesListPage); export default connect(null, mapDispatchToProps)(BillsListPage);

View File

@@ -4,13 +4,13 @@ import { useQuery } from "react-apollo";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import InvoiceDetailEditContainer from "../../components/invoice-detail-edit/invoice-detail-edit.container"; import BillDetailEditContainer from "../../components/bill-detail-edit/bill-detail-edit.container";
import { QUERY_ALL_INVOICES_PAGINATED } from "../../graphql/invoices.queries"; import { QUERY_ALL_BILLS_PAGINATED } from "../../graphql/bills.queries";
import { import {
setBreadcrumbs, setBreadcrumbs,
setSelectedHeader, setSelectedHeader,
} from "../../redux/application/application.actions"; } 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 AlertComponent from "../../components/alert/alert.component";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component"; import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
@@ -19,21 +19,21 @@ const mapDispatchToProps = (dispatch) => ({
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)), setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
}); });
export function InvoicesPageContainer({ setBreadcrumbs, setSelectedHeader }) { export function BillsPageContainer({ setBreadcrumbs, setSelectedHeader }) {
const { t } = useTranslation(); const { t } = useTranslation();
const searchParams = queryString.parse(useLocation().search); const searchParams = queryString.parse(useLocation().search);
const { page, sortcolumn, sortorder, search } = searchParams; const { page, sortcolumn, sortorder, search } = searchParams;
useEffect(() => { useEffect(() => {
document.title = t("titles.invoices-list"); document.title = t("titles.bills-list");
setSelectedHeader("invoices"); setSelectedHeader("bills");
setBreadcrumbs([ setBreadcrumbs([
{ link: "/manage/invoices", label: t("titles.bc.invoices-list") }, { link: "/manage/bills", label: t("titles.bc.bills-list") },
]); ]);
}, [t, setBreadcrumbs, setSelectedHeader]); }, [t, setBreadcrumbs, setSelectedHeader]);
const { loading, error, data, refetch } = useQuery( const { loading, error, data, refetch } = useQuery(
QUERY_ALL_INVOICES_PAGINATED, QUERY_ALL_BILLS_PAGINATED,
{ {
variables: { variables: {
search: search || "", search: search || "",
@@ -54,18 +54,18 @@ export function InvoicesPageContainer({ setBreadcrumbs, setSelectedHeader }) {
if (error) return <AlertComponent message={error.message} type="error" />; if (error) return <AlertComponent message={error.message} type="error" />;
return ( return (
<RbacWrapper action="invoices:list"> <RbacWrapper action="bills:list">
<div> <div>
<InvoicesPageComponent <BillsPageComponent
data={data ? data.search_invoices : []} data={data ? data.search_bills : []}
loading={loading} loading={loading}
refetch={refetch} refetch={refetch}
total={data ? data.search_invoices_aggregate.aggregate.count : 0} total={data ? data.search_bills_aggregate.aggregate.count : 0}
/> />
<InvoiceDetailEditContainer /> <BillDetailEditContainer />
</div> </div>
</RbacWrapper> </RbacWrapper>
); );
} }
export default connect(null, mapDispatchToProps)(InvoicesPageContainer); export default connect(null, mapDispatchToProps)(BillsPageContainer);

View File

@@ -74,15 +74,13 @@ const ContractDetailPage = lazy(() =>
const ContractsList = lazy(() => const ContractsList = lazy(() =>
import("../contracts/contracts.page.container") import("../contracts/contracts.page.container")
); );
const InvoicesListPage = lazy(() => const BillsListPage = lazy(() => import("../bills/bills.page.container"));
import("../invoices/invoices.page.container")
);
const JobCostingModal = lazy(() => const JobCostingModal = lazy(() =>
import("../../components/job-costing-modal/job-costing-modal.container") import("../../components/job-costing-modal/job-costing-modal.container")
); );
const EnterInvoiceModalContainer = lazy(() => const BillEnterModalContainer = lazy(() =>
import("../../components/invoice-enter-modal/invoice-enter-modal.container") import("../../components/bill-enter-modal/bill-enter-modal.container")
); );
const TimeTicketModalContainer = lazy(() => const TimeTicketModalContainer = lazy(() =>
import("../../components/time-ticket-modal/time-ticket-modal.container") import("../../components/time-ticket-modal/time-ticket-modal.container")
@@ -174,7 +172,7 @@ export function Manage({ match, conflict }) {
<PaymentModalContainer /> <PaymentModalContainer />
</Elements> </Elements>
<BreadCrumbs /> <BreadCrumbs />
<EnterInvoiceModalContainer /> <BillEnterModalContainer />
<JobCostingModal /> <JobCostingModal />
<EmailOverlayContainer /> <EmailOverlayContainer />
<TimeTicketModalContainer /> <TimeTicketModalContainer />
@@ -280,8 +278,8 @@ export function Manage({ match, conflict }) {
/> />
<Route <Route
exact exact
path={`${match.path}/invoices`} path={`${match.path}/bills`}
component={InvoicesListPage} component={BillsListPage}
/> />
<Route <Route
exact exact

View File

@@ -10,7 +10,7 @@ const baseModal = {
const INITIAL_STATE = { const INITIAL_STATE = {
jobLineEdit: { ...baseModal }, jobLineEdit: { ...baseModal },
invoiceEnter: { ...baseModal }, billEnter: { ...baseModal },
courtesyCarReturn: { ...baseModal }, courtesyCarReturn: { ...baseModal },
noteUpsert: { ...baseModal }, noteUpsert: { ...baseModal },
schedule: { ...baseModal }, schedule: { ...baseModal },

View File

@@ -7,9 +7,9 @@ export const selectJobLineEditModal = createSelector(
(modals) => modals.jobLineEdit (modals) => modals.jobLineEdit
); );
export const selectInvoiceEnterModal = createSelector( export const selectBillEnterModal = createSelector(
[selectModals], [selectModals],
(modals) => modals.invoiceEnter (modals) => modals.billEnter
); );
export const selectCourtesyCarReturn = createSelector( export const selectCourtesyCarReturn = createSelector(

View File

@@ -73,6 +73,76 @@
"values": "Values" "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": { "bodyshop": {
"actions": { "actions": {
"addbucket": "Add Bucket", "addbucket": "Add Bucket",
@@ -91,6 +161,9 @@
"address1": "Address 1", "address1": "Address 1",
"address2": "Address 2", "address2": "Address 2",
"appt_length": "Default Appointment Length", "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", "city": "City",
"country": "Country", "country": "Country",
"dailybodytarget": "Scoreboard - Daily Body Target", "dailybodytarget": "Scoreboard - Daily Body Target",
@@ -99,9 +172,6 @@
"enforce_class": "Enforce Class on Conversion?", "enforce_class": "Enforce Class on Conversion?",
"federal_tax_id": "Federal Tax ID (GST/HST)", "federal_tax_id": "Federal Tax ID (GST/HST)",
"insurance_vendor_id": "Insurance Vendor ID", "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", "lastnumberworkingdays": "Scoreboard - Last Number of Working Days",
"logo_img_path": "Shop Logo", "logo_img_path": "Shop Logo",
"md_categories": "Categories", "md_categories": "Categories",
@@ -121,6 +191,11 @@
"payments": "Accounting -> Payments", "payments": "Accounting -> Payments",
"receivables": "Accounting -> Receivables" "receivables": "Accounting -> Receivables"
}, },
"bills": {
"enter": "Bills -> Enter",
"list": "Bills -> List",
"view": "Bills -> View"
},
"contracts": { "contracts": {
"create": "Contracts -> Create", "create": "Contracts -> Create",
"detail": "Contracts -> Detail", "detail": "Contracts -> Detail",
@@ -138,11 +213,6 @@
"employees": { "employees": {
"page": "Employees -> List" "page": "Employees -> List"
}, },
"invoices": {
"enter": "Invoices -> Enter",
"list": "Invoices -> List",
"view": "Invoices -> View"
},
"jobs": { "jobs": {
"available-list": "Jobs -> Available List", "available-list": "Jobs -> Available List",
"close": "Jobs -> Close", "close": "Jobs -> Close",
@@ -614,76 +684,6 @@
"rescuetitle": "Rescue Me!" "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": { "joblines": {
"actions": { "actions": {
"new": "New Line" "new": "New Line"
@@ -778,7 +778,7 @@
"intake": "Intake", "intake": "Intake",
"manualnew": "Create New Job Manually", "manualnew": "Create New Job Manually",
"mark": "Mark", "mark": "Mark",
"postInvoices": "Post Invoices", "postbills": "Post Bills",
"printCenter": "Print Center", "printCenter": "Print Center",
"recalculate": "Recalculate", "recalculate": "Recalculate",
"reconcile": "Reconcile", "reconcile": "Reconcile",
@@ -1071,17 +1071,17 @@
"alljobs": "All Jobs", "alljobs": "All Jobs",
"allpayments": "All Payments", "allpayments": "All Payments",
"availablejobs": "Available Jobs", "availablejobs": "Available Jobs",
"bills": "Bills",
"courtesycars": "Courtesy Cars", "courtesycars": "Courtesy Cars",
"courtesycars-all": "All Courtesy Cars", "courtesycars-all": "All Courtesy Cars",
"courtesycars-contracts": "Contracts", "courtesycars-contracts": "Contracts",
"courtesycars-newcontract": "New Contract", "courtesycars-newcontract": "New Contract",
"customers": "Customers", "customers": "Customers",
"enterinvoices": "Enter Invoices", "enterbills": "Enter Bills",
"enterpayment": "Enter Payments", "enterpayment": "Enter Payments",
"entertimeticket": "Enter Time Tickets", "entertimeticket": "Enter Time Tickets",
"export": "Export", "export": "Export",
"home": "Home", "home": "Home",
"invoices": "Invoices",
"jobs": "Jobs", "jobs": "Jobs",
"owners": "Owners", "owners": "Owners",
"productionboard": "Production Board - Visual", "productionboard": "Production Board - Visual",
@@ -1221,7 +1221,7 @@
"actions": { "actions": {
"backordered": "Backordered", "backordered": "Backordered",
"receive": "Receive", "receive": "Receive",
"receiveinvoice": "Receive Invoice" "receivebill": "Receive Bill"
}, },
"errors": { "errors": {
"backordering": "Error backordering part {{message}}.", "backordering": "Error backordering part {{message}}.",
@@ -1451,13 +1451,13 @@
"accounting-payments": "Payments", "accounting-payments": "Payments",
"accounting-receivables": "Receivables", "accounting-receivables": "Receivables",
"availablejobs": "Available Jobs", "availablejobs": "Available Jobs",
"bills-list": "Bills",
"contracts": "Contracts", "contracts": "Contracts",
"contracts-create": "New Contract", "contracts-create": "New Contract",
"contracts-detail": "Contract #{{number}}", "contracts-detail": "Contract #{{number}}",
"courtesycars": "Courtesy Cars", "courtesycars": "Courtesy Cars",
"courtesycars-detail": "Courtesy Car {{number}}", "courtesycars-detail": "Courtesy Car {{number}}",
"courtesycars-new": "New Courtesy Car", "courtesycars-new": "New Courtesy Car",
"invoices-list": "Invoices",
"jobs": "Jobs", "jobs": "Jobs",
"jobs-active": "Active Jobs", "jobs-active": "Active Jobs",
"jobs-all": "All Jobs", "jobs-all": "All Jobs",
@@ -1481,13 +1481,13 @@
"vehicle-details": "Vehicle: {{vehicle}}", "vehicle-details": "Vehicle: {{vehicle}}",
"vehicles": "Vehicles" "vehicles": "Vehicles"
}, },
"bills-list": "Bills | $t(titles.app)",
"contracts": "Courtesy Car Contracts | $t(titles.app)", "contracts": "Courtesy Car Contracts | $t(titles.app)",
"contracts-create": "New Contract | $t(titles.app)", "contracts-create": "New Contract | $t(titles.app)",
"contracts-detail": "Contract {{id}} | $t(titles.app)", "contracts-detail": "Contract {{id}} | $t(titles.app)",
"courtesycars": "Courtesy Cars | $t(titles.app)", "courtesycars": "Courtesy Cars | $t(titles.app)",
"courtesycars-create": "New Courtesy Car | $t(titles.app)", "courtesycars-create": "New Courtesy Car | $t(titles.app)",
"courtesycars-detail": "Courtesy Car {{id}} | $t(titles.app)", "courtesycars-detail": "Courtesy Car {{id}} | $t(titles.app)",
"invoices-list": "Invoices | $t(titles.app)",
"jobs": "Active Jobs | $t(titles.app)", "jobs": "Active Jobs | $t(titles.app)",
"jobs-all": "All Jobs | $t(titles.app)", "jobs-all": "All Jobs | $t(titles.app)",
"jobs-close": "Close Job {{number}} | $t(titles.app)", "jobs-close": "Close Job {{number}} | $t(titles.app)",

View File

@@ -73,6 +73,76 @@
"values": "" "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": { "bodyshop": {
"actions": { "actions": {
"addbucket": "", "addbucket": "",
@@ -91,6 +161,9 @@
"address1": "", "address1": "",
"address2": "", "address2": "",
"appt_length": "", "appt_length": "",
"bill_federal_tax_rate": "",
"bill_local_tax_rate": "",
"bill_state_tax_rate": "",
"city": "", "city": "",
"country": "", "country": "",
"dailybodytarget": "", "dailybodytarget": "",
@@ -99,9 +172,6 @@
"enforce_class": "", "enforce_class": "",
"federal_tax_id": "", "federal_tax_id": "",
"insurance_vendor_id": "", "insurance_vendor_id": "",
"invoice_federal_tax_rate": "",
"invoice_local_tax_rate": "",
"invoice_state_tax_rate": "",
"lastnumberworkingdays": "", "lastnumberworkingdays": "",
"logo_img_path": "", "logo_img_path": "",
"md_categories": "", "md_categories": "",
@@ -121,6 +191,11 @@
"payments": "", "payments": "",
"receivables": "" "receivables": ""
}, },
"bills": {
"enter": "",
"list": "",
"view": ""
},
"contracts": { "contracts": {
"create": "", "create": "",
"detail": "", "detail": "",
@@ -138,11 +213,6 @@
"employees": { "employees": {
"page": "" "page": ""
}, },
"invoices": {
"enter": "",
"list": "",
"view": ""
},
"jobs": { "jobs": {
"available-list": "", "available-list": "",
"close": "", "close": "",
@@ -614,76 +684,6 @@
"rescuetitle": "" "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": { "joblines": {
"actions": { "actions": {
"new": "" "new": ""
@@ -778,7 +778,7 @@
"intake": "", "intake": "",
"manualnew": "", "manualnew": "",
"mark": "", "mark": "",
"postInvoices": "Contabilizar facturas", "postbills": "Contabilizar facturas",
"printCenter": "Centro de impresión", "printCenter": "Centro de impresión",
"recalculate": "", "recalculate": "",
"reconcile": "", "reconcile": "",
@@ -1071,17 +1071,17 @@
"alljobs": "", "alljobs": "",
"allpayments": "", "allpayments": "",
"availablejobs": "Trabajos disponibles", "availablejobs": "Trabajos disponibles",
"bills": "",
"courtesycars": "", "courtesycars": "",
"courtesycars-all": "", "courtesycars-all": "",
"courtesycars-contracts": "", "courtesycars-contracts": "",
"courtesycars-newcontract": "", "courtesycars-newcontract": "",
"customers": "Clientes", "customers": "Clientes",
"enterinvoices": "", "enterbills": "",
"enterpayment": "", "enterpayment": "",
"entertimeticket": "", "entertimeticket": "",
"export": "", "export": "",
"home": "Casa", "home": "Casa",
"invoices": "",
"jobs": "Trabajos", "jobs": "Trabajos",
"owners": "propietarios", "owners": "propietarios",
"productionboard": "", "productionboard": "",
@@ -1221,7 +1221,7 @@
"actions": { "actions": {
"backordered": "", "backordered": "",
"receive": "", "receive": "",
"receiveinvoice": "" "receivebill": ""
}, },
"errors": { "errors": {
"backordering": "", "backordering": "",
@@ -1451,13 +1451,13 @@
"accounting-payments": "", "accounting-payments": "",
"accounting-receivables": "", "accounting-receivables": "",
"availablejobs": "", "availablejobs": "",
"bills-list": "",
"contracts": "", "contracts": "",
"contracts-create": "", "contracts-create": "",
"contracts-detail": "", "contracts-detail": "",
"courtesycars": "", "courtesycars": "",
"courtesycars-detail": "", "courtesycars-detail": "",
"courtesycars-new": "", "courtesycars-new": "",
"invoices-list": "",
"jobs": "", "jobs": "",
"jobs-active": "", "jobs-active": "",
"jobs-all": "", "jobs-all": "",
@@ -1481,13 +1481,13 @@
"vehicle-details": "", "vehicle-details": "",
"vehicles": "" "vehicles": ""
}, },
"bills-list": "",
"contracts": "", "contracts": "",
"contracts-create": "", "contracts-create": "",
"contracts-detail": "", "contracts-detail": "",
"courtesycars": "", "courtesycars": "",
"courtesycars-create": "", "courtesycars-create": "",
"courtesycars-detail": "", "courtesycars-detail": "",
"invoices-list": "",
"jobs": "Todos los trabajos | $t(titles.app)", "jobs": "Todos los trabajos | $t(titles.app)",
"jobs-all": "", "jobs-all": "",
"jobs-close": "", "jobs-close": "",

View File

@@ -73,6 +73,76 @@
"values": "" "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": { "bodyshop": {
"actions": { "actions": {
"addbucket": "", "addbucket": "",
@@ -91,6 +161,9 @@
"address1": "", "address1": "",
"address2": "", "address2": "",
"appt_length": "", "appt_length": "",
"bill_federal_tax_rate": "",
"bill_local_tax_rate": "",
"bill_state_tax_rate": "",
"city": "", "city": "",
"country": "", "country": "",
"dailybodytarget": "", "dailybodytarget": "",
@@ -99,9 +172,6 @@
"enforce_class": "", "enforce_class": "",
"federal_tax_id": "", "federal_tax_id": "",
"insurance_vendor_id": "", "insurance_vendor_id": "",
"invoice_federal_tax_rate": "",
"invoice_local_tax_rate": "",
"invoice_state_tax_rate": "",
"lastnumberworkingdays": "", "lastnumberworkingdays": "",
"logo_img_path": "", "logo_img_path": "",
"md_categories": "", "md_categories": "",
@@ -121,6 +191,11 @@
"payments": "", "payments": "",
"receivables": "" "receivables": ""
}, },
"bills": {
"enter": "",
"list": "",
"view": ""
},
"contracts": { "contracts": {
"create": "", "create": "",
"detail": "", "detail": "",
@@ -138,11 +213,6 @@
"employees": { "employees": {
"page": "" "page": ""
}, },
"invoices": {
"enter": "",
"list": "",
"view": ""
},
"jobs": { "jobs": {
"available-list": "", "available-list": "",
"close": "", "close": "",
@@ -614,76 +684,6 @@
"rescuetitle": "" "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": { "joblines": {
"actions": { "actions": {
"new": "" "new": ""
@@ -778,7 +778,7 @@
"intake": "", "intake": "",
"manualnew": "", "manualnew": "",
"mark": "", "mark": "",
"postInvoices": "Poster des factures", "postbills": "Poster des factures",
"printCenter": "Centre d'impression", "printCenter": "Centre d'impression",
"recalculate": "", "recalculate": "",
"reconcile": "", "reconcile": "",
@@ -1071,17 +1071,17 @@
"alljobs": "", "alljobs": "",
"allpayments": "", "allpayments": "",
"availablejobs": "Emplois disponibles", "availablejobs": "Emplois disponibles",
"bills": "",
"courtesycars": "", "courtesycars": "",
"courtesycars-all": "", "courtesycars-all": "",
"courtesycars-contracts": "", "courtesycars-contracts": "",
"courtesycars-newcontract": "", "courtesycars-newcontract": "",
"customers": "Les clients", "customers": "Les clients",
"enterinvoices": "", "enterbills": "",
"enterpayment": "", "enterpayment": "",
"entertimeticket": "", "entertimeticket": "",
"export": "", "export": "",
"home": "Accueil", "home": "Accueil",
"invoices": "",
"jobs": "Emplois", "jobs": "Emplois",
"owners": "Propriétaires", "owners": "Propriétaires",
"productionboard": "", "productionboard": "",
@@ -1221,7 +1221,7 @@
"actions": { "actions": {
"backordered": "", "backordered": "",
"receive": "", "receive": "",
"receiveinvoice": "" "receivebill": ""
}, },
"errors": { "errors": {
"backordering": "", "backordering": "",
@@ -1451,13 +1451,13 @@
"accounting-payments": "", "accounting-payments": "",
"accounting-receivables": "", "accounting-receivables": "",
"availablejobs": "", "availablejobs": "",
"bills-list": "",
"contracts": "", "contracts": "",
"contracts-create": "", "contracts-create": "",
"contracts-detail": "", "contracts-detail": "",
"courtesycars": "", "courtesycars": "",
"courtesycars-detail": "", "courtesycars-detail": "",
"courtesycars-new": "", "courtesycars-new": "",
"invoices-list": "",
"jobs": "", "jobs": "",
"jobs-active": "", "jobs-active": "",
"jobs-all": "", "jobs-all": "",
@@ -1481,13 +1481,13 @@
"vehicle-details": "", "vehicle-details": "",
"vehicles": "" "vehicles": ""
}, },
"bills-list": "",
"contracts": "", "contracts": "",
"contracts-create": "", "contracts-create": "",
"contracts-detail": "", "contracts-detail": "",
"courtesycars": "", "courtesycars": "",
"courtesycars-create": "", "courtesycars-create": "",
"courtesycars-detail": "", "courtesycars-detail": "",
"invoices-list": "",
"jobs": "Tous les emplois | $t(titles.app)", "jobs": "Tous les emplois | $t(titles.app)",
"jobs-all": "", "jobs-all": "",
"jobs-close": "", "jobs-close": "",

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,5 @@
- args:
cascade: true
read_only: false
sql: DROP FUNCTION IF EXISTS search_invoices;
type: run_sql

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: alter table "public"."bills" rename to "invoices";
type: run_sql

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: alter table "public"."invoices" rename to "bills";
type: run_sql

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: alter table "public"."billlines" rename to "invoicelines";
type: run_sql

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: alter table "public"."invoicelines" rename to "billlines";
type: run_sql

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: alter table "public"."billlines" rename column "billid" to "invoiceid";
type: run_sql

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: alter table "public"."billlines" rename column "invoiceid" to "billid";
type: run_sql

View File

@@ -0,0 +1,7 @@
- args:
name: bill
new_name: invoice
table:
name: billlines
schema: public
type: rename_relationship

View File

@@ -0,0 +1,7 @@
- args:
name: invoice
new_name: bill
table:
name: billlines
schema: public
type: rename_relationship

View File

@@ -0,0 +1,7 @@
- args:
name: bill
new_name: invoice
table:
name: jobs
schema: public
type: rename_relationship

View File

@@ -0,0 +1,7 @@
- args:
name: invoice
new_name: bill
table:
name: jobs
schema: public
type: rename_relationship

View File

@@ -0,0 +1,7 @@
- args:
name: bills
new_name: invoices
table:
name: jobs
schema: public
type: rename_relationship

View File

@@ -0,0 +1,7 @@
- args:
name: invoices
new_name: bills
table:
name: jobs
schema: public
type: rename_relationship

View File

@@ -0,0 +1,7 @@
- args:
name: bill
new_name: invoice
table:
name: documents
schema: public
type: rename_relationship

View File

@@ -0,0 +1,7 @@
- args:
name: invoice
new_name: bill
table:
name: documents
schema: public
type: rename_relationship

View File

@@ -0,0 +1,7 @@
- args:
name: bill
new_name: invoice
table:
name: parts_orders
schema: public
type: rename_relationship

View File

@@ -0,0 +1,7 @@
- args:
name: invoice
new_name: bill
table:
name: parts_orders
schema: public
type: rename_relationship

View File

@@ -0,0 +1 @@
[]

View File

@@ -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

View File

@@ -0,0 +1,7 @@
- args:
name: billlines
new_name: invoicelines
table:
name: bills
schema: public
type: rename_relationship

View File

@@ -0,0 +1,7 @@
- args:
name: invoicelines
new_name: billlines
table:
name: bills
schema: public
type: rename_relationship

View File

@@ -0,0 +1 @@
[]

View File

@@ -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

View File

@@ -0,0 +1 @@
[]

View 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

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: alter table "public"."documents" rename column "billid" to "invoiceid";
type: run_sql

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: alter table "public"."documents" rename column "invoiceid" to "billid";
type: run_sql

View File

@@ -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

View File

@@ -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

View File

@@ -348,6 +348,227 @@ tables:
_eq: X-Hasura-User-Id _eq: X-Hasura-User-Id
- active: - active:
_eq: true _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: - table:
schema: public schema: public
name: bodyshops name: bodyshops
@@ -483,7 +704,7 @@ tables:
- inhousevendorid - inhousevendorid
- insurance_vendor_id - insurance_vendor_id
- intakechecklist - intakechecklist
- invoice_tax_rates - bill_tax_rates
- logo_img_path - logo_img_path
- md_categories - md_categories
- md_classes - md_classes
@@ -541,7 +762,7 @@ tables:
- inhousevendorid - inhousevendorid
- insurance_vendor_id - insurance_vendor_id
- intakechecklist - intakechecklist
- invoice_tax_rates - bill_tax_rates
- logo_img_path - logo_img_path
- md_categories - md_categories
- md_classes - md_classes
@@ -1170,9 +1391,9 @@ tables:
schema: public schema: public
name: documents name: documents
object_relationships: object_relationships:
- name: invoice - name: bill
using: using:
foreign_key_constraint_on: invoiceid foreign_key_constraint_on: billid
- name: job - name: job
using: using:
foreign_key_constraint_on: jobid foreign_key_constraint_on: jobid
@@ -1197,7 +1418,7 @@ tables:
- jobid - jobid
- name - name
- key - key
- invoiceid - billid
- type - type
select_permissions: select_permissions:
- role: user - role: user
@@ -1210,7 +1431,7 @@ tables:
- created_at - created_at
- updated_at - updated_at
- id - id
- invoiceid - billid
- jobid - jobid
filter: filter:
job: job:
@@ -1393,227 +1614,6 @@ tables:
_eq: X-Hasura-User-Id _eq: X-Hasura-User-Id
- active: - active:
_eq: true _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: - table:
schema: public schema: public
name: job_conversations name: job_conversations
@@ -1961,6 +1961,14 @@ tables:
schema: public schema: public
name: jobs name: jobs
object_relationships: object_relationships:
- name: bill
using:
manual_configuration:
remote_table:
schema: public
name: bills
column_mapping:
id: jobid
- name: bodyshop - name: bodyshop
using: using:
foreign_key_constraint_on: shopid foreign_key_constraint_on: shopid
@@ -1973,14 +1981,6 @@ tables:
- name: employee_refinish_rel - name: employee_refinish_rel
using: using:
foreign_key_constraint_on: employee_refinish foreign_key_constraint_on: employee_refinish
- name: invoice
using:
manual_configuration:
remote_table:
schema: public
name: invoices
column_mapping:
id: jobid
- name: owner - name: owner
using: using:
foreign_key_constraint_on: ownerid foreign_key_constraint_on: ownerid
@@ -2002,6 +2002,13 @@ tables:
table: table:
schema: public schema: public
name: available_jobs name: available_jobs
- name: bills
using:
foreign_key_constraint_on:
column: jobid
table:
schema: public
name: bills
- name: cccontracts - name: cccontracts
using: using:
foreign_key_constraint_on: foreign_key_constraint_on:
@@ -2023,13 +2030,6 @@ tables:
table: table:
schema: public schema: public
name: documents name: documents
- name: invoices
using:
foreign_key_constraint_on:
column: jobid
table:
schema: public
name: invoices
- name: job_conversations - name: job_conversations
using: using:
foreign_key_constraint_on: foreign_key_constraint_on:
@@ -3298,7 +3298,7 @@ tables:
schema: public schema: public
name: parts_orders name: parts_orders
object_relationships: object_relationships:
- name: invoice - name: bill
using: using:
foreign_key_constraint_on: returnfrominvoice foreign_key_constraint_on: returnfrominvoice
- name: job - name: job
@@ -4019,7 +4019,7 @@ tables:
column: vendorid column: vendorid
table: table:
schema: public schema: public
name: invoices name: bills
- name: parts_orders - name: parts_orders
using: using:
foreign_key_constraint_on: foreign_key_constraint_on:
@@ -4142,7 +4142,7 @@ tables:
functions: functions:
- function: - function:
schema: public schema: public
name: search_invoices name: search_bills
- function: - function:
schema: public schema: public
name: search_jobs name: search_jobs

View File

@@ -14,7 +14,7 @@ require("dotenv").config({
exports.default = async (req, res) => { exports.default = async (req, res) => {
const BearerToken = req.headers.authorization; const BearerToken = req.headers.authorization;
const { invoices: invoicesToQuery } = req.body; const { bills: billsToQuery } = req.body;
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, { const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
headers: { headers: {
@@ -25,13 +25,13 @@ exports.default = async (req, res) => {
try { try {
const result = await client const result = await client
.setHeaders({ Authorization: BearerToken }) .setHeaders({ Authorization: BearerToken })
.request(queries.QUERY_INVOICES_FOR_PAYABLES_EXPORT, { .request(queries.QUERY_BILLS_FOR_PAYABLES_EXPORT, {
invoices: invoicesToQuery, bills: billsToQuery,
}); });
const { invoices } = result; const { bills } = result;
const QbXmlToExecute = []; const QbXmlToExecute = [];
invoices.map((i) => { bills.map((i) => {
QbXmlToExecute.push({ QbXmlToExecute.push({
id: i.id, id: i.id,
okStatusCodes: ["0"], okStatusCodes: ["0"],
@@ -48,27 +48,24 @@ exports.default = async (req, res) => {
} }
}; };
const generateBill = (invoice) => { const generateBill = (bill) => {
const billQbxmlObj = { const billQbxmlObj = {
QBXML: { QBXML: {
QBXMLMsgsRq: { QBXMLMsgsRq: {
"@onError": "continueOnError", "@onError": "continueOnError",
[`${invoice.is_credit_memo ? "VendorCreditAddRq" : "BillAddRq"}`]: { [`${bill.is_credit_memo ? "VendorCreditAddRq" : "BillAddRq"}`]: {
[`${invoice.is_credit_memo ? "VendorCreditAdd" : "BillAdd"}`]: { [`${bill.is_credit_memo ? "VendorCreditAdd" : "BillAdd"}`]: {
VendorRef: { VendorRef: {
FullName: invoice.vendor.name, FullName: bill.vendor.name,
}, },
TxnDate: invoice.date, TxnDate: bill.date,
DueDate: invoice.due_date, DueDate: bill.due_date,
RefNumber: invoice.invoice_number, RefNumber: bill.bill_number,
Memo: `RO ${invoice.job.ro_number || ""} OWNER ${ Memo: `RO ${bill.job.ro_number || ""} OWNER ${
invoice.job.ownr_fn || "" bill.job.ownr_fn || ""
} ${invoice.job.ownr_ln || ""} ${invoice.job.ownr_co_nm || ""}`, } ${bill.job.ownr_ln || ""} ${bill.job.ownr_co_nm || ""}`,
ExpenseLineAdd: invoice.invoicelines.map((il) => ExpenseLineAdd: bill.billlines.map((il) =>
generateBillLine( generateBillLine(il, bill.job.bodyshop.md_responsibility_centers)
il,
invoice.job.bodyshop.md_responsibility_centers
)
), ),
}, },
}, },
@@ -90,15 +87,15 @@ const generateBill = (invoice) => {
return billQbxml_Full; return billQbxml_Full;
}; };
const generateBillLine = (invoiceLine, responsibilityCenters) => { const generateBillLine = (billLine, responsibilityCenters) => {
return { return {
AccountRef: { AccountRef: {
FullName: responsibilityCenters.costs.find( FullName: responsibilityCenters.costs.find(
(c) => c.name === invoiceLine.cost_center (c) => c.name === billLine.cost_center
).accountname, ).accountname,
}, },
Amount: Dinero({ Amount: Dinero({
amount: Math.round(invoiceLine.actual_cost * 100), amount: Math.round(billLine.actual_cost * 100),
}).toFormat(DineroQbFormat), }).toFormat(DineroQbFormat),
}; };
}; };

View File

@@ -108,9 +108,9 @@ query QUERY_JOBS_FOR_RECEIVABLES_EXPORT($ids: [uuid!]!) {
} }
`; `;
exports.QUERY_INVOICES_FOR_PAYABLES_EXPORT = ` exports.QUERY_BILLS_FOR_PAYABLES_EXPORT = `
query QUERY_INVOICES_FOR_PAYABLES_EXPORT($invoices: [uuid!]!) { query QUERY_BILLS_FOR_PAYABLES_EXPORT($bills: [uuid!]!) {
invoices(where: {id: {_in: $invoices}}) { bills(where: {id: {_in: $bills}}) {
id id
date date
due_date due_date
@@ -128,7 +128,7 @@ query QUERY_INVOICES_FOR_PAYABLES_EXPORT($invoices: [uuid!]!) {
md_responsibility_centers md_responsibility_centers
} }
} }
invoicelines{ billlines{
id id
cost_center cost_center
actual_cost actual_cost