Added payment export BOD-147
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<babeledit_project be_version="2.6.1" version="1.2">
|
||||
<babeledit_project version="1.2" be_version="2.6.1">
|
||||
<!--
|
||||
|
||||
BabelEdit project file
|
||||
@@ -12810,6 +12810,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>accounting-payments</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>accounting-receivables</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -12873,6 +12894,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>allpayments</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>availablejobs</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -13062,6 +13104,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>export</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>
|
||||
@@ -13146,27 +13209,6 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>payments</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>productionboard</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
import { Input, Table } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import PaymentExportButton from "../payment-export-button/payment-export-button.component";
|
||||
import { PaymentsExportAllButton } from "../payments-export-all-button/payments-export-all-button.component";
|
||||
|
||||
export default function AccountingPayablesTableComponent({
|
||||
loading,
|
||||
payments,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [selectedPayments, setSelectedPayments] = useState([]);
|
||||
const [transInProgress, setTransInProgress] = useState(false);
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
search: "",
|
||||
});
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "ro_number",
|
||||
key: "ro_number",
|
||||
sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<Link to={"/manage/jobs/" + record.job.id}>{record.job.ro_number}</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.est_number"),
|
||||
dataIndex: "est_number",
|
||||
key: "est_number",
|
||||
sorter: (a, b) => a.job.est_number - b.job.est_number,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "est_number" && state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<Link to={"/manage/jobs/" + record.job.id}>
|
||||
{record.job.est_number}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.owner"),
|
||||
dataIndex: "owner",
|
||||
key: "owner",
|
||||
ellipsis: true,
|
||||
sorter: (a, b) => alphaSort(a.job.ownr_ln, b.job.ownr_ln),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "ownr_ln" && state.sortedInfo.order,
|
||||
render: (text, record) => {
|
||||
return record.job.owner ? (
|
||||
<Link to={"/manage/owners/" + record.job.owner.id}>
|
||||
{`${record.job.ownr_fn || ""} ${record.job.ownr_ln || ""}`}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.job.ownr_fn || ""} ${
|
||||
record.job.ownr_ln || ""
|
||||
}`}</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("payments.fields.amount"),
|
||||
dataIndex: "amount",
|
||||
key: "amount",
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatter>{record.amount}</CurrencyFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("payments.fields.memo"),
|
||||
dataIndex: "memo",
|
||||
key: "memo",
|
||||
},
|
||||
{
|
||||
title: t("payments.fields.transactionid"),
|
||||
dataIndex: "transactionid",
|
||||
key: "transactionid",
|
||||
},
|
||||
{
|
||||
title: t("payments.fields.stripeid"),
|
||||
dataIndex: "stripeid",
|
||||
key: "stripeid",
|
||||
},
|
||||
{
|
||||
title: t("payments.fields.created_at"),
|
||||
dataIndex: "created_at",
|
||||
key: "created_at",
|
||||
render: (text, record) => (
|
||||
<DateTimeFormatter>{record.created_at}</DateTimeFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("payments.fields.exportedat"),
|
||||
dataIndex: "exportedat",
|
||||
key: "exportedat",
|
||||
render: (text, record) => (
|
||||
<DateTimeFormatter>{record.exportedat}</DateTimeFormatter>
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
title: t("general.labels.actions"),
|
||||
dataIndex: "actions",
|
||||
key: "actions",
|
||||
sorter: (a, b) => a.clm_total - b.clm_total,
|
||||
|
||||
render: (text, record) => (
|
||||
<div>
|
||||
<PaymentExportButton
|
||||
paymentId={record.id}
|
||||
disabled={transInProgress || !!record.exportedat}
|
||||
loadingCallback={setTransInProgress}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const handleSearch = (e) => {
|
||||
setState({ ...state, search: e.target.value });
|
||||
};
|
||||
|
||||
const dataSource = state.search
|
||||
? payments.filter(
|
||||
(v) =>
|
||||
(v.vendor.name || "")
|
||||
.toLowerCase()
|
||||
.includes(state.search.toLowerCase()) ||
|
||||
(v.invoice_number || "")
|
||||
.toLowerCase()
|
||||
.includes(state.search.toLowerCase())
|
||||
)
|
||||
: payments;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Table
|
||||
loading={loading}
|
||||
title={() => {
|
||||
return (
|
||||
<div>
|
||||
<Input
|
||||
value={state.search}
|
||||
onChange={handleSearch}
|
||||
placeholder={t("general.labels.search")}
|
||||
allowClear
|
||||
/>
|
||||
<PaymentsExportAllButton
|
||||
paymentIds={selectedPayments}
|
||||
disabled={transInProgress}
|
||||
loadingCallback={setTransInProgress}
|
||||
completedCallback={setSelectedPayments}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
dataSource={dataSource}
|
||||
size="small"
|
||||
pagination={{ position: "top", pageSize: 50 }}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
onChange={handleTableChange}
|
||||
rowSelection={{
|
||||
onSelectAll: (selected, selectedRows) =>
|
||||
setSelectedPayments(selectedRows.map((i) => i.id)),
|
||||
onSelect: (record, selected, selectedRows, nativeEvent) => {
|
||||
setSelectedPayments(selectedRows.map((i) => i.id));
|
||||
},
|
||||
getCheckboxProps: (record) => ({
|
||||
disabled: record.exported,
|
||||
}),
|
||||
selectedRowKeys: selectedPayments,
|
||||
type: "checkbox",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -232,19 +232,30 @@ function Header({
|
||||
>
|
||||
{t("menus.header.entertimeticket")}
|
||||
</Menu.Item>
|
||||
<Menu.Item key="receivables">
|
||||
<Link to="/manage/accounting/receivables">
|
||||
{t("menus.header.accounting-receivables")}
|
||||
|
||||
<Menu.SubMenu title={t("menus.header.export")}>
|
||||
<Menu.Item key="receivables">
|
||||
<Link to="/manage/accounting/receivables">
|
||||
{t("menus.header.accounting-receivables")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="payables">
|
||||
<Link to="/manage/accounting/payables">
|
||||
{t("menus.header.accounting-payables")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="payments">
|
||||
<Link to="/manage/accounting/payments">
|
||||
{t("menus.header.accounting-payments")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
|
||||
<Menu.Item key="allpayments">
|
||||
<Link to="/manage/payments">
|
||||
{t("menus.header.allpayments")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="payables">
|
||||
<Link to="/manage/accounting/payables">
|
||||
{t("menus.header.accounting-payables")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="payments">
|
||||
<Link to="/manage/payments">{t("menus.header.payments")}</Link>
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
<Menu.SubMenu title={t("menus.header.shop")}>
|
||||
<Menu.Item key="shop">
|
||||
|
||||
@@ -44,8 +44,8 @@ export function JobsCloseExportButton({ bodyshop, jobId, disabled }) {
|
||||
let PartnerResponse;
|
||||
try {
|
||||
PartnerResponse = await axios.post(
|
||||
// "http://localhost:1337/qb/",
|
||||
"http://b47e67f9cbe3.ngrok.io/qb/",
|
||||
"http://localhost:1337/qb/",
|
||||
// "http://609feaeae986.ngrok.io/qb/",
|
||||
QbXmlResponse.data,
|
||||
{
|
||||
headers: {
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
import { useMutation } from "@apollo/react-hooks";
|
||||
import { Button, notification } from "antd";
|
||||
import axios from "axios";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { auth } from "../../firebase/firebase.utils";
|
||||
import { UPDATE_PAYMENTS } from "../../graphql/payments.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export function PaymentExportButton({
|
||||
bodyshop,
|
||||
paymentId,
|
||||
disabled,
|
||||
loadingCallback,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [updatePayment] = useMutation(UPDATE_PAYMENTS);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleQbxml = async () => {
|
||||
setLoading(true);
|
||||
if (!!loadingCallback) loadingCallback(true);
|
||||
|
||||
let QbXmlResponse;
|
||||
try {
|
||||
QbXmlResponse = await axios.post(
|
||||
"/accounting/qbxml/payments",
|
||||
{ payments: [paymentId] },
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log("handle -> XML", QbXmlResponse);
|
||||
} catch (error) {
|
||||
console.log("Error getting QBXML from Server.", error);
|
||||
notification["error"]({
|
||||
message: t("payments.errors.exporting", {
|
||||
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
||||
}),
|
||||
});
|
||||
if (loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let PartnerResponse;
|
||||
|
||||
try {
|
||||
PartnerResponse = await axios.post(
|
||||
"http://localhost:1337/qb/",
|
||||
//"http://609feaeae986.ngrok.io/qb/",
|
||||
QbXmlResponse.data
|
||||
);
|
||||
} catch (error) {
|
||||
console.log("Error connecting to quickbooks or partner.", error);
|
||||
notification["error"]({
|
||||
message: t("payments.errors.exporting-partner"),
|
||||
});
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("handleQbxml -> PartnerResponse", PartnerResponse);
|
||||
const failedTransactions = PartnerResponse.data.filter((r) => !r.success);
|
||||
const successfulTransactions = PartnerResponse.data.filter(
|
||||
(r) => r.success
|
||||
);
|
||||
if (failedTransactions.length > 0) {
|
||||
//Uh oh. At least one was no good.
|
||||
failedTransactions.map((ft) =>
|
||||
notification["error"]({
|
||||
message: t("payments.errors.exporting", {
|
||||
error: ft.errorMessage || "",
|
||||
}),
|
||||
})
|
||||
);
|
||||
}
|
||||
if (successfulTransactions.length > 0) {
|
||||
const paymentUpdateResponse = await updatePayment({
|
||||
variables: {
|
||||
paymentIdList: successfulTransactions.map((st) => st.id),
|
||||
payment: {
|
||||
exportedat: new Date(),
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!!!paymentUpdateResponse.errors) {
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.exported"),
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.exporting", {
|
||||
error: JSON.stringify(paymentUpdateResponse.error),
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={handleQbxml}
|
||||
loading={loading}
|
||||
disabled={disabled}
|
||||
type="dashed"
|
||||
>
|
||||
{t("jobs.actions.export")}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null)(PaymentExportButton);
|
||||
@@ -0,0 +1,126 @@
|
||||
import { useMutation } from "@apollo/react-hooks";
|
||||
import { Button, notification } from "antd";
|
||||
import axios from "axios";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { auth } from "../../firebase/firebase.utils";
|
||||
import { UPDATE_PAYMENTS } from "../../graphql/payments.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export function PaymentsExportAllButton({
|
||||
bodyshop,
|
||||
paymentIds,
|
||||
disabled,
|
||||
loadingCallback,
|
||||
completedCallback,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [updatePayments] = useMutation(UPDATE_PAYMENTS);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleQbxml = async () => {
|
||||
setLoading(true);
|
||||
if (!!loadingCallback) loadingCallback(true);
|
||||
|
||||
let QbXmlResponse;
|
||||
try {
|
||||
QbXmlResponse = await axios.post(
|
||||
"/accounting/qbxml/payments",
|
||||
{ payments: paymentIds },
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log("handle -> XML", QbXmlResponse);
|
||||
} catch (error) {
|
||||
console.log("Error getting QBXML from Server.", error);
|
||||
notification["error"]({
|
||||
message: t("payments.errors.exporting", {
|
||||
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
||||
}),
|
||||
});
|
||||
if (loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let PartnerResponse;
|
||||
|
||||
try {
|
||||
PartnerResponse = await axios.post(
|
||||
"http://localhost:1337/qb/",
|
||||
QbXmlResponse.data
|
||||
);
|
||||
} catch (error) {
|
||||
console.log("Error connecting to quickbooks or partner.", error);
|
||||
notification["error"]({
|
||||
message: t("payments.errors.exporting-partner"),
|
||||
});
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("handleQbxml -> PartnerResponse", PartnerResponse);
|
||||
const failedTransactions = PartnerResponse.data.filter((r) => !r.success);
|
||||
const successfulTransactions = PartnerResponse.data.filter(
|
||||
(r) => r.success
|
||||
);
|
||||
if (failedTransactions.length > 0) {
|
||||
//Uh oh. At least one was no good.
|
||||
failedTransactions.map((ft) =>
|
||||
notification["error"]({
|
||||
message: t("payments.errors.exporting", {
|
||||
error: ft.errorMessage || "",
|
||||
}),
|
||||
})
|
||||
);
|
||||
}
|
||||
if (successfulTransactions.length > 0) {
|
||||
const paymentUpdateResponse = await updatePayments({
|
||||
variables: {
|
||||
paymentIdList: successfulTransactions.map((st) => st.id),
|
||||
payment: {
|
||||
//exported: true,
|
||||
exportedat: new Date(),
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!!!paymentUpdateResponse.errors) {
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.exported"),
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.exporting", {
|
||||
error: JSON.stringify(paymentUpdateResponse.error),
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!!completedCallback) completedCallback([]);
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={handleQbxml}
|
||||
loading={loading}
|
||||
disabled={disabled}
|
||||
type="dashed"
|
||||
>
|
||||
{t("jobs.actions.exportselected")}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null)(PaymentsExportAllButton);
|
||||
@@ -10,11 +10,8 @@ import { createStructuredSelector } from "reselect";
|
||||
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { onlyUnique } from "../../utils/arrayHelper";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import StartChatButton from "../chat-open-button/chat-open-button.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
|
||||
@@ -48,3 +48,29 @@ export const QUERY_INVOICES_FOR_EXPORT = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_PAYMENTS_FOR_EXPORT = gql`
|
||||
query QUERY_PAYMENTS_FOR_EXPORT {
|
||||
payments(
|
||||
where: { exportedat: { _eq: null } }
|
||||
order_by: { created_at: desc }
|
||||
) {
|
||||
id
|
||||
amount
|
||||
job {
|
||||
ro_number
|
||||
est_number
|
||||
id
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_co_nm
|
||||
}
|
||||
payer
|
||||
memo
|
||||
exportedat
|
||||
stripeid
|
||||
created_at
|
||||
transactionid
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -160,6 +160,7 @@ export const UPDATE_INVOICE = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const UPDATE_INVOICES = gql`
|
||||
mutation UPDATE_INVOICES(
|
||||
$invoiceIdList: [uuid!]!
|
||||
|
||||
@@ -48,3 +48,28 @@ export const QUERY_ALL_PAYMENTS_PAGINATED = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const UPDATE_PAYMENT = gql`
|
||||
mutation UPDATE_PAYMENT($paymentId: uuid!, $payment: payments_set_input!) {
|
||||
update_payments(where: { id: { _eq: $paymentId } }, _set: $payment) {
|
||||
returning {
|
||||
id
|
||||
exported_at
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const UPDATE_PAYMENTS = gql`
|
||||
mutation UPDATE_PAYMENTS(
|
||||
$paymentIdList: [uuid!]!
|
||||
$payment: payments_set_input!
|
||||
) {
|
||||
update_payments(where: { id: { _in: $paymentIdList } }, _set: $payment) {
|
||||
returning {
|
||||
id
|
||||
exportedat
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { useQuery } from "@apollo/react-hooks";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import AccountingPaymentsTable from "../../components/accounting-payments-table/accounting-payments-table.component";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import { QUERY_PAYMENTS_FOR_EXPORT } from "../../graphql/accounting.queries";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
});
|
||||
export function AccountingPaymentsContainer({ bodyshop, setBreadcrumbs }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.accounting-payments");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/accounting/payments",
|
||||
label: t("titles.bc.accounting-payments"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs]);
|
||||
|
||||
const { loading, error, data } = useQuery(QUERY_PAYMENTS_FOR_EXPORT);
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AccountingPaymentsTable
|
||||
loadaing={loading}
|
||||
payments={data ? data.payments : []}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(AccountingPaymentsContainer);
|
||||
@@ -104,6 +104,9 @@ const AccountingReceivables = lazy(() =>
|
||||
const AccountingPayables = lazy(() =>
|
||||
import("../accounting-payables/accounting-payables.container")
|
||||
);
|
||||
const AccountingPayments = lazy(() =>
|
||||
import("../accounting-payments/accounting-payments.container")
|
||||
);
|
||||
const AllJobs = lazy(() => import("../jobs-all/jobs-all.container"));
|
||||
const JobsClose = lazy(() => import("../jobs-close/jobs-close.container"));
|
||||
const ShopCsiPageContainer = lazy(() =>
|
||||
@@ -163,10 +166,8 @@ export function Manage({ match, conflict }) {
|
||||
<Elements stripe={stripePromise}>
|
||||
<PaymentModalContainer />
|
||||
</Elements>
|
||||
|
||||
<Route exact path={`${match.path}`} component={ManageRootPage} />
|
||||
<Route exact path={`${match.path}/jobs`} component={JobsPage} />
|
||||
|
||||
<Switch>
|
||||
<Route
|
||||
exact
|
||||
@@ -304,6 +305,11 @@ export function Manage({ match, conflict }) {
|
||||
path={`${match.path}/accounting/payables`}
|
||||
component={AccountingPayables}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/accounting/payments`}
|
||||
component={AccountingPayments}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/payments`}
|
||||
|
||||
@@ -1,21 +1,8 @@
|
||||
import { useQuery } from "@apollo/react-hooks";
|
||||
import queryString from "query-string";
|
||||
import React from "react";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import TechLookupJobsList from "../../components/tech-lookup-jobs-list/tech-lookup-jobs-list.component";
|
||||
import { SEARCH_FOR_JOBS } from "../../graphql/jobs.queries";
|
||||
import { Row, Col } from "antd";
|
||||
import TechLookupJobsDrawer from "../../components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component";
|
||||
import TechLookupJobsList from "../../components/tech-lookup-jobs-list/tech-lookup-jobs-list.component";
|
||||
|
||||
export default function TechLookupContainer() {
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const history = useHistory();
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(SEARCH_FOR_JOBS, {
|
||||
variables: { search: `%${search.ro_number}%` },
|
||||
skip: !!!search.ro_number,
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TechLookupJobsList />
|
||||
|
||||
@@ -780,9 +780,11 @@
|
||||
"header": {
|
||||
"accounting": "Accounting",
|
||||
"accounting-payables": "Payables",
|
||||
"accounting-payments": "Payments",
|
||||
"accounting-receivables": "Receivables",
|
||||
"activejobs": "Active Jobs",
|
||||
"alljobs": "All Jobs",
|
||||
"allpayments": "All Payments",
|
||||
"availablejobs": "Available Jobs",
|
||||
"courtesycars": "Courtesy Cars",
|
||||
"courtesycars-all": "All Courtesy Cars",
|
||||
@@ -792,11 +794,11 @@
|
||||
"enterinvoices": "Enter Invoices",
|
||||
"enterpayment": "Enter Payments",
|
||||
"entertimeticket": "Enter Time Tickets",
|
||||
"export": "Export",
|
||||
"home": "Home",
|
||||
"invoices": "Invoices",
|
||||
"jobs": "Jobs",
|
||||
"owners": "Owners",
|
||||
"payments": "All Payments",
|
||||
"productionboard": "Production Board",
|
||||
"productionlist": "Production - List",
|
||||
"schedule": "Schedule",
|
||||
|
||||
@@ -780,9 +780,11 @@
|
||||
"header": {
|
||||
"accounting": "",
|
||||
"accounting-payables": "",
|
||||
"accounting-payments": "",
|
||||
"accounting-receivables": "",
|
||||
"activejobs": "Empleos activos",
|
||||
"alljobs": "",
|
||||
"allpayments": "",
|
||||
"availablejobs": "Trabajos disponibles",
|
||||
"courtesycars": "",
|
||||
"courtesycars-all": "",
|
||||
@@ -792,11 +794,11 @@
|
||||
"enterinvoices": "",
|
||||
"enterpayment": "",
|
||||
"entertimeticket": "",
|
||||
"export": "",
|
||||
"home": "Casa",
|
||||
"invoices": "",
|
||||
"jobs": "Trabajos",
|
||||
"owners": "propietarios",
|
||||
"payments": "",
|
||||
"productionboard": "",
|
||||
"productionlist": "",
|
||||
"schedule": "Programar",
|
||||
|
||||
@@ -780,9 +780,11 @@
|
||||
"header": {
|
||||
"accounting": "",
|
||||
"accounting-payables": "",
|
||||
"accounting-payments": "",
|
||||
"accounting-receivables": "",
|
||||
"activejobs": "Emplois actifs",
|
||||
"alljobs": "",
|
||||
"allpayments": "",
|
||||
"availablejobs": "Emplois disponibles",
|
||||
"courtesycars": "",
|
||||
"courtesycars-all": "",
|
||||
@@ -792,11 +794,11 @@
|
||||
"enterinvoices": "",
|
||||
"enterpayment": "",
|
||||
"entertimeticket": "",
|
||||
"export": "",
|
||||
"home": "Accueil",
|
||||
"invoices": "",
|
||||
"jobs": "Emplois",
|
||||
"owners": "Propriétaires",
|
||||
"payments": "",
|
||||
"productionboard": "",
|
||||
"productionlist": "",
|
||||
"schedule": "Programme",
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: ALTER TABLE "public"."payments" DROP COLUMN "type";
|
||||
type: run_sql
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: ALTER TABLE "public"."payments" ADD COLUMN "type" text NULL;
|
||||
type: run_sql
|
||||
@@ -0,0 +1,35 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: payments
|
||||
schema: public
|
||||
type: drop_insert_permission
|
||||
- args:
|
||||
permission:
|
||||
check:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- amount
|
||||
- created_at
|
||||
- exportedat
|
||||
- id
|
||||
- jobid
|
||||
- memo
|
||||
- payer
|
||||
- stripeid
|
||||
- transactionid
|
||||
- updated_at
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: payments
|
||||
schema: public
|
||||
type: create_insert_permission
|
||||
@@ -0,0 +1,36 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: payments
|
||||
schema: public
|
||||
type: drop_insert_permission
|
||||
- args:
|
||||
permission:
|
||||
check:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- amount
|
||||
- created_at
|
||||
- exportedat
|
||||
- id
|
||||
- jobid
|
||||
- memo
|
||||
- payer
|
||||
- stripeid
|
||||
- transactionid
|
||||
- type
|
||||
- updated_at
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: payments
|
||||
schema: public
|
||||
type: create_insert_permission
|
||||
@@ -0,0 +1,35 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: payments
|
||||
schema: public
|
||||
type: drop_update_permission
|
||||
- args:
|
||||
permission:
|
||||
columns:
|
||||
- amount
|
||||
- created_at
|
||||
- exportedat
|
||||
- id
|
||||
- jobid
|
||||
- memo
|
||||
- payer
|
||||
- stripeid
|
||||
- transactionid
|
||||
- updated_at
|
||||
filter:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: payments
|
||||
schema: public
|
||||
type: create_update_permission
|
||||
@@ -0,0 +1,36 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: payments
|
||||
schema: public
|
||||
type: drop_update_permission
|
||||
- args:
|
||||
permission:
|
||||
columns:
|
||||
- amount
|
||||
- created_at
|
||||
- exportedat
|
||||
- id
|
||||
- jobid
|
||||
- memo
|
||||
- payer
|
||||
- stripeid
|
||||
- transactionid
|
||||
- type
|
||||
- updated_at
|
||||
filter:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: payments
|
||||
schema: public
|
||||
type: create_update_permission
|
||||
@@ -3243,6 +3243,7 @@ tables:
|
||||
- payer
|
||||
- stripeid
|
||||
- transactionid
|
||||
- type
|
||||
- updated_at
|
||||
select_permissions:
|
||||
- role: user
|
||||
@@ -3282,6 +3283,7 @@ tables:
|
||||
- payer
|
||||
- stripeid
|
||||
- transactionid
|
||||
- type
|
||||
- updated_at
|
||||
filter:
|
||||
job:
|
||||
|
||||
@@ -47,6 +47,7 @@ app.post("/accounting/iif/receivables", accountingIIF.receivables);
|
||||
const accountQbxml = require("./server/accounting/qbxml/qbxml");
|
||||
app.post("/accounting/qbxml/receivables", accountQbxml.receivables);
|
||||
app.post("/accounting/qbxml/payables", accountQbxml.payables);
|
||||
app.post("/accounting/qbxml/payments", accountQbxml.payments);
|
||||
|
||||
//Cloudinary Media Paths
|
||||
var media = require("./server/media/media");
|
||||
|
||||
106
server/accounting/qbxml/qbxml-payments.js
Normal file
106
server/accounting/qbxml/qbxml-payments.js
Normal file
@@ -0,0 +1,106 @@
|
||||
const GraphQLClient = require("graphql-request").GraphQLClient;
|
||||
const path = require("path");
|
||||
const DineroQbFormat = require("../accounting-constants").DineroQbFormat;
|
||||
const queries = require("../../graphql-client/queries");
|
||||
const Dinero = require("dinero.js");
|
||||
var builder = require("xmlbuilder");
|
||||
const moment = require("moment");
|
||||
const QbXmlUtils = require("./qbxml-utils");
|
||||
require("dotenv").config({
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
});
|
||||
|
||||
const { generateJobTier, generateOwnerTier, generateSourceTier } = QbXmlUtils;
|
||||
|
||||
exports.default = async (req, res) => {
|
||||
const BearerToken = req.headers.authorization;
|
||||
const { payments: paymentsToQuery } = req.body;
|
||||
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await client
|
||||
.setHeaders({ Authorization: BearerToken })
|
||||
.request(queries.QUERY_PAYMENTS_FOR_EXPORT, {
|
||||
payments: paymentsToQuery,
|
||||
});
|
||||
const { payments } = result;
|
||||
|
||||
const QbXmlToExecute = [];
|
||||
payments.map((i) => {
|
||||
QbXmlToExecute.push({
|
||||
id: i.id,
|
||||
okStatusCodes: ["0"],
|
||||
qbxml: generatePayment(i),
|
||||
});
|
||||
});
|
||||
|
||||
res.status(200).json(QbXmlToExecute);
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
res.status(400).send(JSON.stringify(error));
|
||||
}
|
||||
};
|
||||
|
||||
const generatePayment = (payment) => {
|
||||
console.log("generatePayment -> payment", payment);
|
||||
const paymentQbxmlObj = {
|
||||
QBXML: {
|
||||
QBXMLMsgsRq: {
|
||||
"@onError": "continueOnError",
|
||||
ReceivePaymentAddRq: {
|
||||
ReceivePaymentAdd: {
|
||||
CustomerRef: {
|
||||
FullName:
|
||||
payment.job.bodyshop.accountingconfig.tiers === 3
|
||||
? `${generateSourceTier(payment.job)}:${generateOwnerTier(
|
||||
payment.job
|
||||
)}:${generateJobTier(payment.job)}`
|
||||
: `${generateOwnerTier(payment.job)}:${generateJobTier(
|
||||
payment.job
|
||||
)}`,
|
||||
},
|
||||
ARAccountRef: {
|
||||
FullName:
|
||||
payment.job.bodyshop.md_responsibility_centers.ar.accountname,
|
||||
},
|
||||
TxnDate: moment(payment.created_at).format("YYYY-MM-DD"), //Trim String
|
||||
RefNumber: payment.stripeid || payment.transactionid,
|
||||
TotalAmount: Dinero({
|
||||
amount: Math.round(payment.amount * 100),
|
||||
}).toFormat(DineroQbFormat),
|
||||
Memo: `RO ${payment.job.ro_number || ""} OWNER ${
|
||||
payment.job.ownr_fn || ""
|
||||
} ${payment.job.ownr_ln || ""} ${payment.job.ownr_co_nm || ""} ${
|
||||
payment.stripeid
|
||||
}`,
|
||||
IsAutoApply: true,
|
||||
// AppliedToTxnAdd:{
|
||||
// T
|
||||
// }
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var paymentQbxmlPartial = builder
|
||||
.create(paymentQbxmlObj, {
|
||||
version: "1.30",
|
||||
encoding: "UTF-8",
|
||||
headless: true,
|
||||
})
|
||||
.end({ pretty: true });
|
||||
|
||||
const paymentQbxmlFull = QbXmlUtils.addQbxmlHeader(paymentQbxmlPartial);
|
||||
console.log("generateBill -> paymentQbxmlFull", paymentQbxmlFull);
|
||||
|
||||
return paymentQbxmlFull;
|
||||
};
|
||||
@@ -12,6 +12,8 @@ require("dotenv").config({
|
||||
),
|
||||
});
|
||||
|
||||
const { generateJobTier, generateOwnerTier, generateSourceTier } = QbXmlUtils;
|
||||
|
||||
exports.default = async (req, res) => {
|
||||
const BearerToken = req.headers.authorization;
|
||||
const { jobId } = req.body;
|
||||
@@ -72,224 +74,13 @@ exports.default = async (req, res) => {
|
||||
qbxml: generateInvoiceQbxml(jobs_by_pk, bodyshop),
|
||||
});
|
||||
|
||||
res.status(200).json([{ id: jobId, okStatusCodes: ["0"], qbxml: t }]);
|
||||
res.status(200).json(QbXmlToExecute);
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
res.status(400).send(JSON.stringify(error));
|
||||
}
|
||||
};
|
||||
|
||||
const t = `<?xml version="1.0" encoding="utf-8"?>
|
||||
<?qbxml version="13.0"?>
|
||||
<QBXML>
|
||||
<QBXMLMsgsRq onError="continueOnError">
|
||||
<CustomerAddRq>
|
||||
<CustomerAdd>
|
||||
<Name>Insurance Corporation of British Co</Name>
|
||||
<BillAddress>
|
||||
<Addr1>21291 122B AVE</Addr1>
|
||||
<City>MAPLE RIDGE</City>
|
||||
<State>BC</State>
|
||||
</BillAddress>
|
||||
</CustomerAdd>
|
||||
</CustomerAddRq>
|
||||
|
||||
<CustomerAddRq>
|
||||
<CustomerAdd>
|
||||
<Name>CLOVER LANDON #4</Name>
|
||||
<ParentRef>
|
||||
<FullName>Insurance Corporation of British Co</FullName>
|
||||
</ParentRef>
|
||||
</CustomerAdd>
|
||||
</CustomerAddRq>
|
||||
|
||||
<CustomerAddRq>
|
||||
<CustomerAdd>
|
||||
<Name>RO29</Name>
|
||||
<ParentRef>
|
||||
<FullName>Insurance Corporation of British Co:CLOVER LANDON #4</FullName>
|
||||
</ParentRef>
|
||||
</CustomerAdd>
|
||||
</CustomerAddRq>
|
||||
|
||||
<InvoiceAddRq>
|
||||
<InvoiceAdd>
|
||||
<CustomerRef>
|
||||
<FullName>Insurance Corporation of British Co:CLOVER LANDON #4:RO29</FullName>
|
||||
</CustomerRef>
|
||||
<TxnDate/>
|
||||
<RefNumber>RO29</RefNumber>
|
||||
<BillAddress>
|
||||
<Addr1>21291 122B AVE</Addr1>
|
||||
<City>MAPLE RIDGE</City>
|
||||
<State>BC</State>
|
||||
</BillAddress>
|
||||
<PONumber>BM27914-1-A</PONumber>
|
||||
<InvoiceLineAdd>
|
||||
<ItemRef>
|
||||
<FullName>BODY SHOP_PAA</FullName>
|
||||
</ItemRef>
|
||||
<Desc>Aftermarketd</Desc>
|
||||
<Quantity>1</Quantity>
|
||||
<Amount>1351.03</Amount>
|
||||
<SalesTaxCodeRef>
|
||||
<FullName>E</FullName>
|
||||
</SalesTaxCodeRef>
|
||||
</InvoiceLineAdd>
|
||||
<InvoiceLineAdd>
|
||||
<ItemRef>
|
||||
<FullName>BODY SHOP_PAN</FullName>
|
||||
</ItemRef>
|
||||
<Desc>BODY SHOP SALES:PARTS:OEM</Desc>
|
||||
<Quantity>1</Quantity>
|
||||
<Amount>292.45</Amount>
|
||||
<SalesTaxCodeRef>
|
||||
<FullName>E</FullName>
|
||||
</SalesTaxCodeRef>
|
||||
</InvoiceLineAdd>
|
||||
<InvoiceLineAdd>
|
||||
<ItemRef>
|
||||
<FullName>BODY SHOP_ATP</FullName>
|
||||
</ItemRef>
|
||||
<Desc>ATPd</Desc>
|
||||
<Quantity>1</Quantity>
|
||||
<Amount>144.09</Amount>
|
||||
<SalesTaxCodeRef>
|
||||
<FullName>E</FullName>
|
||||
</SalesTaxCodeRef>
|
||||
</InvoiceLineAdd>
|
||||
<InvoiceLineAdd>
|
||||
<ItemRef>
|
||||
<FullName>BODY SHOP_LAB</FullName>
|
||||
</ItemRef>
|
||||
<Desc>BODY SHOP SALESLABOR:BODY</Desc>
|
||||
<Quantity>1</Quantity>
|
||||
<Amount>653.35</Amount>
|
||||
<SalesTaxCodeRef>
|
||||
<FullName>E</FullName>
|
||||
</SalesTaxCodeRef>
|
||||
</InvoiceLineAdd>
|
||||
<InvoiceLineAdd>
|
||||
<ItemRef>
|
||||
<FullName>BODY SHOP_LAR</FullName>
|
||||
</ItemRef>
|
||||
<Desc>BODY SHOP SALES:LABOR:REFINISH</Desc>
|
||||
<Quantity>1</Quantity>
|
||||
<Amount>565.26</Amount>
|
||||
<SalesTaxCodeRef>
|
||||
<FullName>E</FullName>
|
||||
</SalesTaxCodeRef>
|
||||
</InvoiceLineAdd>
|
||||
<InvoiceLineAdd>
|
||||
<ItemRef>
|
||||
<FullName>BODY SHOP_MAPA</FullName>
|
||||
</ItemRef>
|
||||
<Desc>paintd</Desc>
|
||||
<Quantity>1</Quantity>
|
||||
<Amount>347.66</Amount>
|
||||
<SalesTaxCodeRef>
|
||||
<FullName>E</FullName>
|
||||
</SalesTaxCodeRef>
|
||||
</InvoiceLineAdd>
|
||||
<InvoiceLineAdd>
|
||||
<ItemRef>
|
||||
<FullName>BODY SHOP_MASH</FullName>
|
||||
</ItemRef>
|
||||
<Desc>shopd</Desc>
|
||||
<Quantity>1</Quantity>
|
||||
<Amount>54.38</Amount>
|
||||
<SalesTaxCodeRef>
|
||||
<FullName>E</FullName>
|
||||
</SalesTaxCodeRef>
|
||||
</InvoiceLineAdd>
|
||||
<InvoiceLineAdd>
|
||||
<ItemRef>
|
||||
<FullName>GST On Sales</FullName>
|
||||
</ItemRef>
|
||||
<Desc>Receiver General - GST</Desc>
|
||||
<Amount>170.41</Amount>
|
||||
</InvoiceLineAdd>
|
||||
<InvoiceLineAdd>
|
||||
<ItemRef>
|
||||
<FullName>PST On Sales</FullName>
|
||||
</ItemRef>
|
||||
<Desc>Ministry of Finance (BC)</Desc>
|
||||
<Amount>238.58</Amount>
|
||||
</InvoiceLineAdd>
|
||||
</InvoiceAdd>
|
||||
</InvoiceAddRq>
|
||||
</QBXMLMsgsRq>
|
||||
|
||||
</QBXML>
|
||||
`;
|
||||
|
||||
// exports.default = async (req, res) => {
|
||||
// const BearerToken = req.headers.authorization;
|
||||
// const { jobId } = req.body;
|
||||
|
||||
// const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
// headers: {
|
||||
// Authorization: BearerToken,
|
||||
// },
|
||||
// });
|
||||
|
||||
// try {
|
||||
// const result = await client
|
||||
// .setHeaders({ Authorization: BearerToken })
|
||||
// .request(queries.QUERY_JOBS_FOR_RECEIVABLES_EXPORT, { id: jobId });
|
||||
// const { jobs_by_pk } = result;
|
||||
// const { bodyshop } = jobs_by_pk;
|
||||
// const QbXmlToExecute = [];
|
||||
|
||||
// //Is this a two tier, or 3 tier setup?
|
||||
// const isThreeTier = bodyshop.accountingconfig.tiers === 3;
|
||||
// const twoTierPref = bodyshop.accountingconfig.twotierpref;
|
||||
|
||||
// if (isThreeTier) {
|
||||
// QbXmlToExecute.push({
|
||||
// id: jobId,
|
||||
// okStatusCodes: ["0", "3100"],
|
||||
// qbxml: generateSourceCustomerQbxml(jobs_by_pk, bodyshop), // Create the source customer.
|
||||
// });
|
||||
// }
|
||||
|
||||
// QbXmlToExecute.push({
|
||||
// id: jobId,
|
||||
// okStatusCodes: ["0", "3100"],
|
||||
// qbxml: generateJobQbxml(
|
||||
// jobs_by_pk,
|
||||
// bodyshop,
|
||||
// isThreeTier,
|
||||
// 2,
|
||||
// twoTierPref
|
||||
// ),
|
||||
// });
|
||||
|
||||
// QbXmlToExecute.push({
|
||||
// id: jobId,
|
||||
// okStatusCodes: ["0", "3100"],
|
||||
// qbxml: generateJobQbxml(
|
||||
// jobs_by_pk,
|
||||
// bodyshop,
|
||||
// isThreeTier,
|
||||
// 3,
|
||||
// twoTierPref
|
||||
// ),
|
||||
// });
|
||||
// //Generate the actual invoice.
|
||||
// QbXmlToExecute.push({
|
||||
// id: jobId,
|
||||
// okStatusCodes: ["0"],
|
||||
// qbxml: generateInvoiceQbxml(jobs_by_pk, bodyshop),
|
||||
// });
|
||||
|
||||
// res.status(200).json(QbXmlToExecute);
|
||||
// } catch (error) {
|
||||
// console.log("error", error);
|
||||
// res.status(400).send(JSON.stringify(error));
|
||||
// }
|
||||
// };
|
||||
|
||||
const generateSourceCustomerQbxml = (jobs_by_pk, bodyshop) => {
|
||||
const customerQbxmlObj = {
|
||||
QBXML: {
|
||||
@@ -324,22 +115,6 @@ const generateSourceCustomerQbxml = (jobs_by_pk, bodyshop) => {
|
||||
return customerQbxml_Full;
|
||||
};
|
||||
|
||||
const generateSourceTier = (jobs_by_pk) => {
|
||||
return jobs_by_pk.ins_co_nm;
|
||||
};
|
||||
const generateJobTier = (jobs_by_pk) => {
|
||||
return jobs_by_pk.ro_number;
|
||||
};
|
||||
const generateOwnerTier = (jobs_by_pk) => {
|
||||
return jobs_by_pk.ownr_co_nm
|
||||
? `${jobs_by_pk.ownr_co_nm} - ${jobs_by_pk.ownr_ln || ""} ${
|
||||
jobs_by_pk.ownr_fn || ""
|
||||
} #${jobs_by_pk.owner.accountingid || ""}`
|
||||
: `${jobs_by_pk.ownr_ln || ""} ${jobs_by_pk.ownr_fn || ""} #${
|
||||
jobs_by_pk.owner.accountingid || ""
|
||||
}`;
|
||||
};
|
||||
|
||||
const generateJobQbxml = (
|
||||
jobs_by_pk,
|
||||
bodyshop,
|
||||
|
||||
@@ -4,3 +4,21 @@ exports.addQbxmlHeader = addQbxmlHeader = (xml) => {
|
||||
${xml}
|
||||
`;
|
||||
};
|
||||
|
||||
exports.generateSourceTier = (jobs_by_pk) => {
|
||||
return jobs_by_pk.ins_co_nm;
|
||||
};
|
||||
|
||||
exports.generateJobTier = (jobs_by_pk) => {
|
||||
return jobs_by_pk.ro_number;
|
||||
};
|
||||
|
||||
exports.generateOwnerTier = (jobs_by_pk) => {
|
||||
return jobs_by_pk.ownr_co_nm
|
||||
? `${jobs_by_pk.ownr_co_nm} - ${jobs_by_pk.ownr_ln || ""} ${
|
||||
jobs_by_pk.ownr_fn || ""
|
||||
} #${jobs_by_pk.owner.accountingid || ""}`
|
||||
: `${jobs_by_pk.ownr_ln || ""} ${jobs_by_pk.ownr_fn || ""} #${
|
||||
jobs_by_pk.owner.accountingid || ""
|
||||
}`;
|
||||
};
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
exports.receivables = require("./qbxml-receivables").default;
|
||||
exports.payables = require("./qbxml-payables").default;
|
||||
exports.payments = require("./qbxml-payments").default;
|
||||
|
||||
@@ -106,9 +106,40 @@ query QUERY_INVOICES_FOR_PAYABLES_EXPORT($invoices: [uuid!]!) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
exports.QUERY_PAYMENTS_FOR_EXPORT = `
|
||||
query QUERY_PAYMENTS_FOR_EXPORT($payments: [uuid!]!) {
|
||||
payments(where: {id: {_in: $payments}}) {
|
||||
id
|
||||
created_at
|
||||
jobid
|
||||
job {
|
||||
id
|
||||
ro_number
|
||||
est_number
|
||||
ins_co_nm
|
||||
owner{
|
||||
accountingid
|
||||
}
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_co_nm
|
||||
bodyshop{
|
||||
accountingconfig
|
||||
md_responsibility_centers
|
||||
}
|
||||
}
|
||||
transactionid
|
||||
memo
|
||||
amount
|
||||
stripeid
|
||||
exportedat
|
||||
stripeid
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
exports.QUERY_UPCOMING_APPOINTMENTS = `
|
||||
query QUERY_UPCOMING_APPOINTMENTS($now: timestamptz!, $jobId: uuid!) {
|
||||
jobs_by_pk(id: $jobId) {
|
||||
|
||||
Reference in New Issue
Block a user