IO-2261 Opensearch for Payments, Bills and All Jobs

This commit is contained in:
Allan Carr
2023-05-03 16:29:01 -07:00
parent 852fd9c388
commit 759a8ac58c
10 changed files with 198 additions and 86 deletions

View File

@@ -1,5 +1,6 @@
import { SyncOutlined } from "@ant-design/icons"; import { SyncOutlined } from "@ant-design/icons";
import { Button, Card, Input, Space, Table, Typography } from "antd"; import { Button, Card, Input, Space, Table, Typography } from "antd";
import axios from "axios";
import _ from "lodash"; import _ from "lodash";
import queryString from "query-string"; import queryString from "query-string";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
@@ -11,7 +12,6 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import StartChatButton from "../chat-open-button/chat-open-button.component"; import StartChatButton from "../chat-open-button/chat-open-button.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import axios from "axios";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -209,11 +209,11 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
search: value || search.search, search: value || search.search,
index: "jobs", index: "jobs",
}); });
setOpenSearchResults(searchData.data.hits.hits.map((s) => s._source)); setOpenSearchResults(searchData.data.hits.hits.map((s) => s._source));
} catch (error) { } catch (error) {
console.log("Error while fetching search results", error);
} finally { } finally {
//setLoading(false) // setLoading(false);
} }
} }

View File

