IO-710 Export Log page & insert actions on export

This commit is contained in:
Patrick Fic
2021-04-21 14:31:40 -07:00
parent 6d9576b9a4
commit 09e1ab7f83
38 changed files with 653 additions and 180 deletions

View File

@@ -1 +1 @@
client_max_body_size 50M;
client_max_body_size 15M;

View File

@@ -1,4 +1,4 @@
<babeledit_project be_version="2.7.1" version="1.2">
<babeledit_project version="1.2" be_version="2.7.1">
<!--
BabelEdit project file
@@ -12354,6 +12354,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>created_at</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>email</name>
<definition_loaded>false</definition_loaded>
@@ -12627,6 +12648,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>message</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>monday</name>
<definition_loaded>false</definition_loaded>
@@ -13073,6 +13115,27 @@
</concept_node>
</children>
</folder_node>
<concept_node>
<name>successful</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>sunday</name>
<definition_loaded>false</definition_loaded>
@@ -23055,6 +23118,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>export-logs</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>home</name>
<definition_loaded>false</definition_loaded>
@@ -31520,6 +31604,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>export-logs</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>jobs</name>
<definition_loaded>false</definition_loaded>
@@ -32236,6 +32341,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>export-logs</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>jobs</name>
<definition_loaded>false</definition_loaded>

View File

