WIP PBS AP.

This commit is contained in:
Patrick Fic
2022-10-31 15:59:00 -07:00
parent cd96de3a96
commit ede1cdb89b
16 changed files with 418 additions and 174 deletions

View File

@@ -22351,6 +22351,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>amount</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> <concept_node>
<name>center</name> <name>center</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -22540,6 +22561,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>lines</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> <concept_node>
<name>name1</name> <name>name1</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -47259,6 +47301,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>dmsid</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> <concept_node>
<name>due_date</name> <name>due_date</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>

View File

@@ -1,5 +1,5 @@
import { SyncOutlined } from "@ant-design/icons"; import { SyncOutlined } from "@ant-design/icons";
import { Button, Card, Table, Typography } from "antd"; import { Button, Card, Form, Input, Table } from "antd";
import Dinero from "dinero.js"; import Dinero from "dinero.js";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -29,52 +29,49 @@ export function DmsAllocationsSummaryAp({ socket, bodyshop, billids, title }) {
if (socket.connected) { if (socket.connected) {
socket.emit("pbs-calculate-allocations-ap", billids, (ack) => { socket.emit("pbs-calculate-allocations-ap", billids, (ack) => {
setAllocationsSummary(ack); setAllocationsSummary(ack);
socket.allocationsSummary = ack; socket.allocationsSummary = ack;
}); });
} }
}, [socket, socket.connected, billids]); }, [socket, socket.connected, billids]);
console.log(allocationsSummary);
const columns = [ const columns = [
{ {
title: t("jobs.fields.dms.center"), title: t("jobs.fields.ro_number"),
dataIndex: "center", dataIndex: ["Posting", "Reference"],
key: "center", key: "reference",
}, },
{ {
title: t("jobs.fields.dms.sale"), title: t("jobs.fields.dms.lines"),
dataIndex: "sale", dataIndex: "Lines",
key: "sale", key: "Lines",
render: (text, record) => Dinero(record.sale).toFormat(), render: (text, record) => (
}, <table style={{ tableLayout: "auto" }}>
{ <tr>
title: t("jobs.fields.dms.cost"), <th>{t("bills.fields.invoice_number")}</th>
dataIndex: "cost", <th>{t("bodyshop.fields.dms.dms_acctnumber")}</th>
key: "cost", <th>{t("jobs.fields.dms.amount")}</th>
render: (text, record) => Dinero(record.cost).toFormat(), </tr>
}, {record.Posting.Lines.map((l, idx) => (
{ <tr key={idx}>
title: t("jobs.fields.dms.sale_dms_acctnumber"), <td>{l.InvoiceNumber}</td>
dataIndex: "sale_dms_acctnumber", <td>{l.Account}</td>
key: "sale_dms_acctnumber", <td>{Dinero(l.Amount).toFormat()}</td>
render: (text, record) => </tr>
record.profitCenter && record.profitCenter.dms_acctnumber, ))}
}, </table>
{ ),
title: t("jobs.fields.dms.cost_dms_acctnumber"),
dataIndex: "cost_dms_acctnumber",
key: "cost_dms_acctnumber",
render: (text, record) =>
record.costCenter && record.costCenter.dms_acctnumber,
},
{
title: t("jobs.fields.dms.dms_wip_acctnumber"),
dataIndex: "dms_wip_acctnumber",
key: "dms_wip_acctnumber",
render: (text, record) =>
record.costCenter && record.costCenter.dms_wip_acctnumber,
}, },
]; ];
const handleFinish = async (values) => {
socket.emit(`pbs-export-ap`, {
billids,
txEnvelope: values,
});
};
return ( return (
<Card <Card
title={title} title={title}
@@ -93,46 +90,31 @@ export function DmsAllocationsSummaryAp({ socket, bodyshop, billids, title }) {
<Table <Table
pagination={{ position: "top", defaultPageSize: 50 }} pagination={{ position: "top", defaultPageSize: 50 }}
columns={columns} columns={columns}
rowKey="center" rowKey={(record) => `${record.InvoiceNumber}${record.Account}`}
dataSource={allocationsSummary} dataSource={allocationsSummary}
locale={{ emptyText: t("dms.labels.refreshallocations") }} locale={{ emptyText: t("dms.labels.refreshallocations") }}
summary={() => {
const totals =
allocationsSummary &&
allocationsSummary.reduce(
(acc, val) => {
return {
totalSale: acc.totalSale.add(Dinero(val.sale)),
totalCost: acc.totalCost.add(Dinero(val.cost)),
};
},
{
totalSale: Dinero(),
totalCost: Dinero(),
}
);
return (
<Table.Summary.Row>
<Table.Summary.Cell>
<Typography.Title level={4}>
{t("general.labels.totals")}
</Typography.Title>
</Table.Summary.Cell>
<Table.Summary.Cell>
{totals && totals.totalSale.toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell>
{
// totals.totalCost.toFormat()
}
</Table.Summary.Cell>
<Table.Summary.Cell></Table.Summary.Cell>
<Table.Summary.Cell></Table.Summary.Cell>
</Table.Summary.Row>
);
}}
/> />
<Form layout="vertical" onFinish={handleFinish}>
<Form.Item
name="journal"
label={t("jobs.fields.dms.journal")}
initialValue={
bodyshop.cdk_configuration &&
bodyshop.cdk_configuration.default_journal
}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input />
</Form.Item>
<Button disabled={!socket.allocationsSummary} htmlType="submit">
{t("jobs.actions.dms.post")}
</Button>
</Form>
</Card> </Card>
); );
} }

