Added invoice search + reformatted invoices page BOD-123
This commit is contained in:
@@ -1,159 +1,161 @@
|
|||||||
import React, { useState } from "react";
|
//DEPRECATED.
|
||||||
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() {
|
// import React, { useState } from "react";
|
||||||
const search = queryString.parse(useLocation().search);
|
// import { QUERY_INVOICES_BY_VENDOR_PAGINATED } from "../../graphql/invoices.queries";
|
||||||
const history = useHistory();
|
// import { useQuery } from "@apollo/react-hooks";
|
||||||
const { page, sortcolumn, sortorder } = search;
|
// 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";
|
||||||
|
|
||||||
const { loading, error, data } = useQuery(
|
// export default function InvoicesByVendorList() {
|
||||||
QUERY_INVOICES_BY_VENDOR_PAGINATED,
|
// const search = queryString.parse(useLocation().search);
|
||||||
{
|
// const history = useHistory();
|
||||||
variables: {
|
// const { page, sortcolumn, sortorder } = search;
|
||||||
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 { 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 [state, setState] = useState({
|
// const { t } = useTranslation();
|
||||||
sortedInfo: {},
|
|
||||||
search: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleTableChange = (pagination, filters, sorter) => {
|
// const [state, setState] = useState({
|
||||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
// sortedInfo: {},
|
||||||
search.page = pagination.current;
|
// search: "",
|
||||||
search.sortcolumn = sorter.columnKey;
|
// });
|
||||||
search.sortorder = sorter.order;
|
|
||||||
history.push({ search: queryString.stringify(search) });
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOnRowClick = (record) => {
|
// const handleTableChange = (pagination, filters, sorter) => {
|
||||||
if (record) {
|
// setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||||
if (record.id) {
|
// search.page = pagination.current;
|
||||||
search.invoiceid = record.id;
|
// search.sortcolumn = sorter.columnKey;
|
||||||
history.push({ search: queryString.stringify(search) });
|
// search.sortorder = sorter.order;
|
||||||
}
|
// history.push({ search: queryString.stringify(search) });
|
||||||
} else {
|
// };
|
||||||
delete search.invoiceid;
|
|
||||||
history.push({ search: queryString.stringify(search) });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const columns = [
|
// const handleOnRowClick = (record) => {
|
||||||
{
|
// if (record) {
|
||||||
title: t("invoices.fields.invoice_number"),
|
// if (record.id) {
|
||||||
dataIndex: "invoice_number",
|
// search.invoiceid = record.id;
|
||||||
key: "invoice_number",
|
// history.push({ search: queryString.stringify(search) });
|
||||||
sorter: (a, b) => alphaSort(a.invoice_number, b.invoice_number),
|
// }
|
||||||
sortOrder:
|
// } else {
|
||||||
state.sortedInfo.columnKey === "invoice_number" &&
|
// delete search.invoiceid;
|
||||||
state.sortedInfo.order,
|
// history.push({ search: queryString.stringify(search) });
|
||||||
},
|
// }
|
||||||
{
|
// };
|
||||||
title: t("invoices.fields.date"),
|
|
||||||
dataIndex: "date",
|
|
||||||
key: "date",
|
|
||||||
|
|
||||||
sorter: (a, b) => a.date - b.date,
|
// const columns = [
|
||||||
sortOrder:
|
// {
|
||||||
state.sortedInfo.columnKey === "date" && state.sortedInfo.order,
|
// title: t("invoices.fields.invoice_number"),
|
||||||
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
|
// dataIndex: "invoice_number",
|
||||||
},
|
// key: "invoice_number",
|
||||||
{
|
// sorter: (a, b) => alphaSort(a.invoice_number, b.invoice_number),
|
||||||
title: t("invoices.fields.total"),
|
// sortOrder:
|
||||||
dataIndex: "total",
|
// state.sortedInfo.columnKey === "invoice_number" &&
|
||||||
key: "total",
|
// state.sortedInfo.order,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// title: t("invoices.fields.date"),
|
||||||
|
// dataIndex: "date",
|
||||||
|
// key: "date",
|
||||||
|
|
||||||
sorter: (a, b) => a.total - b.total,
|
// sorter: (a, b) => a.date - b.date,
|
||||||
sortOrder:
|
// sortOrder:
|
||||||
state.sortedInfo.columnKey === "total" && state.sortedInfo.order,
|
// state.sortedInfo.columnKey === "date" && state.sortedInfo.order,
|
||||||
render: (text, record) => (
|
// render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
|
||||||
<CurrencyFormatter>{record.total}</CurrencyFormatter>
|
// },
|
||||||
),
|
// {
|
||||||
},
|
// title: t("invoices.fields.total"),
|
||||||
];
|
// dataIndex: "total",
|
||||||
|
// key: "total",
|
||||||
|
|
||||||
const handleSearch = (e) => {
|
// sorter: (a, b) => a.total - b.total,
|
||||||
setState({ ...state, search: e.target.value });
|
// sortOrder:
|
||||||
};
|
// state.sortedInfo.columnKey === "total" && state.sortedInfo.order,
|
||||||
|
// render: (text, record) => (
|
||||||
|
// <CurrencyFormatter>{record.total}</CurrencyFormatter>
|
||||||
|
// ),
|
||||||
|
// },
|
||||||
|
// ];
|
||||||
|
|
||||||
const dataSource = state.search
|
// const handleSearch = (e) => {
|
||||||
? data.invoices.filter(
|
// setState({ ...state, search: e.target.value });
|
||||||
(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' />;
|
// 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) || [];
|
||||||
|
|
||||||
return (
|
// if (error) return <AlertComponent message={error.message} type='error' />;
|
||||||
<Table
|
|
||||||
loading={loading}
|
// return (
|
||||||
title={() => {
|
// <Table
|
||||||
return (
|
// loading={loading}
|
||||||
<div>
|
// title={() => {
|
||||||
<Input
|
// return (
|
||||||
value={state.search}
|
// <div>
|
||||||
onChange={handleSearch}
|
// <Input
|
||||||
placeholder={t("general.labels.search")}
|
// value={state.search}
|
||||||
allowClear
|
// onChange={handleSearch}
|
||||||
/>
|
// placeholder={t("general.labels.search")}
|
||||||
</div>
|
// allowClear
|
||||||
);
|
// />
|
||||||
}}
|
// </div>
|
||||||
dataSource={dataSource}
|
// );
|
||||||
size='small'
|
// }}
|
||||||
scroll={{ x: true }}
|
// dataSource={dataSource}
|
||||||
pagination={{
|
// size='small'
|
||||||
position: "top",
|
// scroll={{ x: true }}
|
||||||
pageSize: 25,
|
// pagination={{
|
||||||
current: parseInt(page || 1),
|
// position: "top",
|
||||||
total: data ? data.invoices_aggregate.aggregate.count : 0,
|
// pageSize: 25,
|
||||||
}}
|
// current: parseInt(page || 1),
|
||||||
columns={columns}
|
// total: data ? data.invoices_aggregate.aggregate.count : 0,
|
||||||
rowKey='id'
|
// }}
|
||||||
onChange={handleTableChange}
|
// columns={columns}
|
||||||
rowSelection={{
|
// rowKey='id'
|
||||||
onSelect: (record) => {
|
// onChange={handleTableChange}
|
||||||
handleOnRowClick(record);
|
// rowSelection={{
|
||||||
},
|
// onSelect: (record) => {
|
||||||
selectedRowKeys: [search.invoiceid],
|
// handleOnRowClick(record);
|
||||||
type: "radio",
|
// },
|
||||||
}}
|
// selectedRowKeys: [search.invoiceid],
|
||||||
onRow={(record, rowIndex) => {
|
// type: "radio",
|
||||||
return {
|
// }}
|
||||||
onClick: (event) => {
|
// onRow={(record, rowIndex) => {
|
||||||
handleOnRowClick(record);
|
// return {
|
||||||
}, // click row
|
// onClick: (event) => {
|
||||||
};
|
// handleOnRowClick(record);
|
||||||
}}
|
// }, // click row
|
||||||
/>
|
// };
|
||||||
);
|
// }}
|
||||||
}
|
// />
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|||||||
@@ -94,7 +94,8 @@ export function InvoicesListTableComponent({
|
|||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<div>
|
<div>
|
||||||
<Link
|
<Link
|
||||||
to={`/manage/invoices?invoiceid=${record.id}&vendorid=${record.vendorid}`}>
|
to={`/manage/invoices?invoiceid=${record.id}&vendorid=${record.vendorid}`}
|
||||||
|
>
|
||||||
<Button>{t("invoices.actions.edit")}</Button>
|
<Button>{t("invoices.actions.edit")}</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<Button
|
<Button
|
||||||
@@ -119,7 +120,8 @@ export function InvoicesListTableComponent({
|
|||||||
isReturn: true,
|
isReturn: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}>
|
}
|
||||||
|
>
|
||||||
{t("invoices.actions.return")}
|
{t("invoices.actions.return")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -243,11 +245,11 @@ export function InvoicesListTableComponent({
|
|||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
<Table
|
<Table
|
||||||
size='small'
|
size="small"
|
||||||
scroll={{ x: "50%", y: "40rem" }}
|
scroll={{ x: "50%", y: "40rem" }}
|
||||||
pagination={{ position: "top", defaultPageSize: 25 }}
|
pagination={{ position: "top", defaultPageSize: 25 }}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
rowKey='id'
|
rowKey="id"
|
||||||
dataSource={record.invoicelines}
|
dataSource={record.invoicelines}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -261,37 +263,45 @@ export function InvoicesListTableComponent({
|
|||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
<Table
|
<Table
|
||||||
loading={loading}
|
loading={loading}
|
||||||
size='small'
|
size="small"
|
||||||
title={() => (
|
title={() => (
|
||||||
<div className='imex-table-header'>
|
<div className="imex-table-header">
|
||||||
<Button onClick={() => refetch()}>
|
<Button onClick={() => refetch()}>
|
||||||
<SyncOutlined />
|
<SyncOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
{job ? (
|
||||||
onClick={() => {
|
<div>
|
||||||
setInvoiceEnterContext({
|
<Button
|
||||||
actions: { refetch: invoicesQuery.refetch },
|
onClick={() => {
|
||||||
context: {
|
setInvoiceEnterContext({
|
||||||
job,
|
actions: { refetch: invoicesQuery.refetch },
|
||||||
},
|
context: {
|
||||||
});
|
job,
|
||||||
}}>
|
},
|
||||||
{t("jobs.actions.postInvoices")}
|
});
|
||||||
</Button>
|
}}
|
||||||
<Button
|
>
|
||||||
onClick={() => {
|
{t("jobs.actions.postInvoices")}
|
||||||
setReconciliationContext({
|
</Button>
|
||||||
actions: { refetch: invoicesQuery.refetch },
|
<Button
|
||||||
context: {
|
onClick={() => {
|
||||||
job,
|
setReconciliationContext({
|
||||||
invoices:
|
actions: { refetch: invoicesQuery.refetch },
|
||||||
(invoicesQuery.data && invoicesQuery.data.invoices) || [],
|
context: {
|
||||||
},
|
job,
|
||||||
});
|
invoices:
|
||||||
}}>
|
(invoicesQuery.data && invoicesQuery.data.invoices) ||
|
||||||
{t("jobs.actions.reconcile")}
|
[],
|
||||||
</Button>{" "}
|
},
|
||||||
<div className='imex-table-header__search'>
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("jobs.actions.reconcile")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<div className="imex-table-header__search">
|
||||||
<Input.Search
|
<Input.Search
|
||||||
placeholder={t("general.labels.search")}
|
placeholder={t("general.labels.search")}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -305,7 +315,7 @@ export function InvoicesListTableComponent({
|
|||||||
expandedRowRender={rowExpander}
|
expandedRowRender={rowExpander}
|
||||||
pagination={{ position: "top", defaultPageSize: 25 }}
|
pagination={{ position: "top", defaultPageSize: 25 }}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
rowKey='id'
|
rowKey="id"
|
||||||
dataSource={invoices}
|
dataSource={invoices}
|
||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
expandable={{
|
expandable={{
|
||||||
|
|||||||
@@ -11,13 +11,27 @@ export const INSERT_NEW_INVOICE = gql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const QUERY_ALL_INVOICES_PAGINATED = gql`
|
export const QUERY_ALL_INVOICES_PAGINATED = gql`
|
||||||
query QUERY_ALL_INVOICES_PAGINATED($offset: Int, $limit: Int) {
|
query QUERY_ALL_INVOICES_PAGINATED(
|
||||||
invoices(offset: $offset, limit: $limit, order_by: { date: desc }) {
|
$search: String
|
||||||
|
$offset: Int
|
||||||
|
$limit: Int
|
||||||
|
$order: [invoices_order_by!]!
|
||||||
|
) {
|
||||||
|
search_invoices(
|
||||||
|
args: { search: $search }
|
||||||
|
offset: $offset
|
||||||
|
limit: $limit
|
||||||
|
order_by: $order
|
||||||
|
) {
|
||||||
id
|
id
|
||||||
vendor {
|
vendor {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
federal_tax_rate
|
||||||
|
local_tax_rate
|
||||||
|
state_tax_rate
|
||||||
|
is_credit_memo
|
||||||
total
|
total
|
||||||
invoice_number
|
invoice_number
|
||||||
date
|
date
|
||||||
@@ -34,6 +48,11 @@ export const QUERY_ALL_INVOICES_PAGINATED = gql`
|
|||||||
line_desc
|
line_desc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
search_invoices_aggregate(args: { search: $search }) {
|
||||||
|
aggregate {
|
||||||
|
count(distinct: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
195
client/src/pages/invoices/invoices.page.component.jsx
Normal file
195
client/src/pages/invoices/invoices.page.component.jsx
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
import { SyncOutlined } from "@ant-design/icons";
|
||||||
|
import { Button, Checkbox, Input, Table, Typography } from "antd";
|
||||||
|
import queryString from "query-string";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { Link, useHistory, useLocation } from "react-router-dom";
|
||||||
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
|
import { alphaSort } from "../../utils/sorters";
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
setPartsOrderContext: (context) =>
|
||||||
|
dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
||||||
|
setInvoiceEnterContext: (context) =>
|
||||||
|
dispatch(setModalContext({ context: context, modal: "invoiceEnter" })),
|
||||||
|
});
|
||||||
|
|
||||||
|
export function InvoicesListPage({
|
||||||
|
loading,
|
||||||
|
data,
|
||||||
|
refetch,
|
||||||
|
total,
|
||||||
|
setPartsOrderContext,
|
||||||
|
setInvoiceEnterContext,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [state, setState] = useState({
|
||||||
|
sortedInfo: {},
|
||||||
|
});
|
||||||
|
const history = useHistory();
|
||||||
|
const search = queryString.parse(useLocation().search);
|
||||||
|
const { page } = search;
|
||||||
|
|
||||||
|
const selectedInvoice = search.invoiceid;
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t("invoices.fields.vendorname"),
|
||||||
|
dataIndex: "vendorname",
|
||||||
|
key: "vendorname",
|
||||||
|
// sorter: (a, b) => alphaSort(a.vendor.name, b.vendor.name),
|
||||||
|
// sortOrder:
|
||||||
|
// state.sortedInfo.columnKey === "vendorname" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => <span>{record.vendor.name}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("invoices.fields.is_credit_memo"),
|
||||||
|
dataIndex: "is_credit_memo",
|
||||||
|
key: "is_credit_memo",
|
||||||
|
sorter: (a, b) => a.is_credit_memo - b.is_credit_memo,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "is_credit_memo" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
render: (text, record) => <Checkbox checked={record.is_credit_memo} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("general.labels.actions"),
|
||||||
|
dataIndex: "actions",
|
||||||
|
key: "actions",
|
||||||
|
render: (text, record) => (
|
||||||
|
<div>
|
||||||
|
<Link to={`/manage/invoices?invoiceid=${record.id}`}>
|
||||||
|
<Button>{t("invoices.actions.edit")}</Button>
|
||||||
|
</Link>
|
||||||
|
<Button
|
||||||
|
disabled={record.is_credit_memo}
|
||||||
|
onClick={() =>
|
||||||
|
setPartsOrderContext({
|
||||||
|
actions: {},
|
||||||
|
context: {
|
||||||
|
jobId: record.jobid,
|
||||||
|
vendorId: record.vendorid,
|
||||||
|
returnFromInvoice: record.id,
|
||||||
|
invoiceNumber: record.invoice_number,
|
||||||
|
linesToOrder: record.invoicelines.map((i) => {
|
||||||
|
return {
|
||||||
|
line_desc: i.line_desc,
|
||||||
|
db_price: i.actual_price,
|
||||||
|
act_price: i.actual_cost,
|
||||||
|
quantity: i.quantity,
|
||||||
|
joblineid: i.joblineid,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
isReturn: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t("invoices.actions.return")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
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) });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Typography.Title level={4}>
|
||||||
|
{t("invoices.labels.invoices")}
|
||||||
|
</Typography.Title>
|
||||||
|
<Table
|
||||||
|
loading={loading}
|
||||||
|
size="small"
|
||||||
|
title={() => (
|
||||||
|
<div className="imex-table-header">
|
||||||
|
<Button onClick={() => refetch()}>
|
||||||
|
<SyncOutlined />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setInvoiceEnterContext({
|
||||||
|
actions: { refetch: refetch },
|
||||||
|
context: {},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("jobs.actions.postInvoices")}
|
||||||
|
</Button>
|
||||||
|
<div className="imex-table-header__search">
|
||||||
|
<Input.Search
|
||||||
|
placeholder={t("general.labels.search")}
|
||||||
|
onSearch={(value) => {
|
||||||
|
search.search = value;
|
||||||
|
history.push({ search: queryString.stringify(search) });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
scroll={{ x: "50%", y: "40rem" }}
|
||||||
|
pagination={{
|
||||||
|
position: "top",
|
||||||
|
pageSize: 25,
|
||||||
|
current: parseInt(page || 1),
|
||||||
|
total: total,
|
||||||
|
}}
|
||||||
|
columns={columns}
|
||||||
|
rowKey="id"
|
||||||
|
dataSource={data}
|
||||||
|
onChange={handleTableChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default connect(null, mapDispatchToProps)(InvoicesListPage);
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
import { Col, Row } from "antd";
|
import queryString from "query-string";
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
|
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 InvoiceDetailEditContainer from "../../components/invoice-detail-edit/invoice-detail-edit.container";
|
import InvoiceDetailEditContainer from "../../components/invoice-detail-edit/invoice-detail-edit.container";
|
||||||
import InvoicesByVendorList from "../../components/invoices-by-vendor-list/invoices-by-vendor-list.component";
|
import { QUERY_ALL_INVOICES_PAGINATED } from "../../graphql/invoices.queries";
|
||||||
import VendorsList from "../../components/invoices-vendors-list/invoices-vendors-list.component";
|
|
||||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||||
|
import InvoicesPageComponent from "./invoices.page.component";
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||||
@@ -13,6 +15,9 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
|
|
||||||
export function InvoicesPageContainer({ setBreadcrumbs }) {
|
export function InvoicesPageContainer({ setBreadcrumbs }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const searchParams = queryString.parse(useLocation().search);
|
||||||
|
const { page, sortcolumn, sortorder, search } = searchParams;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = t("titles.invoices-list");
|
document.title = t("titles.invoices-list");
|
||||||
setBreadcrumbs([
|
setBreadcrumbs([
|
||||||
@@ -20,20 +25,37 @@ export function InvoicesPageContainer({ setBreadcrumbs }) {
|
|||||||
]);
|
]);
|
||||||
}, [t, setBreadcrumbs]);
|
}, [t, setBreadcrumbs]);
|
||||||
|
|
||||||
|
const { loading, error, data, refetch } = useQuery(
|
||||||
|
QUERY_ALL_INVOICES_PAGINATED,
|
||||||
|
{
|
||||||
|
variables: {
|
||||||
|
search: search || "",
|
||||||
|
offset: page ? (page - 1) * 25 : 0,
|
||||||
|
limit: 25,
|
||||||
|
order: [
|
||||||
|
{
|
||||||
|
[sortcolumn || "date"]: sortorder
|
||||||
|
? sortorder === "descend"
|
||||||
|
? "desc"
|
||||||
|
: "asc"
|
||||||
|
: "desc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row>
|
<div>
|
||||||
<Col span={8}>
|
<InvoicesPageComponent
|
||||||
<VendorsList />
|
data={data ? data.search_invoices : []}
|
||||||
</Col>
|
loading={loading}
|
||||||
<Col span={16}>
|
refetch={refetch}
|
||||||
<Row>
|
total={data ? data.search_invoices_aggregate.aggregate.count : 0}
|
||||||
<InvoicesByVendorList />
|
/>
|
||||||
</Row>
|
|
||||||
<Row>
|
<InvoiceDetailEditContainer />
|
||||||
<InvoiceDetailEditContainer />
|
</div>
|
||||||
</Row>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export default connect(null, mapDispatchToProps)(InvoicesPageContainer);
|
export default connect(null, mapDispatchToProps)(InvoicesPageContainer);
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
- args:
|
||||||
|
cascade: true
|
||||||
|
read_only: false
|
||||||
|
sql: CREATE INDEX idx_invoices_invoicenumber ON invoices USING GIN (invoice_number
|
||||||
|
gin_trgm_ops);
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
12
hasura/migrations/1595971216422_run_sql_migration/up.yaml
Normal file
12
hasura/migrations/1595971216422_run_sql_migration/up.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
- args:
|
||||||
|
cascade: true
|
||||||
|
read_only: false
|
||||||
|
sql: "CREATE OR REPLACE FUNCTION public.search_invoices(search text)\n RETURNS
|
||||||
|
SETOF invoices\n LANGUAGE plpgsql\n STABLE\nAS $function$\n\nBEGIN\n if search
|
||||||
|
= '' then\n return query select * from invoices ;\n else \n return query
|
||||||
|
SELECT\n *\nFROM\n payments\nWHERE\n search <% (invoice_number);\n end if;\n\n\tEND\n$function$;"
|
||||||
|
type: run_sql
|
||||||
|
- args:
|
||||||
|
name: search_invoices
|
||||||
|
schema: public
|
||||||
|
type: track_function
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
- args:
|
||||||
|
cascade: true
|
||||||
|
read_only: false
|
||||||
|
sql: "CREATE OR REPLACE FUNCTION public.search_invoices(search text)\n RETURNS
|
||||||
|
SETOF invoices\n LANGUAGE plpgsql\n STABLE\nAS $function$\n\nBEGIN\n if search
|
||||||
|
= '' then\n return query select * from invoices ;\n else \n return query
|
||||||
|
SELECT\n *\nFROM\n invoices\nWHERE\n search <% (invoice_number);\n end if;\n\n\tEND\n$function$;"
|
||||||
|
type: run_sql
|
||||||
@@ -4053,6 +4053,9 @@ tables:
|
|||||||
- active:
|
- active:
|
||||||
_eq: true
|
_eq: true
|
||||||
functions:
|
functions:
|
||||||
|
- function:
|
||||||
|
schema: public
|
||||||
|
name: search_invoices
|
||||||
- function:
|
- function:
|
||||||
schema: public
|
schema: public
|
||||||
name: search_jobs
|
name: search_jobs
|
||||||
|
|||||||
Reference in New Issue
Block a user