@@ -1,7 +1,8 @@
import { EditFilled, SyncOutlined } from "@ant-design/icons"; import { EditFilled, SyncOutlined } from "@ant-design/icons";
import { Button, Card, Input, Space, Table, Typography } from "antd"; import { Button, Card, Input, Space, Table, Typography } from "antd";
import axios from "axios";
import queryString from "query-string"; import queryString from "query-string";
import React, { 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 { connect } from "react-redux";
import { Link, useHistory, useLocation } from "react-router-dom"; import { Link, useHistory, useLocation } from "react-router-dom";
@@ -10,11 +11,11 @@ import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter"; import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
import { alphaSort } from "../../utils/sorters";
import CaBcEtfTableModalContainer from "../ca-bc-etf-table-modal/ca-bc-etf-table-modal.container"; import CaBcEtfTableModalContainer from "../ca-bc-etf-table-modal/ca-bc-etf-table-modal.container";
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
@@ -39,6 +40,7 @@ export function PaymentsListPaginated({
bodyshop, bodyshop,
}) { }) {
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const [openSearchResults, setOpenSearchResults] = useState([]);
const { page, sortcolumn, sortorder } = search; const { page, sortcolumn, sortorder } = search;
const history = useHistory(); const history = useHistory();
const [state, setState] = useState({ const [state, setState] = useState({
@@ -54,11 +56,15 @@ export function PaymentsListPaginated({
key: "ro_number", key: "ro_number",
sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number), sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number),
sortOrder: sortcolumn === "ro_number" && sortorder, sortOrder: sortcolumn === "ro_number" && sortorder,
render: (text, record) => ( render: (text, record) => {
<Link to={"/manage/jobs/" + record.job.id}> return record.job ? (
{record.job.ro_number || t("general.labels.na")} <Link to={"/manage/jobs/" + record.job.id}>
</Link> {record.job.ro_number || t("general.labels.na")}
), </Link>
) : (
<span>{t("general.labels.na")}</span>
);
},
}, },
{ {
title: t("payments.fields.paymentnum"), title: t("payments.fields.paymentnum"),
@@ -75,13 +81,13 @@ export function PaymentsListPaginated({
sorter: (a, b) => alphaSort(a.job.ownr_ln, b.job.ownr_ln), sorter: (a, b) => alphaSort(a.job.ownr_ln, b.job.ownr_ln),
sortOrder: sortcolumn === "owner" && sortorder, sortOrder: sortcolumn === "owner" && sortorder,
render: (text, record) => { render: (text, record) => {
return record.job.owner ? ( return record.job?.owner ? (
<Link to={"/manage/owners/" + record.job.owner.id}> <Link to={"/manage/owners/" + record.job?.owner?.id}>
<OwnerNameDisplay ownerObject={record} /> <OwnerNameDisplay ownerObject={record.job} />
</Link> </Link>
) : ( ) : (
<span> <span>
<OwnerNameDisplay ownerObject={record} /> <OwnerNameDisplay ownerObject={record.job} />
</span> </span>
); );
}, },
@@ -177,6 +183,28 @@ export function PaymentsListPaginated({
history.push({ search: queryString.stringify(search) }); history.push({ search: queryString.stringify(search) });
}; };
useEffect(() => {
if (search.search && search.search.trim() !== "") {
// setLoading(true);
searchPayments();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
async function searchPayments(value) {
try {
const searchData = await axios.post("/search", {
search: value || search.search,
index: "payments",
});
setOpenSearchResults(searchData.data.hits.hits.map((s) => s._source));
} catch (error) {
console.log("Error while fetching search results", error);
} finally {
// setLoading(false);
}
}
return ( return (
<Card <Card
extra={ extra={
@@ -212,6 +240,7 @@ export function PaymentsListPaginated({
onSearch={(value) => { onSearch={(value) => {
search.search = value; search.search = value;
history.push({ search: queryString.stringify(search) }); history.push({ search: queryString.stringify(search) });
searchPayments(value);
}} }}
enterButton enterButton
/> />
@@ -221,15 +250,20 @@ export function PaymentsListPaginated({
<Table <Table
loading={loading} loading={loading}
scroll={{ x: true }} scroll={{ x: true }}
pagination={{ pagination={
position: "top", search?.search
pageSize: 25, ? { pageSize: 25 }
current: parseInt(page || 1), : {
total: total, position: "top",
}} pageSize: 25,
current: parseInt(page || 1),
total: total,
showSizeChanger: false,
}
}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={payments} dataSource={search?.search ? openSearchResults : payments}
onChange={handleTableChange} onChange={handleTableChange}
/> />
</Card> </Card>

View File

@@ -20,13 +20,11 @@ export const DELETE_BILL = gql`
export const QUERY_ALL_BILLS_PAGINATED = gql` export const QUERY_ALL_BILLS_PAGINATED = gql`
query QUERY_ALL_BILLS_PAGINATED( query QUERY_ALL_BILLS_PAGINATED(
$search: String
$offset: Int $offset: Int
$limit: Int $limit: Int
$order: [bills_order_by!]! $order: [bills_order_by!]!
) { ) {
search_bills( bills(
args: { search: $search }
offset: $offset offset: $offset
limit: $limit limit: $limit
order_by: $order order_by: $order
@@ -51,7 +49,7 @@ export const QUERY_ALL_BILLS_PAGINATED = gql`
ro_number ro_number
} }
} }
search_bills_aggregate(args: { search: $search }) { bills_aggregate {
aggregate { aggregate {
count(distinct: true) count(distinct: true)
} }

View File

@@ -12,13 +12,11 @@ export const INSERT_NEW_PAYMENT = gql`
export const QUERY_ALL_PAYMENTS_PAGINATED = gql` export const QUERY_ALL_PAYMENTS_PAGINATED = gql`
query QUERY_ALL_PAYMENTS_PAGINATED( query QUERY_ALL_PAYMENTS_PAGINATED(
$search: String
$offset: Int $offset: Int
$limit: Int $limit: Int
$order: [payments_order_by!]! $order: [payments_order_by!]!
) { ) {
search_payments( payments(
args: { search: $search }
offset: $offset offset: $offset
limit: $limit limit: $limit
order_by: $order order_by: $order
@@ -31,9 +29,16 @@ export const QUERY_ALL_PAYMENTS_PAGINATED = gql`
job { job {
id id
ro_number ro_number
ownerid
ownr_co_nm
ownr_fn ownr_fn
ownr_ln ownr_ln
ownr_co_nm owner {
id
ownr_co_nm
ownr_fn
ownr_ln
}
} }
transactionid transactionid
memo memo
@@ -44,7 +49,7 @@ export const QUERY_ALL_PAYMENTS_PAGINATED = gql`
stripeid stripeid
payer payer
} }
search_payments_aggregate(args: { search: $search }) { payments_aggregate {
aggregate { aggregate {
count(distinct: true) count(distinct: true)
} }

View File

@@ -1,7 +1,8 @@
import { SyncOutlined, EditFilled } from "@ant-design/icons"; import { EditFilled, SyncOutlined } from "@ant-design/icons";
import { Button, Card, Checkbox, Input, Space, Table, Typography } from "antd"; import { Button, Card, Checkbox, Input, Space, Table, Typography } from "antd";
import axios from "axios";
import queryString from "query-string"; import queryString from "query-string";
import React, { 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 { connect } from "react-redux";
import { Link, useHistory, useLocation } from "react-router-dom"; import { Link, useHistory, useLocation } from "react-router-dom";
@@ -11,8 +12,8 @@ import PrintWrapperComponent from "../../components/print-wrapper/print-wrapper.
import { setModalContext } from "../../redux/modals/modals.actions"; import { setModalContext } from "../../redux/modals/modals.actions";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter"; import { DateFormatter } from "../../utils/DateFormatter";
import { alphaSort, dateSort } from "../../utils/sorters";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
import { alphaSort, dateSort } from "../../utils/sorters";
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setPartsOrderContext: (context) => setPartsOrderContext: (context) =>
@@ -29,15 +30,16 @@ export function BillsListPage({
setPartsOrderContext, setPartsOrderContext,
setBillEnterContext, setBillEnterContext,
}) { }) {
const { t } = useTranslation(); const search = queryString.parse(useLocation().search);
const [openSearchResults, setOpenSearchResults] = useState([]);
const { page } = search;
const history = useHistory();
const [state, setState] = useState({ const [state, setState] = useState({
sortedInfo: {}, sortedInfo: {},
filteredInfo: { text: "" },
}); });
const history = useHistory();
const search = queryString.parse(useLocation().search);
const { page } = search;
const Templates = TemplateList("bill"); const Templates = TemplateList("bill");
const { t } = useTranslation();
const columns = [ const columns = [
{ {
title: t("bills.fields.vendorname"), title: t("bills.fields.vendorname"),
@@ -199,11 +201,32 @@ export function BillsListPage({
search.sortcolumn = sorter.order ? sorter.columnKey : null; search.sortcolumn = sorter.order ? sorter.columnKey : null;
search.sortorder = sorter.order; search.sortorder = sorter.order;
} }
search.sort = JSON.stringify({ [sorter.columnKey]: sorter.order }); search.sort = JSON.stringify({ [sorter.columnKey]: sorter.order });
history.push({ search: queryString.stringify(search) }); history.push({ search: queryString.stringify(search) });
}; };
useEffect(() => {
if (search.search && search.search.trim() !== "") {
// setLoading(true);
searchBills();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
async function searchBills(value) {
try {
const searchData = await axios.post("/search", {
search: value || search.search,
index: "bills",
});
setOpenSearchResults(searchData.data.hits.hits.map((s) => s._source));
} catch (error) {
console.log("Error while fetching search results", error);
} finally {
// setLoading(false);
}
}
return ( return (
<Card <Card
title={t("bills.labels.bills")} title={t("bills.labels.bills")}
@@ -243,7 +266,9 @@ export function BillsListPage({
onSearch={(value) => { onSearch={(value) => {
search.search = value; search.search = value;
history.push({ search: queryString.stringify(search) }); history.push({ search: queryString.stringify(search) });
searchBills(value);
}} }}
enterButton
/> />
</Space> </Space>
} }
@@ -252,18 +277,24 @@ export function BillsListPage({
<Table <Table
loading={loading} loading={loading}
scroll={{ // scroll={{
x: "50%", // y: "40rem" // x: "50%", // y: "40rem"
}} // }}
pagination={{ scroll={{ x: true }}
position: "top", pagination={
pageSize: 25, search?.search
current: parseInt(page || 1), ? { pageSize: 25 }
total: total, : {
}} position: "top",
pageSize: 25,
current: parseInt(page || 1),
total: total,
showSizeChanger: false,
}
}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={data} dataSource={search?.search ? openSearchResults : data}
onChange={handleTableChange} onChange={handleTableChange}
/> />
</Card> </Card>

View File

@@ -1,6 +1,6 @@
import { useQuery } from "@apollo/client";
import queryString from "query-string"; import queryString from "query-string";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { useQuery } from "@apollo/client";
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";
@@ -22,7 +22,7 @@ const mapDispatchToProps = (dispatch) => ({
export function BillsPageContainer({ 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, searchObj } = searchParams; const { page, sortcolumn, sortorder, searchObj } = searchParams;
useEffect(() => { useEffect(() => {
document.title = t("titles.bills-list"); document.title = t("titles.bills-list");
@@ -38,7 +38,6 @@ export function BillsPageContainer({ setBreadcrumbs, setSelectedHeader }) {
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
variables: { variables: {
search: search || "",
offset: page ? (page - 1) * 25 : 0, offset: page ? (page - 1) * 25 : 0,
limit: 25, limit: 25,
order: [ order: [
@@ -61,10 +60,10 @@ export function BillsPageContainer({ setBreadcrumbs, setSelectedHeader }) {
<RbacWrapper action="bills:list"> <RbacWrapper action="bills:list">
<div> <div>
<BillsPageComponent <BillsPageComponent
data={data ? data.search_bills : []} data={data ? data.bills : []}
loading={loading} loading={loading}
refetch={refetch} refetch={refetch}
total={data ? data.search_bills_aggregate.aggregate.count : 0} total={data ? data.bills_aggregate.aggregate.count : 0}
/> />
<BillDetailEditContainer /> <BillDetailEditContainer />

View File

@@ -25,7 +25,7 @@ const mapDispatchToProps = (dispatch) => ({
export function AllJobs({ setBreadcrumbs, setSelectedHeader }) { export function AllJobs({ setBreadcrumbs, setSelectedHeader }) {
const searchParams = queryString.parse(useLocation().search); const searchParams = queryString.parse(useLocation().search);
const { page, sortcolumn, sortorder, search, statusFilters } = searchParams; const { page, sortcolumn, sortorder, statusFilters } = searchParams;
const { loading, error, data, refetch } = useQuery( const { loading, error, data, refetch } = useQuery(
QUERY_ALL_JOBS_PAGINATED_STATUS_FILTERED, QUERY_ALL_JOBS_PAGINATED_STATUS_FILTERED,

View File

@@ -26,7 +26,7 @@ const mapDispatchToProps = (dispatch) => ({
export function AllJobs({ bodyshop, setBreadcrumbs, setSelectedHeader }) { export function AllJobs({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
const searchParams = queryString.parse(useLocation().search); const searchParams = queryString.parse(useLocation().search);
const { page, sortcolumn, sortorder, search } = searchParams; const { page, sortcolumn, sortorder, searchObj } = searchParams;
const { loading, error, data, refetch } = useQuery( const { loading, error, data, refetch } = useQuery(
QUERY_ALL_PAYMENTS_PAGINATED, QUERY_ALL_PAYMENTS_PAGINATED,
@@ -34,11 +34,12 @@ export function AllJobs({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
variables: { variables: {
search: search || "",
offset: page ? (page - 1) * 25 : 0, offset: page ? (page - 1) * 25 : 0,
limit: 25, limit: 25,
order: [ order: [
{ searchObj
? JSON.parse(searchObj)
: {
[sortcolumn || "date"]: sortorder [sortcolumn || "date"]: sortorder
? sortorder === "descend" ? sortorder === "descend"
? "desc" ? "desc"
@@ -66,8 +67,8 @@ export function AllJobs({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
refetch={refetch} refetch={refetch}
loading={loading} loading={loading}
searchParams={searchParams} searchParams={searchParams}
total={data ? data.search_payments_aggregate.aggregate.count : 0} total={data ? data.payments_aggregate.aggregate.count : 0}
payments={data ? data.search_payments : []} payments={data ? data.payments : []}
/> />
</RbacWrapper> </RbacWrapper>
); );

View File

@@ -50,7 +50,7 @@ const getClient = async () => {
async function OpenSearchUpdateHandler(req, res) { async function OpenSearchUpdateHandler(req, res) {
try { try {
var osClient = await getClient(); var osClient = await getClient();
// const osClient = new Client({ // const osClient = new Client({
// node: `https://imex:password@search-imexonline-search-ixp2stfvwp6qocjsowzjzyreoy.ca-central-1.es.amazonaws.com`, // node: `https://imex:password@search-imexonline-search-ixp2stfvwp6qocjsowzjzyreoy.ca-central-1.es.amazonaws.com`,
// }); // });
@@ -74,12 +74,18 @@ async function OpenSearchUpdateHandler(req, res) {
const jobsData = await gqlclient.request(`query{jobs{ const jobsData = await gqlclient.request(`query{jobs{
id id
bodyshopid:shopid bodyshopid:shopid
ro_number
clm_no clm_no
clm_total
comment
ins_co_nm
ownr_co_nm
ownr_fn ownr_fn
ownr_ln ownr_ln
ownr_ph1
ownr_ph2
plate_no
ro_number
status status
ownr_co_nm
v_model_yr v_model_yr
v_make_desc v_make_desc
v_model_desc v_model_desc
@@ -128,12 +134,12 @@ async function OpenSearchUpdateHandler(req, res) {
vehicles { vehicles {
id id
bodyshopid: shopid bodyshopid: shopid
v_model_yr plate_no
v_model_desc v_model_yr
v_make_desc v_model_desc
v_color v_make_desc
v_vin v_color
plate_no v_vin
} }
} }
`); `);
@@ -155,11 +161,25 @@ plate_no
payments { payments {
id id
amount amount
paymentnum created_at
exportedat
memo memo
payer
paymentnum
transactionid transactionid
type
job { job {
id id
ownerid
ownr_co_nm
ownr_fn
ownr_ln
owner {
id
ownr_co_nm
ownr_fn
ownr_ln
}
ro_number ro_number
bodyshopid: shopid bodyshopid: shopid
} }
@@ -187,9 +207,11 @@ plate_no
const billsData = await gqlclient.request(`{ const billsData = await gqlclient.request(`{
bills { bills {
id id
total
invoice_number
date date
exported
invoice_number
is_credit_memo
total
vendor { vendor {
name name
id id
@@ -200,9 +222,7 @@ plate_no
bodyshopid: shopid bodyshopid: shopid
} }
} }
} }`);
`);
for (let i = 0; i <= billsData.bills.length / batchSize; i++) { for (let i = 0; i <= billsData.bills.length / batchSize; i++) {
const slicedArray = billsData.bills.slice( const slicedArray = billsData.bills.slice(
i * batchSize, i * batchSize,

View File

@@ -66,18 +66,21 @@ async function OpenSearchUpdateHandler(req, res) {
document = _.pick(req.body.event.data.new, [ document = _.pick(req.body.event.data.new, [
"id", "id",
"bodyshopid", "bodyshopid",
"ro_number",
"clm_no", "clm_no",
"clm_total",
"comment",
"ins_co_nm",
"ownr_co_nm",
"ownr_fn", "ownr_fn",
"ownr_ln", "ownr_ln",
"ownr_ph1",
"ownr_ph2",
"plate_no",
"ro_number",
"status", "status",
"ownr_co_nm",
"v_model_yr", "v_model_yr",
"v_make_desc", "v_make_desc",
"v_model_desc", "v_model_desc",
"clm_total",
"plate_no",
"ownr_ph1",
]); ]);
document.bodyshopid = req.body.event.data.new.shopid; document.bodyshopid = req.body.event.data.new.shopid;
break; break;
@@ -127,8 +130,10 @@ async function OpenSearchUpdateHandler(req, res) {
document = { document = {
..._.pick(req.body.event.data.new, [ ..._.pick(req.body.event.data.new, [
"id", "id",
"invoice_number",
"date", "date",
"exported",
"invoice_number",
"is_credit_memo",
]), ]),
...bill.bills_by_pk, ...bill.bills_by_pk,
bodyshopid: bill.bills_by_pk.job.shopid, bodyshopid: bill.bills_by_pk.job.shopid,
@@ -145,15 +150,34 @@ async function OpenSearchUpdateHandler(req, res) {
id id
ro_number ro_number
shopid shopid
ownerid
ownr_co_nm
ownr_fn
ownr_ln
owner {
id
ownr_co_nm
ownr_fn
ownr_ln
}
} }
} }
} }
`,
`,
{ paymentId: req.body.event.data.new.id } { paymentId: req.body.event.data.new.id }
); );
document = { document = {
..._.pick(req.body.event.data.new, ["id", "invoice_number"]), ..._.pick(req.body.event.data.new, [
"id",
"amount",
"created_at",
"exportedat",
"memo",
"payer",
"paymentnum",
"transactionid",
"type",
]),
...payment.payments_by_pk, ...payment.payments_by_pk,
bodyshopid: bill.payments_by_pk.job.shopid, bodyshopid: bill.payments_by_pk.job.shopid,
}; };