View File

@@ -4536,68 +4536,71 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<Input /> <Input />
</Form.Item> */} </Form.Item> */}
</LayoutFormRow> </LayoutFormRow>
{/* <LayoutFormRow>
<Form.Item {DmsAp.treatment === "on" && (
label={t("bodyshop.fields.responsibilitycenters.ap")} <LayoutFormRow header={t("bodyshop.fields.responsibilitycenters.ap")}>
rules={[ {/* <Form.Item
{ label={t("bodyshop.fields.responsibilitycenters.ap")}
required: true, rules={[
//message: t("general.validation.required"), {
}, required: true,
]} //message: t("general.validation.required"),
name={["md_responsibility_centers", "ap", "name"]} },
> ]}
<Input /> name={["md_responsibility_centers", "ap", "name"]}
</Form.Item> >
<Form.Item <Input />
label={t("bodyshop.fields.responsibilitycenter_accountnumber")} </Form.Item> */}
rules={[ {/* <Form.Item
{ label={t("bodyshop.fields.responsibilitycenter_accountnumber")}
required: true, rules={[
//message: t("general.validation.required"), {
}, required: true,
]} //message: t("general.validation.required"),
name={["md_responsibility_centers", "ap", "accountnumber"]} },
> ]}
<Input /> name={["md_responsibility_centers", "ap", "accountnumber"]}
</Form.Item> >
<Form.Item <Input />
label={t("bodyshop.fields.responsibilitycenter_accountname")} </Form.Item> */}
rules={[ <Form.Item
{ label={t("bodyshop.fields.responsibilitycenter_accountname")}
required: true, rules={[
//message: t("general.validation.required"), {
}, required: true,
]} //message: t("general.validation.required"),
name={["md_responsibility_centers", "ap", "accountname"]} },
> ]}
<Input /> name={["md_responsibility_centers", "ap", "accountname"]}
</Form.Item> >
<Form.Item <Input />
label={t("bodyshop.fields.responsibilitycenter_accountdesc")} </Form.Item>
rules={[ <Form.Item
{ label={t("bodyshop.fields.responsibilitycenter_accountdesc")}
required: true, rules={[
//message: t("general.validation.required"), {
}, required: true,
]} //message: t("general.validation.required"),
name={["md_responsibility_centers", "ap", "accountdesc"]} },
> ]}
<Input /> name={["md_responsibility_centers", "ap", "accountdesc"]}
</Form.Item> >
<Form.Item <Input />
label={t("bodyshop.fields.responsibilitycenter_accountitem")} </Form.Item>
rules={[ <Form.Item
{ label={t("bodyshop.fields.dms.dms_acctnumber")}
required: true, rules={[
//message: t("general.validation.required"), {
}, required: true,
]} //message: t("general.validation.required"),
name={["md_responsibility_centers", "ap", "accountitem"]} },
> ]}
<Input /> name={["md_responsibility_centers", "ap", "dms_acctnumber"]}
</Form.Item> >
</LayoutFormRow> */} <Input />
</Form.Item>
</LayoutFormRow>
)}
<LayoutFormRow header={<div>Refund</div>}> <LayoutFormRow header={<div>Refund</div>}>
{/* <Form.Item {/* <Form.Item
label={t("bodyshop.fields.responsibilitycenters.refund")} label={t("bodyshop.fields.responsibilitycenters.refund")}

View File

@@ -1,5 +1,6 @@
import { DeleteFilled } from "@ant-design/icons"; import { DeleteFilled } from "@ant-design/icons";
import { useApolloClient } from "@apollo/client"; import { useApolloClient } from "@apollo/client";
import { useTreatments } from "@splitsoftware/splitio-react";
import { import {
Button, Button,
Divider, Divider,
@@ -20,7 +21,23 @@ import PhoneFormItem, {
} from "../form-items-formatted/phone-form-item.component"; } from "../form-items-formatted/phone-form-item.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import VendorsPhonebookAdd from "../vendors-phonebook-add/vendors-phonebook-add.component"; import VendorsPhonebookAdd from "../vendors-phonebook-add/vendors-phonebook-add.component";
export default function VendorsFormComponent({
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(VendorsFormComponent);
export function VendorsFormComponent({
bodyshop,
form, form,
formLoading, formLoading,
handleDelete, handleDelete,
@@ -29,6 +46,12 @@ export default function VendorsFormComponent({
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const client = useApolloClient(); const client = useApolloClient();
const { DmsAp } = useTreatments(
["DmsAp"],
{},
bodyshop && bodyshop.imexshopid
);
const { getFieldValue } = form; const { getFieldValue } = form;
return ( return (
<div> <div>
@@ -184,6 +207,12 @@ export default function VendorsFormComponent({
// </Form.Item> // </Form.Item>
} }
</LayoutFormRow> </LayoutFormRow>
{DmsAp.treatment === "on" && (
<Form.Item label={t("vendors.fields.dmsid")} name="dmsid">
<Input />
</Form.Item>
)}
<Divider align="left">{t("vendors.labels.preferredmakes")}</Divider> <Divider align="left">{t("vendors.labels.preferredmakes")}</Divider>
<Form.List name="favorite"> <Form.List name="favorite">
{(fields, { add, remove }) => { {(fields, { add, remove }) => {

View File

@@ -18,6 +18,7 @@ export const QUERY_VENDOR_BY_ID = gql`
street1 street1
active active
phone phone
dmsid
} }
} }
`; `;

View File

@@ -1,5 +1,4 @@
import { Button, Card, Col, notification, Row, Select, Space } from "antd"; import { Button, Card, Col, notification, Row, Select, Space } from "antd";
import queryString from "query-string";
import React, { useEffect, useRef, useState } from "react"; import React, { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -7,9 +6,7 @@ import { useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import SocketIO from "socket.io-client"; import SocketIO from "socket.io-client";
import DmsAllocationsSummaryApComponent from "../../components/dms-allocations-summary-ap/dms-allocations-summary-ap.component"; import DmsAllocationsSummaryApComponent from "../../components/dms-allocations-summary-ap/dms-allocations-summary-ap.component";
import DmsCustomerSelector from "../../components/dms-customer-selector/dms-customer-selector.component";
import DmsLogEvents from "../../components/dms-log-events/dms-log-events.component"; import DmsLogEvents from "../../components/dms-log-events/dms-log-events.component";
import DmsPostForm from "../../components/dms-post-form/dms-post-form.component";
import { auth } from "../../firebase/firebase.utils"; import { auth } from "../../firebase/firebase.utils";
import { import {
setBreadcrumbs, setBreadcrumbs,
@@ -47,10 +44,9 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
const [logLevel, setLogLevel] = useState("DEBUG"); const [logLevel, setLogLevel] = useState("DEBUG");
const history = useHistory(); const history = useHistory();
const [logs, setLogs] = useState([]); const [logs, setLogs] = useState([]);
const search = queryString.parse(useLocation().search);
const { state } = useLocation(); const { state } = useLocation();
const { jobId } = search;
const logsRef = useRef(null); const logsRef = useRef(null);
useEffect(() => { useEffect(() => {
@@ -109,24 +105,13 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
return ( return (
<div> <div>
DMS PAYABLES SCREEN
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
<Col md={24} lg={10}> <Col span={24}>
<DmsAllocationsSummaryApComponent <DmsAllocationsSummaryApComponent
socket={socket} socket={socket}
billids={state?.billids} billids={state?.billids}
/> />
</Col> </Col>
<Col md={24} lg={14}>
{/* <DmsPostForm
socket={socket}
jobId={jobId}
// job={data && data.jobs_by_pk}
logsRef={logsRef}
/> */}
</Col>
<DmsCustomerSelector />
<Col span={24}> <Col span={24}>
<div ref={logsRef}> <div ref={logsRef}>