@@ -6,7 +6,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { alphaSort } from "../../utils/sorters";
import JobExportButton from "../jobs-close-export-button/jobs-close-export-button.component";
import { JobsExportAllButton } from "../jobs-export-all-button/jobs-export-all-button.component";
import JobsExportAllButton from "../jobs-export-all-button/jobs-export-all-button.component";
export default function AccountingReceivablesTableComponent({ loading, jobs }) {
const { t } = useTranslation();

View File

@@ -7,15 +7,24 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { auth } from "../../firebase/firebase.utils";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
export function JobsCloseExportButton({ bodyshop, jobId, disabled }) {
export function JobsCloseExportButton({
bodyshop,
currentUser,
jobId,
disabled,
}) {
const { t } = useTranslation();
const [updateJob] = useMutation(UPDATE_JOB);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
@@ -77,35 +86,39 @@ export function JobsCloseExportButton({ bodyshop, jobId, disabled }) {
failedTransactions.forEach((ft) => {
//insert failed export log
notification.open({
key: "failedexports",
// key: "failedexports",
type: "error",
message: t("jobs.errors.exporting", {
error: ft.errorMessage || "",
}),
});
//Call is not awaited as it is not critical to finish before proceeding.
insertExportLog({
});
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
jobid: jobId,
success: false,
message: JSON.stringify(ft.errorMessage),
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
});
});
} else {
//Insert success export log.
insertExportLog({
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
jobid: jobId,
success: true,
successful: true,
useremail: currentUser.email,
},
],
},
@@ -123,7 +136,7 @@ export function JobsCloseExportButton({ bodyshop, jobId, disabled }) {
if (!jobUpdateResponse.errors) {
notification.open({
type: "error",
type: "success",
key: "jobsuccessexport",
message: t("jobs.successes.exported"),
});

View File

@@ -7,15 +7,21 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { UPDATE_JOBS } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
export function JobsExportAllButton({
bodyshop,
currentUser,
jobIds,
disabled,
loadingCallback,
@@ -23,6 +29,8 @@ export function JobsExportAllButton({
}) {
const { t } = useTranslation();
const [updateJob] = useMutation(UPDATE_JOBS);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const [loading, setLoading] = useState(false);
const handleQbxml = async () => {
logImEXEvent("jobs_export_all");
@@ -73,35 +81,67 @@ export function JobsExportAllButton({
console.log("PartnerResponse", PartnerResponse);
const groupedData = _.groupBy(PartnerResponse.data, "id");
const proms = [];
Object.keys(groupedData).forEach((key) => {
proms.push(async () => {
await Promise.all(
Object.keys(groupedData).map(async (key) => {
//Check to see if any of them failed. If they didn't don't execute the update.
const failedTransactions = groupedData[key].filter((r) => !r.success);
if (failedTransactions.length > 0) {
//Uh oh. At least one was no good.
failedTransactions.map((ft) =>
notification["error"]({
failedTransactions.forEach((ft) => {
notification.open({
// key: "failedexports",
type: "error",
message: t("jobs.errors.exporting", {
error: ft.errorMessage || "",
}),
})
);
});
//Call is not awaited as it is not critical to finish before proceeding.
});
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
jobid: key,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
});
} else {
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
jobid: key,
successful: true,
useremail: currentUser.email,
},
],
},
});
const jobUpdateResponse = await updateJob({
variables: {
jobIds: [key],
job: {
fields: {
status: bodyshop.md_ro_statuses.default_exported || "Exported*",
date_exported: new Date(),
},
},
});
if (!!!jobUpdateResponse.errors) {
notification["success"]({
if (!jobUpdateResponse.errors) {
notification.open({
type: "success",
key: "jobsuccessexport",
message: t("jobs.successes.exported"),
});
} else {
@@ -112,9 +152,8 @@ export function JobsExportAllButton({
});
}
}
});
});
await Promise.all(proms);
})
);
if (!!completedCallback) completedCallback([]);
if (!!loadingCallback) loadingCallback(false);

View File

@@ -7,16 +7,22 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { auth } from "../../firebase/firebase.utils";
import { UPDATE_BILLS } from "../../graphql/bills.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { logImEXEvent } from "../../firebase/firebase.utils";
import _ from "lodash";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
export function PayableExportAll({
bodyshop,
currentUser,
billids,
disabled,
loadingCallback,
@@ -25,6 +31,7 @@ export function PayableExportAll({
const { t } = useTranslation();
const [updateBill] = useMutation(UPDATE_BILLS);
const [loading, setLoading] = useState(false);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const handleQbxml = async () => {
logImEXEvent("accounting_payables_export_all");
@@ -89,7 +96,36 @@ export function PayableExportAll({
}),
})
);
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
billid: key,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
});
} else {
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
billid: key,
successful: true,
useremail: currentUser.email,
},
],
},
});
const billUpdateResponse = await updateBill({
variables: {
billIdList: [key],
@@ -100,7 +136,9 @@ export function PayableExportAll({
},
});
if (!!!billUpdateResponse.errors) {
notification["success"]({
notification.open({
type: "success",
key: "billsuccessexport",
message: t("bills.successes.exported"),
});
} else {

View File

@@ -7,15 +7,21 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { auth } from "../../firebase/firebase.utils";
import { UPDATE_BILLS } from "../../graphql/bills.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
export function PayableExportButton({
bodyshop,
currentUser,
billId,
disabled,
loadingCallback,
@@ -23,6 +29,7 @@ export function PayableExportButton({
const { t } = useTranslation();
const [updateBill] = useMutation(UPDATE_BILLS);
const [loading, setLoading] = useState(false);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const handleQbxml = async () => {
logImEXEvent("accounting_export_payable");
@@ -84,8 +91,35 @@ export function PayableExportButton({
}),
})
);
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
billid: billId,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
});
}
if (successfulTransactions.length > 0) {
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
billid: billId,
successful: true,
useremail: currentUser.email,
},
],
},
});
const billUpdateResponse = await updateBill({
variables: {
billIdList: successfulTransactions.map((st) => st.id),
@@ -96,7 +130,9 @@ export function PayableExportButton({
},
});
if (!!!billUpdateResponse.errors) {
notification["success"]({
notification.open({
type: "success",
key: "billsuccessexport",
message: t("bills.successes.exported"),
});
} else {

View File

@@ -6,15 +6,21 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { UPDATE_PAYMENTS } from "../../graphql/payments.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
export function PaymentExportButton({
bodyshop,
currentUser,
paymentId,
disabled,
loadingCallback,
@@ -22,6 +28,7 @@ export function PaymentExportButton({
const { t } = useTranslation();
const [updatePayment] = useMutation(UPDATE_PAYMENTS);
const [loading, setLoading] = useState(false);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const handleQbxml = async () => {
logImEXEvent("accounting_payment_export");
@@ -83,7 +90,36 @@ export function PaymentExportButton({
}),
})
);
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: paymentId,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
});
} else {
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: paymentId,
successful: true,
useremail: currentUser.email,
},
],
},
});
const paymentUpdateResponse = await updatePayment({
variables: {
paymentIdList: [paymentId],
@@ -93,7 +129,9 @@ export function PaymentExportButton({
},
});
if (!!!paymentUpdateResponse.errors) {
notification["success"]({
notification.open({
type: "success",
key: "paymentsuccessexport",
message: t("payments.successes.exported"),
});
} else {

View File

@@ -7,14 +7,20 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { auth } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { UPDATE_PAYMENTS } from "../../graphql/payments.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
export function PaymentsExportAllButton({
bodyshop,
currentUser,
paymentIds,
disabled,
loadingCallback,
@@ -23,6 +29,7 @@ export function PaymentsExportAllButton({
const { t } = useTranslation();
const [updatePayments] = useMutation(UPDATE_PAYMENTS);
const [loading, setLoading] = useState(false);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const handleQbxml = async () => {
setLoading(true);
@@ -84,7 +91,34 @@ export function PaymentsExportAllButton({
}),
})
);
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: key,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
});
} else {
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: key,
successful: true,
useremail: currentUser.email,
},
],
},
});
const paymentUpdateResponse = await updatePayments({
variables: {
paymentIdList: [key],
@@ -94,7 +128,9 @@ export function PaymentsExportAllButton({
},
});
if (!!!paymentUpdateResponse.errors) {
notification["success"]({
notification.open({
type: "success",
key: "paymentsuccessexport",
message: t("payments.successes.exported"),
});
} else {

View File

@@ -86,11 +86,17 @@ export const INSERT_EXPORT_LOG = gql`
export const QUERY_EXPORT_LOG_PAGINATED = gql`
query QUERY_ALL_EXPORTLOG_PAGINATED(
$search: String
$offset: Int
$limit: Int
$order: [exportlog_order_by!]
) {
exportlog(offset: $offset, limit: $limit, order_by: $order) {
search_exportlog(
offset: $offset
limit: $limit
order_by: $order
args: { search: $search }
) {
id
job {
ro_number
@@ -107,8 +113,9 @@ export const QUERY_EXPORT_LOG_PAGINATED = gql`
successful
message
created_at
useremail
}
exportlog_aggregate {
search_exportlog_aggregate(args: { search: $search }) {
aggregate {
count(distinct: true)
}

View File

@@ -860,6 +860,8 @@ export const UPDATE_JOBS = gql`
update_jobs(where: { id: { _in: $jobIds } }, _set: $fields) {
returning {
id
date_exported
status
}
}
}

View File

@@ -1,6 +1,6 @@
import { SyncOutlined } from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { Button, Card, Input, Space, Table, Typography } from "antd";
import { Button, Card, Checkbox, Input, Space, Table, Typography } from "antd";
import _ from "lodash";
import queryString from "query-string";
import React from "react";
@@ -9,18 +9,16 @@ import { connect } from "react-redux";
import { Link, useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import AlertComponent from "../../components/alert/alert.component";
import JobRemoveFromPartsQueue from "../../components/job-remove-from-parst-queue/job-remove-from-parts-queue.component";
import { QUERY_EXPORT_LOG_PAGINATED } from "../../graphql/accounting.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { TimeAgoFormatter } from "../../utils/DateFormatter";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
export function PartsQueuePageComponent({ bodyshop }) {
export function ExportLogsPageComponent({ bodyshop }) {
const searchParams = queryString.parse(useLocation().search);
const { page, sortcolumn, sortorder, search } = searchParams;
const history = useHistory();
@@ -29,10 +27,10 @@ export function PartsQueuePageComponent({ bodyshop }) {
QUERY_EXPORT_LOG_PAGINATED,
{
variables: {
//search: search || "",
search: search || "",
offset: page ? (page - 1) * 25 : 0,
limit: 25,
order: sortcolumn && [
order: [
{
[sortcolumn || "created_at"]: sortorder
? sortorder === "descend"
@@ -64,6 +62,19 @@ export function PartsQueuePageComponent({ bodyshop }) {
};
const columns = [
{
title: t("general.labels.created_at"),
dataIndex: "created_at",
key: "created_at",
render: (text, record) => (
<DateTimeFormatter>{record.created_at}</DateTimeFormatter>
),
},
{
title: t("employees.fields.user_email"),
dataIndex: "useremail",
key: "useremail",
},
{
title: t("jobs.fields.ro_number"),
dataIndex: "ro_number",
@@ -71,105 +82,61 @@ export function PartsQueuePageComponent({ bodyshop }) {
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
sortOrder: sortcolumn === "ro_number" && sortorder,
render: (text, record) => (
<Link to={"/manage/jobs/" + record.id}>
render: (text, record) =>
record.job && (
<Link to={"/manage/jobs/" + record.job && record.job.id}>
{(record.job && record.job.ro_number) || t("general.labels.na")}
</Link>
),
},
{
title: t("jobs.fields.owner"),
dataIndex: "owner",
key: "owner",
// sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
// sortOrder: sortcolumn === "owner" && sortorder,
render: (text, record) => {
return record.owner ? (
<Link to={"/manage/owners/" + record.owner.id}>
{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
record.ownr_co_nm || ""
}`}
title: t("bills.fields.invoice_number"),
dataIndex: "invoice_number",
key: "invoice_number",
render: (text, record) =>
record.bill && (
<Link to={"/manage/bills?billid=" + (record.bill && record.bill.id)}>
{record.bill && record.bill.invoice_number}
</Link>
) : (
<span>{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
record.ownr_co_nm || ""
}`}</span>
);
},
},
{
title: t("jobs.fields.vehicle"),
dataIndex: "vehicle",
key: "vehicle",
ellipsis: true,
render: (text, record) => {
return record.vehicleid ? (
<Link to={"/manage/vehicles/" + record.vehicleid}>
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
}`}
</Link>
) : (
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
}`}</span>
);
},
},
{
title: t("vehicles.fields.plate_no"),
dataIndex: "plate_no",
key: "plate_no",
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
sortOrder: sortcolumn === "plate_no" && sortorder,
render: (text, record) => {
return record.plate_no ? record.plate_no : "";
},
},
{
title: t("jobs.fields.clm_no"),
dataIndex: "clm_no",
key: "clm_no",
ellipsis: true,
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
sortOrder: sortcolumn === "clm_no" && sortorder,
render: (text, record) => {
return record.clm_no ? (
<span>{record.clm_no}</span>
) : (
t("general.labels.unknown")
);
},
},
{
title: t("jobs.fields.clm_total"),
dataIndex: "clm_total",
key: "clm_total",
sorter: (a, b) => a.clm_total - b.clm_total,
sortOrder: sortcolumn === "clm_total" && sortorder,
render: (text, record) => {
return record.clm_total ? (
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
) : (
t("general.labels.unknown")
);
},
},
{
title: t("jobs.fields.updated_at"),
dataIndex: "updated_at",
key: "updated_at",
render: (text, record) => (
<TimeAgoFormatter>{record.updated_at}</TimeAgoFormatter>
),
},
{
title: t("general.labels.actions"),
dataIndex: "actions",
key: "actions",
title: t("payments.fields.paymentnum"),
dataIndex: "paymentnum",
key: "paymentnum",
render: (text, record) =>
record.payment && (
<Link
to={
"/manage/payments?search=" +
(record.payment && record.payment.paymentnum)
}
>
{record.payment && record.payment.paymentnum}
</Link>
),
},
{
title: t("general.labels.successful"),
dataIndex: "successful",
key: "successful",
render: (text, record) => (
<JobRemoveFromPartsQueue jobId={record.id} refetch={refetch} />
<Checkbox disabled checked={record.successful} />
),
},
{
title: t("general.labels.message"),
dataIndex: "message",
key: "message",
render: (text, record) =>
record.message && (
<div>
<ul>
{JSON.parse(record.message).map((m, idx) => (
<li key={idx}>{m}</li>
))}
</ul>
</div>
),
},
];
@@ -214,11 +181,11 @@ export function PartsQueuePageComponent({ bodyshop }) {
position: "top",
pageSize: 25,
current: parseInt(page || 1),
total: data && data.exportlog_aggregate.aggregate.count,
total: data && data.search_exportlog_aggregate.aggregate.count,
}}
columns={columns}
rowKey="id"
dataSource={data && data.exportlog}
dataSource={data && data.search_exportlog}
style={{ height: "100%" }}
scroll={{ x: true }}
onChange={handleTableChange}
@@ -227,4 +194,4 @@ export function PartsQueuePageComponent({ bodyshop }) {
);
}
export default connect(mapStateToProps, null)(PartsQueuePageComponent);
export default connect(mapStateToProps, null)(ExportLogsPageComponent);

View File

@@ -35,7 +35,7 @@ export function AllJobs({ setBreadcrumbs, setSelectedHeader }) {
offset: page ? (page - 1) * 25 : 0,
limit: 25,
...(statusFilters ? { statusList: JSON.parse(statusFilters) } : {}),
order: sortcolumn && [
order: [
{
[sortcolumn || "created_at"]: sortorder
? sortorder === "descend"

View File

@@ -33,7 +33,7 @@ export function PartsQueuePageComponent({ bodyshop }) {
limit: 25,
statuses: (statusFilters && JSON.parse(statusFilters)) ||
bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
order: sortcolumn && [
order: [
{
[sortcolumn || "updated_at"]: sortorder
? sortorder === "descend"

View File

@@ -790,6 +790,7 @@
"cancel": "Are you sure you want to cancel? Your changes will not be saved.",
"clear": "Clear",
"confirmpassword": "Confirm Password",
"created_at": "Created At",
"email": "Email",
"errors": "Errors",
"exceptiontitle": "An error has occurred.",
@@ -803,6 +804,7 @@
"loadingapp": "Loading $t(titles.app)",
"loadingshop": "Loading shop data...",
"loggingin": "Authorizing...",
"message": "Message",
"monday": "Monday",
"na": "N/A",
"no": "No",
@@ -826,6 +828,7 @@
"sub_status": {
"expired": "The subscription for this shop has expired. Please contact technical support to reactivate the subscription. "
},
"successful": "Successful",
"sunday": "Sunday",
"text": "Text",
"thursday": "Thursday",
@@ -1363,6 +1366,7 @@
"enterpayment": "Enter Payments",
"entertimeticket": "Enter Time Tickets",
"export": "Export",
"export-logs": "Export Logs",
"home": "Home",
"jobs": "Jobs",
"owners": "Owners",
@@ -1900,6 +1904,7 @@
"courtesycars": "Courtesy Cars",
"courtesycars-detail": "Courtesy Car {{number}}",
"courtesycars-new": "New Courtesy Car",
"export-logs": "Export Logs",
"jobs": "Jobs",
"jobs-active": "Active Jobs",
"jobs-admin": "Admin",
@@ -1935,6 +1940,7 @@
"courtesycars": "Courtesy Cars | $t(titles.app)",
"courtesycars-create": "New Courtesy Car | $t(titles.app)",
"courtesycars-detail": "Courtesy Car {{id}} | $t(titles.app)",
"export-logs": "Export Logs | $t(titles.app)",
"jobs": "Active Jobs | $t(titles.app)",
"jobs-admin": "Job {{ro_number}} - Admin | $t(titles.app)",
"jobs-all": "All Jobs | $t(titles.app)",

View File

@@ -790,6 +790,7 @@
"cancel": "",
"clear": "",
"confirmpassword": "",
"created_at": "",
"email": "",
"errors": "",
"exceptiontitle": "",
@@ -803,6 +804,7 @@
"loadingapp": "Cargando $t(titles.app)",
"loadingshop": "Cargando datos de la tienda ...",
"loggingin": "Iniciando sesión ...",
"message": "",
"monday": "",
"na": "N / A",
"no": "",
@@ -826,6 +828,7 @@
"sub_status": {
"expired": ""
},
"successful": "",
"sunday": "",
"text": "",
"thursday": "",
@@ -1363,6 +1366,7 @@
"enterpayment": "",
"entertimeticket": "",
"export": "",
"export-logs": "",
"home": "Casa",
"jobs": "Trabajos",
"owners": "propietarios",
@@ -1900,6 +1904,7 @@
"courtesycars": "",
"courtesycars-detail": "",
"courtesycars-new": "",
"export-logs": "",
"jobs": "",
"jobs-active": "",
"jobs-admin": "",
@@ -1935,6 +1940,7 @@
"courtesycars": "",
"courtesycars-create": "",
"courtesycars-detail": "",
"export-logs": "",
"jobs": "Todos los trabajos | $t(titles.app)",
"jobs-admin": "",
"jobs-all": "",

View File

@@ -790,6 +790,7 @@
"cancel": "",
"clear": "",
"confirmpassword": "",
"created_at": "",
"email": "",
"errors": "",
"exceptiontitle": "",
@@ -803,6 +804,7 @@
"loadingapp": "Chargement de $t(titles.app)",
"loadingshop": "Chargement des données de la boutique ...",
"loggingin": "Vous connecter ...",
"message": "",
"monday": "",
"na": "N / A",
"no": "",
@@ -826,6 +828,7 @@
"sub_status": {
"expired": ""
},
"successful": "",
"sunday": "",
"text": "",
"thursday": "",
@@ -1363,6 +1366,7 @@
"enterpayment": "",
"entertimeticket": "",
"export": "",
"export-logs": "",
"home": "Accueil",
"jobs": "Emplois",
"owners": "Propriétaires",
@@ -1900,6 +1904,7 @@
"courtesycars": "",
"courtesycars-detail": "",
"courtesycars-new": "",
"export-logs": "",
"jobs": "",
"jobs-active": "",
"jobs-admin": "",
@@ -1935,6 +1940,7 @@
"courtesycars": "",
"courtesycars-create": "",
"courtesycars-detail": "",
"export-logs": "",
"jobs": "Tous les emplois | $t(titles.app)",
"jobs-admin": "",
"jobs-all": "",

View File

@@ -7,15 +7,15 @@ exports.processSignUp = functions.auth.user().onCreate((user) => {
// Check if user meets role criteria:
// Your custom logic here: to decide what roles and other `x-hasura-*` should the user get
let customClaims;
// if (user.email && user.email.indexOf("@thinkimex.com") !== -1) {
// customClaims = {
// "https://hasura.io/jwt/claims": {
// "x-hasura-default-role": "admin",
// "x-hasura-allowed-roles": ["user", "admin"],
// "x-hasura-user-id": user.uid,
// },
// };
// } else {
if (user.email && user.email.indexOf("@admin.imex.online") !== -1) {
customClaims = {
"https://hasura.io/jwt/claims": {
"x-hasura-default-role": "admin",
"x-hasura-allowed-roles": ["user", "admin"],
"x-hasura-user-id": user.uid,
},
};
} else {
customClaims = {
"https://hasura.io/jwt/claims": {
"x-hasura-default-role": "user",
@@ -23,24 +23,13 @@ exports.processSignUp = functions.auth.user().onCreate((user) => {
"x-hasura-user-id": user.uid,
},
};
//}
//Removed for now - outbound connections are not free on firebase.
// fetch(GRAPHQL_ENDPOINT, {
// method: "POST",
// headers: {
// "Content-Type": "application/json",
// Accept: "application/json",
// "x-hasura-admin-secret": HASURA_SECRET_ADMIN_KEY
// },
// body: JSON.stringify({
// query: UPSERT_USER,
// variables: { authEmail: user.email, authToken: user.uid }
// })
// });
}
// Set custom user claims on this newly created user.
return admin.auth().setCustomUserClaims(user.uid, customClaims);
//exports.test = functions.auth.
// .then(() => {
// // Update real-time database to notify client to force refresh.
// const metadataRef = admin.database().ref("metadata/" + user.uid);

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."exportlog" ALTER COLUMN "message" SET NOT NULL;
type: run_sql

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."exportlog" ALTER COLUMN "message" DROP NOT NULL;
type: run_sql

View File

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

View File

@@ -0,0 +1,17 @@
- args:
cascade: false
read_only: false
sql: "CREATE\r\nOR REPLACE FUNCTION public.search_exportlog(search text) RETURNS
SETOF exportlog LANGUAGE plpgsql STABLE AS $function$ BEGIN IF search = '' THEN
RETURN query\r\nSELECT\r\n *\r\nFROM\r\n exportlog e;\r\n ELSE RETURN query\r\nSELECT\r\n
\ exportlog.*\r\nFROM\r\n exportlog e,\r\n jobs j,\r\n payments p,\r\n bills
b,\r\n users u\r\nWHERE\r\n (\r\n j.ro_number ILIKE '%' || search || '%'\r\n
\ OR b.invoice_number ILIKE '%' || search || '%'\r\n OR p.paymentnum ILIKE
'%' || search || '%'\r\n OR u.email ILIKE '%' || search || '%'\r\n\r\n )\r\n
\ AND (e.jobid = j.id\r\n or e.paymentid = p.id\r\n or e.billid = b.id\r\nor
e.useremail = u.email)\r\n;\r\nEND IF;\r\nEND $function$;"
type: run_sql
- args:
name: search_exportlog
schema: public
type: track_function

View File

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

View File

@@ -0,0 +1,18 @@
- args:
cascade: false
read_only: false
sql: "drop function public.search_exportlog;\r\n\r\nCREATE\r\nOR REPLACE FUNCTION
public.search_exportlog(search text) RETURNS SETOF exportlog LANGUAGE plpgsql
STABLE AS $function$ BEGIN IF search = '' THEN RETURN query\r\nSELECT\r\n *\r\nFROM\r\n
\ exportlog e;\r\n ELSE RETURN query\r\nSELECT\r\n exportlog.*\r\nFROM\r\n
\ exportlog e,\r\n jobs j,\r\n payments p,\r\n bills b,\r\n users u\r\nWHERE\r\n
\ (\r\n j.ro_number ILIKE '%' || search || '%'\r\n OR b.invoice_number
ILIKE '%' || search || '%'\r\n OR p.paymentnum ILIKE '%' || search || '%'\r\n
\ OR u.email ILIKE '%' || search || '%'\r\n\r\n )\r\n AND (e.jobid = j.id\r\n
\ or e.paymentid = p.id\r\n or e.billid = b.id\r\nor u.useremail = u.email)\r\n;\r\nEND
IF;\r\nEND $function$;"
type: run_sql
- args:
name: search_exportlog
schema: public
type: track_function

View File

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

View File

@@ -0,0 +1,18 @@
- args:
cascade: false
read_only: false
sql: "drop function public.search_exportlog;\r\n\r\nCREATE\r\nOR REPLACE FUNCTION
public.search_exportlog(search text) RETURNS SETOF exportlog LANGUAGE plpgsql
STABLE AS $function$ BEGIN IF search = '' THEN RETURN query\r\nSELECT\r\n *\r\nFROM\r\n
\ exportlog e;\r\n ELSE RETURN query\r\nSELECT\r\n e.*\r\nFROM\r\n exportlog
e,\r\n jobs j,\r\n payments p,\r\n bills b,\r\n users u\r\nWHERE\r\n (\r\n
\ j.ro_number ILIKE '%' || search || '%'\r\n OR b.invoice_number ILIKE
'%' || search || '%'\r\n OR p.paymentnum ILIKE '%' || search || '%'\r\n OR
u.email ILIKE '%' || search || '%'\r\n\r\n )\r\n AND (e.jobid = j.id\r\n or
e.paymentid = p.id\r\n or e.billid = b.id\r\nor u.useremail = u.email)\r\n;\r\nEND
IF;\r\nEND $function$;"
type: run_sql
- args:
name: search_exportlog
schema: public
type: track_function

View File

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

View File

@@ -0,0 +1,17 @@
- args:
cascade: false
read_only: false
sql: "drop function public.search_exportlog;\r\n\r\nCREATE\r\nOR REPLACE FUNCTION
public.search_exportlog(search text) RETURNS SETOF exportlog LANGUAGE plpgsql
STABLE AS $function$ BEGIN IF search = '' THEN RETURN query\r\nSELECT\r\n *\r\nFROM\r\n
\ exportlog e;\r\n ELSE RETURN query\r\nSELECT\r\n e.*\r\nFROM\r\n exportlog
e,\r\n jobs j,\r\n payments p,\r\n bills b\r\nWHERE\r\n (\r\n j.ro_number
ILIKE '%' || search || '%'\r\n OR b.invoice_number ILIKE '%' || search ||
'%'\r\n OR p.paymentnum ILIKE '%' || search || '%'\r\n OR e.useremail
ILIKE '%' || search || '%'\r\n\r\n )\r\n AND (e.jobid = j.id\r\n or e.paymentid
= p.id\r\n or e.billid = b.id)\r\n;\r\nEND IF;\r\nEND $function$;"
type: run_sql
- args:
name: search_exportlog
schema: public
type: track_function

View File

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

View File

@@ -0,0 +1,17 @@
- args:
cascade: false
read_only: false
sql: "drop function public.search_exportlog;\r\n\r\nCREATE\r\nOR REPLACE FUNCTION
public.search_exportlog(search text) RETURNS SETOF exportlog LANGUAGE plpgsql
STABLE AS $function$ BEGIN IF search = '' THEN RETURN query\r\nSELECT\r\n *\r\nFROM\r\n
\ exportlog e;\r\n ELSE RETURN query\r\nSELECT\r\n e.*\r\nFROM\r\n exportlog
e,\r\n jobs j,\r\n payments p,\r\n bills b\r\nWHERE\r\n(e.jobid = j.id and
\ j.ro_number ILIKE '%' || search || '%') OR\r\n(e.paymentid = p.id and p.paymentnum
ILIKE '%' || search || '%') OR\r\n(e.billid = b.id and b.invoice_number ILIKE
'%' || search || '%') OR\r\n e.useremail ILIKE '%' || search || '%'\r\n\r\n
\r\n \r\n;\r\nEND IF;\r\nEND $function$;"
type: run_sql
- args:
name: search_exportlog
schema: public
type: track_function

View File

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

View File

@@ -0,0 +1,18 @@
- args:
cascade: false
read_only: false
sql: "drop function public.search_exportlog;\r\n\r\nCREATE\r\nOR REPLACE FUNCTION
public.search_exportlog(search text) RETURNS SETOF exportlog LANGUAGE plpgsql
STABLE AS $function$ BEGIN IF search = '' THEN RETURN query\r\nSELECT\r\n *\r\nFROM\r\n
\ exportlog e;\r\n ELSE RETURN query\r\nSELECT\r\n e.*\r\nFROM\r\n exportlog
e\r\nINNER JOIN payments p \r\n ON p.id = e.paymentid\r\nINNER JOIN bills
b\r\n ON e.billid = b.id\r\n \r\nWHERE\r\n\r\n\r\n\r\n (\r\n j.ro_number
ILIKE '%' || search || '%'\r\n OR b.invoice_number ILIKE '%' || search ||
'%'\r\n OR p.paymentnum ILIKE '%' || search || '%'\r\n OR e.useremail
ILIKE '%' || search || '%'\r\n\r\n )\r\n AND (e.jobid = j.id\r\n or e.paymentid
= p.id\r\n \r\n or e.billid = b.id)\r\n \r\n \r\n;\r\nEND IF;\r\nEND $function$;"
type: run_sql
- args:
name: search_exportlog
schema: public
type: track_function

View File

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

View File

@@ -0,0 +1,19 @@
- args:
cascade: false
read_only: false
sql: "drop function public.search_exportlog;\r\n\r\nCREATE\r\nOR REPLACE FUNCTION
public.search_exportlog(search text) RETURNS SETOF exportlog LANGUAGE plpgsql
STABLE AS $function$ BEGIN IF search = '' THEN RETURN query\r\nSELECT\r\n *\r\nFROM\r\n
\ exportlog e;\r\n ELSE RETURN query\r\nSELECT\r\n e.*\r\nFROM\r\n exportlog
e\r\n INNER JOIN jobs j on j.id = e.jobid\r\nINNER JOIN payments p \r\n ON
p.id = e.paymentid\r\nINNER JOIN bills b\r\n ON e.billid = b.id\r\n \r\nWHERE\r\n\r\n\r\n\r\n
\ (\r\n j.ro_number ILIKE '%' || search || '%'\r\n OR b.invoice_number
ILIKE '%' || search || '%'\r\n OR p.paymentnum ILIKE '%' || search || '%'\r\n
\ OR e.useremail ILIKE '%' || search || '%'\r\n\r\n )\r\n AND (e.jobid =
j.id\r\n or e.paymentid = p.id\r\n \r\n or e.billid = b.id)\r\n \r\n \r\n;\r\nEND
IF;\r\nEND $function$;"
type: run_sql
- args:
name: search_exportlog
schema: public
type: track_function

View File

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

View File

@@ -0,0 +1,19 @@
- args:
cascade: false
read_only: false
sql: "drop function public.search_exportlog;\r\n\r\nCREATE\r\nOR REPLACE FUNCTION
public.search_exportlog(search text) RETURNS SETOF exportlog LANGUAGE plpgsql
STABLE AS $function$ BEGIN IF search = '' THEN RETURN query\r\nSELECT\r\n *\r\nFROM\r\n
\ exportlog e;\r\n ELSE RETURN query\r\nSELECT\r\n e.*\r\nFROM\r\n exportlog
e\r\n LEFT JOIN jobs j on j.id = e.jobid\r\nLEFT JOIN payments p \r\n ON
p.id = e.paymentid\r\nLEFT JOIN bills b\r\n ON e.billid = b.id\r\n \r\nWHERE\r\n\r\n\r\n\r\n
\ (\r\n j.ro_number ILIKE '%' || search || '%'\r\n OR b.invoice_number
ILIKE '%' || search || '%'\r\n OR p.paymentnum ILIKE '%' || search || '%'\r\n
\ OR e.useremail ILIKE '%' || search || '%'\r\n\r\n )\r\n AND (e.jobid =
j.id\r\n or e.paymentid = p.id\r\n \r\n or e.billid = b.id)\r\n \r\n \r\n;\r\nEND
IF;\r\nEND $function$;"
type: run_sql
- args:
name: search_exportlog
schema: public
type: track_function

View File

@@ -4334,6 +4334,9 @@ functions:
- function:
schema: public
name: search_cccontracts
- function:
schema: public
name: search_exportlog
- function:
schema: public
name: search_jobs

View File

@@ -201,7 +201,7 @@ const generateInvoiceQbxml = (
if (!account) {
throw new Error(
`A matching account does not exist for the allocation. Center: ${center}`
`A matching account does not exist for the allocation. Center: ${jobline.profitcenter_part}`
);
}