View File

@@ -1355,6 +1355,7 @@
"depreciation_taxes": "Depreciation/Taxes", "depreciation_taxes": "Depreciation/Taxes",
"dms": { "dms": {
"address": "Customer Address", "address": "Customer Address",
"amount": "Amount",
"center": "Center", "center": "Center",
"cost": "Cost", "cost": "Cost",
"cost_dms_acctnumber": "Cost DMS Acct #", "cost_dms_acctnumber": "Cost DMS Acct #",
@@ -1364,6 +1365,7 @@
"id": "DMS ID", "id": "DMS ID",
"inservicedate": "In Service Date", "inservicedate": "In Service Date",
"journal": "Journal #", "journal": "Journal #",
"lines": "Posting Lines",
"name1": "Customer Name", "name1": "Customer Name",
"payer": { "payer": {
"amount": "Amount", "amount": "Amount",
@@ -2801,6 +2803,7 @@
"country": "Country", "country": "Country",
"discount": "Discount % (as decimal)", "discount": "Discount % (as decimal)",
"display_name": "Display Name", "display_name": "Display Name",
"dmsid": "DMS ID",
"due_date": "Payment Due Date (# of days)", "due_date": "Payment Due Date (# of days)",
"email": "Contact Email", "email": "Contact Email",
"favorite": "Favorite?", "favorite": "Favorite?",

View File

@@ -1355,6 +1355,7 @@
"depreciation_taxes": "Depreciación / Impuestos", "depreciation_taxes": "Depreciación / Impuestos",
"dms": { "dms": {
"address": "", "address": "",
"amount": "",
"center": "", "center": "",
"cost": "", "cost": "",
"cost_dms_acctnumber": "", "cost_dms_acctnumber": "",
@@ -1364,6 +1365,7 @@
"id": "", "id": "",
"inservicedate": "", "inservicedate": "",
"journal": "", "journal": "",
"lines": "",
"name1": "", "name1": "",
"payer": { "payer": {
"amount": "", "amount": "",
@@ -2801,6 +2803,7 @@
"country": "País", "country": "País",
"discount": "% De descuento", "discount": "% De descuento",
"display_name": "Nombre para mostrar", "display_name": "Nombre para mostrar",
"dmsid": "",
"due_date": "Fecha de vencimiento del pago", "due_date": "Fecha de vencimiento del pago",
"email": "Email de contacto", "email": "Email de contacto",
"favorite": "¿Favorito?", "favorite": "¿Favorito?",

View File

@@ -1355,6 +1355,7 @@
"depreciation_taxes": "Amortissement / taxes", "depreciation_taxes": "Amortissement / taxes",
"dms": { "dms": {
"address": "", "address": "",
"amount": "",
"center": "", "center": "",
"cost": "", "cost": "",
"cost_dms_acctnumber": "", "cost_dms_acctnumber": "",
@@ -1364,6 +1365,7 @@
"id": "", "id": "",
"inservicedate": "", "inservicedate": "",
"journal": "", "journal": "",
"lines": "",
"name1": "", "name1": "",
"payer": { "payer": {
"amount": "", "amount": "",
@@ -2801,6 +2803,7 @@
"country": "Pays", "country": "Pays",
"discount": "Remise %", "discount": "Remise %",
"display_name": "Afficher un nom", "display_name": "Afficher un nom",
"dmsid": "",
"due_date": "Date limite de paiement", "due_date": "Date limite de paiement",
"email": "Email du contact", "email": "Email du contact",
"favorite": "Préféré?", "favorite": "Préféré?",

View File

@@ -5396,6 +5396,7 @@
- country - country
- created_at - created_at
- discount - discount
- dmsid
- due_date - due_date
- email - email
- favorite - favorite
@@ -5418,6 +5419,7 @@
- country - country
- created_at - created_at
- discount - discount
- dmsid
- due_date - due_date
- email - email
- favorite - favorite
@@ -5450,6 +5452,7 @@
- country - country
- created_at - created_at
- discount - discount
- dmsid
- due_date - due_date
- email - email
- favorite - favorite

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."vendors" add column "dmsid" text
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."vendors" add column "dmsid" text
null;

View File

@@ -11,8 +11,47 @@ const queries = require("../../graphql-client/queries");
const CdkBase = require("../../web-sockets/web-socket"); const CdkBase = require("../../web-sockets/web-socket");
const moment = require("moment"); const moment = require("moment");
const Dinero = require("dinero.js"); const Dinero = require("dinero.js");
const AxiosLib = require("axios").default;
const axios = AxiosLib.create();
const { PBS_ENDPOINTS, PBS_CREDENTIALS } = require("./pbs-constants");
const { CheckForErrors } = require("./pbs-job-export");
const uuid = require("uuid").v4;
axios.interceptors.request.use((x) => {
const socket = x.socket;
exports.default = async function (socket, billids) { const headers = {
...x.headers.common,
...x.headers[x.method],
...x.headers,
};
const printable = `${new Date()} | Request: ${x.method.toUpperCase()} | ${
x.url
} | ${JSON.stringify(x.data)} | ${JSON.stringify(headers)}`;
console.log(printable);
CdkBase.createJsonEvent(socket, "TRACE", `Raw Request: ${printable}`, x.data);
return x;
});
axios.interceptors.response.use((x) => {
const socket = x.config.socket;
const printable = `${new Date()} | Response: ${x.status} | ${JSON.stringify(
x.data
)}`;
console.log(printable);
CdkBase.createJsonEvent(
socket,
"TRACE",
`Raw Response: ${printable}`,
x.data
);
return x;
});
async function PbsCalculateAllocationsAp(socket, billids) {
try { try {
CdkBase.createLogEvent( CdkBase.createLogEvent(
socket, socket,
@@ -21,28 +60,48 @@ exports.default = async function (socket, billids) {
); );
const { bills, bodyshops } = await QueryBillData(socket, billids); const { bills, bodyshops } = await QueryBillData(socket, billids);
const bodyshop = bodyshops[0]; const bodyshop = bodyshops[0];
const transactionLines = []; socket.bodyshop = bodyshop;
socket.bills = bills;
//Each bill will enter it's own top level transaction.
const transactionlist = [];
bills.forEach((bill) => { bills.forEach((bill) => {
//Keep the allocations at the bill level. //Keep the allocations at the bill level.
const transactionObject = {
SerialNumber: socket.bodyshop.pbs_serialnumber,
billid: bill.id,
Posting: {
Reference: bill.job.ro_number,
JournalCode: socket.txEnvelope ? socket.txEnvelope.journal : null,
TransactionDate: moment().tz(socket.bodyshop.timezone).toISOString(), //"0001-01-01T00:00:00.0000000Z",
//Description: "Bulk AP posting.",
//AdditionalInfo: "String",
Source: "ImEX Online",
Lines: [], //socket.apAllocations,
},
};
const billHash = { const billHash = {
[bodyshop.md_responsibility_centers.taxes.federal_itc.name]: { [bodyshop.md_responsibility_centers.taxes.federal_itc.name]: {
Account: Account:
bodyshop.md_responsibility_centers.taxes.federal_itc.dms_acctnumber, bodyshop.md_responsibility_centers.taxes.federal_itc.dms_acctnumber,
//ControlNumber: "String", //need to figure this out still? ControlNumber: bill.vendor.dmsid,
Amount: Dinero(), Amount: Dinero(),
// Comment: "String", // Comment: "String",
//AdditionalInfo: "String", AdditionalInfo: bill.vendor.name,
InvoiceNumber: bill.invoice_number, InvoiceNumber: bill.invoice_number,
InvoiceDate: moment(bill.date).tz(bodyshop.timezone).toISOString(), InvoiceDate: moment(bill.date).tz(bodyshop.timezone).toISOString(),
}, },
[bodyshop.md_responsibility_centers.taxes.state.name]: { [bodyshop.md_responsibility_centers.taxes.state.name]: {
Account: Account:
bodyshop.md_responsibility_centers.taxes.state.dms_acctnumber, bodyshop.md_responsibility_centers.taxes.state.dms_acctnumber,
//ControlNumber: "String", //need to figure this out still? ControlNumber: bill.vendor.dmsid,
Amount: Dinero(), Amount: Dinero(),
// Comment: "String", // Comment: "String",
//AdditionalInfo: "String", AdditionalInfo: bill.vendor.name,
InvoiceNumber: bill.invoice_number, InvoiceNumber: bill.invoice_number,
InvoiceDate: moment(bill.date).tz(bodyshop.timezone).toISOString(), InvoiceDate: moment(bill.date).tz(bodyshop.timezone).toISOString(),
}, },
@@ -59,17 +118,16 @@ exports.default = async function (socket, billids) {
if (!billHash[cc.name]) { if (!billHash[cc.name]) {
billHash[cc.name] = { billHash[cc.name] = {
Account: cc.dms_acctnumber, Account: cc.dms_acctnumber,
//ControlNumber: "String", //need to figure this out still? ControlNumber: bill.vendor.dmsid,
Amount: Dinero(), Amount: Dinero(),
// Comment: "String", // Comment: "String",
//AdditionalInfo: "String", AdditionalInfo: bill.vendor.name,
InvoiceNumber: bill.invoice_number, InvoiceNumber: bill.invoice_number,
InvoiceDate: moment(bill.date).tz(bodyshop.timezone).toISOString(), InvoiceDate: moment(bill.date).tz(bodyshop.timezone).toISOString(),
}; };
} }
//Add the line amount. //Add the line amount.
billHash[cc.name] = { billHash[cc.name] = {
...billHash[cc.name], ...billHash[cc.name],
Amount: billHash[cc.name].Amount.add(lineDinero), Amount: billHash[cc.name].Amount.add(lineDinero),
@@ -79,36 +137,57 @@ exports.default = async function (socket, billids) {
if (bl.applicable_taxes.federal) { if (bl.applicable_taxes.federal) {
billHash[bodyshop.md_responsibility_centers.taxes.federal_itc.name] = billHash[bodyshop.md_responsibility_centers.taxes.federal_itc.name] =
{ {
...bodyshop.md_responsibility_centers.taxes.federal_itc.name, ...billHash[
bodyshop.md_responsibility_centers.taxes.federal_itc.name
],
Amount: billHash[ Amount: billHash[
bodyshop.md_responsibility_centers.taxes.federal_itc.name bodyshop.md_responsibility_centers.taxes.federal_itc.name
].Amount.add(lineDinero.percentage(bl.federal_tax_rate || 0)), ].Amount.add(lineDinero.percentage(bill.federal_tax_rate || 0)),
}; };
} }
if (bl.applicable_taxes.state) { if (bl.applicable_taxes.state) {
billHash[bodyshop.md_responsibility_centers.taxes.state.name] = { billHash[bodyshop.md_responsibility_centers.taxes.state.name] = {
...bodyshop.md_responsibility_centers.taxes.state.name, ...billHash[bodyshop.md_responsibility_centers.taxes.state.name],
Amount: billHash[ Amount: billHash[
bodyshop.md_responsibility_centers.taxes.state.name bodyshop.md_responsibility_centers.taxes.state.name
].Amount.add(lineDinero.percentage(bl.state_tax_rate || 0)), ].Amount.add(lineDinero.percentage(bill.state_tax_rate || 0)),
}; };
} }
//End tax check
});
let APAmount = Dinero();
Object.keys(billHash).map((key) => {
if (billHash[key].Amount.getAmount() > 0) {
transactionObject.Posting.Lines.push(billHash[key]);
APAmount = APAmount.add(billHash[key].Amount); //Calculate the total expense for the bill iteratively to create the corresponding credit to AP.
}
}); });
Object.keys(billHash).map((key) => { transactionObject.Posting.Lines.push({
transactionLines.push(billHash[key]); Account: bodyshop.md_responsibility_centers.ap.dms_acctnumber,
ControlNumber: bill.vendor.dmsid,
Amount: APAmount.multiply(-1),
// Comment: "String",
AdditionalInfo: bill.vendor.name,
InvoiceNumber: bill.invoice_number,
InvoiceDate: moment(bill.date).tz(bodyshop.timezone).toISOString(),
}); });
transactionlist.push(transactionObject);
}); });
return transactionLines; return transactionlist;
} catch (error) { } catch (error) {
CdkBase.createLogEvent( CdkBase.createLogEvent(
socket, socket,
"ERROR", "ERROR",
`Error encountered in CdkCalculateAllocations. ${error}` `Error encountered in PbsCalculateAllocationsAp. ${error}`
); );
} }
}; }
exports.PbsCalculateAllocationsAp = PbsCalculateAllocationsAp;
async function QueryBillData(socket, billids) { async function QueryBillData(socket, billids) {
CdkBase.createLogEvent( CdkBase.createLogEvent(
@@ -125,6 +204,7 @@ async function QueryBillData(socket, billids) {
"TRACE", "TRACE",
`Bill data query result ${JSON.stringify(result, null, 2)}` `Bill data query result ${JSON.stringify(result, null, 2)}`
); );
return result; return result;
} }
@@ -136,3 +216,56 @@ function getCostAccount(billline, respcenters) {
return respcenters.costs.find((c) => c.name === acctName); return respcenters.costs.find((c) => c.name === acctName);
} }
exports.PbsExportAp = async function (socket, { billids, txEnvelope }) {
CdkBase.createLogEvent(socket, "DEBUG", `Exporting selected AP.`);
//apAllocations has the same shap as the lines key for the accounting posting to PBS.
socket.apAllocations = await PbsCalculateAllocationsAp(socket, billids);
socket.txEnvelope = txEnvelope;
for (const allocation of socket.apAllocations) {
const { billid, ...restAllocation } = allocation;
const { data: AccountPostingChange } = await axios.post(
PBS_ENDPOINTS.AccountingPostingChange,
restAllocation,
{ auth: PBS_CREDENTIALS, socket }
);
CheckForErrors(socket, AccountPostingChange);
if (AccountPostingChange.WasSuccessful) {
CdkBase.createLogEvent(socket, "DEBUG", `Marking bill as exported.`);
await MarkApExported(socket, [billid]);
// socket.emit("export-success", billids);
} else {
CdkBase.createLogEvent(socket, "ERROR", `Export was not succesful.`);
}
}
};
async function MarkApExported(socket, billids) {
CdkBase.createLogEvent(
socket,
"DEBUG",
`Marking bills as exported for id ${billids}`
);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.request(queries.MARK_BILLS_EXPORTED, {
billids,
bill: {
exported: true,
exported_at: new Date(),
},
logs: socket.bills.map((bill) => ({
bodyshopid: socket.bodyshop.id,
billid: bill.id,
successful: true,
useremail: socket.user.email,
})),
});
return result;
}

View File

@@ -182,6 +182,8 @@ async function CheckForErrors(socket, response) {
} }
} }
exports.CheckForErrors = CheckForErrors;
async function QueryJobData(socket, jobid) { async function QueryJobData(socket, jobid) {
CdkBase.createLogEvent(socket, "DEBUG", `Querying job data for id ${jobid}`); CdkBase.createLogEvent(socket, "DEBUG", `Querying job data for id ${jobid}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {}); const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});

View File

@@ -1506,6 +1506,23 @@ mutation MARK_JOB_EXPORTED($jobId: uuid!, $job: jobs_set_input!, $log: exportlog
} }
`; `;
exports.MARK_BILLS_EXPORTED = `
mutation UPDATE_BILLS($billids: [uuid!]!, $bill: bills_set_input!, $logs: [exportlog_insert_input!]!) {
update_bills(where: {id: {_in: $billids}}, _set: $bill) {
returning {
id
exported
exported_at
}
}
insert_exportlog(objects: $logs) {
returning{
id
}
}
}
`;
exports.INSERT_EXPORT_LOG = ` exports.INSERT_EXPORT_LOG = `
mutation INSERT_EXPORT_LOG($log: exportlog_insert_input!) { mutation INSERT_EXPORT_LOG($log: exportlog_insert_input!) {
insert_exportlog_one(object: $log) { insert_exportlog_one(object: $log) {
@@ -1639,6 +1656,8 @@ query GET_PBS_AP_ALLOCATIONS($billids: [uuid!]) {
bodyshops(where: {associations: {active: {_eq: true}}}) { bodyshops(where: {associations: {active: {_eq: true}}}) {
md_responsibility_centers md_responsibility_centers
timezone timezone
pbs_serialnumber
id
} }
bills(where: {id: {_in: $billids}}) { bills(where: {id: {_in: $billids}}) {
id id
@@ -1659,6 +1678,7 @@ query GET_PBS_AP_ALLOCATIONS($billids: [uuid!]) {
vendor { vendor {
id id
name name
dmsid
} }
billlines { billlines {
id id

View File

@@ -22,8 +22,10 @@ const {
PbsSelectedCustomer, PbsSelectedCustomer,
} = require("../accounting/pbs/pbs-job-export"); } = require("../accounting/pbs/pbs-job-export");
const PbsCalculateAllocationsAp = const {
require("../accounting/pbs/pbs-ap-allocations").default; PbsCalculateAllocationsAp,
PbsExportAp,
} = require("../accounting/pbs/pbs-ap-allocations");
io.use(function (socket, next) { io.use(function (socket, next) {
try { try {
@@ -139,9 +141,15 @@ io.on("connection", (socket) => {
"TRACE", "TRACE",
`Allocations calculated. ${JSON.stringify(allocations, null, 2)}` `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`
); );
socket.apAllocations = allocations;
callback(allocations); callback(allocations);
}); });
socket.on("pbs-export-ap", ({ billids, txEnvelope }) => {
socket.txEnvelope = txEnvelope;
PbsExportAp(socket, { billids, txEnvelope });
});
//END PBS AP //END PBS AP
socket.on("disconnect", () => { socket.on("disconnect", () => {