-
+
-
- {/* */}
-
-
-
diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json
index 11718ed7f..473f2a232 100644
--- a/client/src/translations/en_us/common.json
+++ b/client/src/translations/en_us/common.json
@@ -1355,6 +1355,7 @@
"depreciation_taxes": "Depreciation/Taxes",
"dms": {
"address": "Customer Address",
+ "amount": "Amount",
"center": "Center",
"cost": "Cost",
"cost_dms_acctnumber": "Cost DMS Acct #",
@@ -1364,6 +1365,7 @@
"id": "DMS ID",
"inservicedate": "In Service Date",
"journal": "Journal #",
+ "lines": "Posting Lines",
"name1": "Customer Name",
"payer": {
"amount": "Amount",
@@ -2801,6 +2803,7 @@
"country": "Country",
"discount": "Discount % (as decimal)",
"display_name": "Display Name",
+ "dmsid": "DMS ID",
"due_date": "Payment Due Date (# of days)",
"email": "Contact Email",
"favorite": "Favorite?",
diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json
index 984d5d8e7..7df3d4062 100644
--- a/client/src/translations/es/common.json
+++ b/client/src/translations/es/common.json
@@ -1355,6 +1355,7 @@
"depreciation_taxes": "Depreciación / Impuestos",
"dms": {
"address": "",
+ "amount": "",
"center": "",
"cost": "",
"cost_dms_acctnumber": "",
@@ -1364,6 +1365,7 @@
"id": "",
"inservicedate": "",
"journal": "",
+ "lines": "",
"name1": "",
"payer": {
"amount": "",
@@ -2801,6 +2803,7 @@
"country": "País",
"discount": "% De descuento",
"display_name": "Nombre para mostrar",
+ "dmsid": "",
"due_date": "Fecha de vencimiento del pago",
"email": "Email de contacto",
"favorite": "¿Favorito?",
diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json
index ae70715af..a17359079 100644
--- a/client/src/translations/fr/common.json
+++ b/client/src/translations/fr/common.json
@@ -1355,6 +1355,7 @@
"depreciation_taxes": "Amortissement / taxes",
"dms": {
"address": "",
+ "amount": "",
"center": "",
"cost": "",
"cost_dms_acctnumber": "",
@@ -1364,6 +1365,7 @@
"id": "",
"inservicedate": "",
"journal": "",
+ "lines": "",
"name1": "",
"payer": {
"amount": "",
@@ -2801,6 +2803,7 @@
"country": "Pays",
"discount": "Remise %",
"display_name": "Afficher un nom",
+ "dmsid": "",
"due_date": "Date limite de paiement",
"email": "Email du contact",
"favorite": "Préféré?",
diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml
index 2a338443a..00be05ed1 100644
--- a/hasura/metadata/tables.yaml
+++ b/hasura/metadata/tables.yaml
@@ -5396,6 +5396,7 @@
- country
- created_at
- discount
+ - dmsid
- due_date
- email
- favorite
@@ -5418,6 +5419,7 @@
- country
- created_at
- discount
+ - dmsid
- due_date
- email
- favorite
@@ -5450,6 +5452,7 @@
- country
- created_at
- discount
+ - dmsid
- due_date
- email
- favorite
diff --git a/hasura/migrations/1667251913323_alter_table_public_vendors_add_column_dmsid/down.sql b/hasura/migrations/1667251913323_alter_table_public_vendors_add_column_dmsid/down.sql
new file mode 100644
index 000000000..e7d1107de
--- /dev/null
+++ b/hasura/migrations/1667251913323_alter_table_public_vendors_add_column_dmsid/down.sql
@@ -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;
diff --git a/hasura/migrations/1667251913323_alter_table_public_vendors_add_column_dmsid/up.sql b/hasura/migrations/1667251913323_alter_table_public_vendors_add_column_dmsid/up.sql
new file mode 100644
index 000000000..25b9e8232
--- /dev/null
+++ b/hasura/migrations/1667251913323_alter_table_public_vendors_add_column_dmsid/up.sql
@@ -0,0 +1,2 @@
+alter table "public"."vendors" add column "dmsid" text
+ null;
diff --git a/server/accounting/pbs/pbs-ap-allocations.js b/server/accounting/pbs/pbs-ap-allocations.js
index ebf52cac7..036b0c538 100644
--- a/server/accounting/pbs/pbs-ap-allocations.js
+++ b/server/accounting/pbs/pbs-ap-allocations.js
@@ -11,8 +11,47 @@ const queries = require("../../graphql-client/queries");
const CdkBase = require("../../web-sockets/web-socket");
const moment = require("moment");
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 {
CdkBase.createLogEvent(
socket,
@@ -21,28 +60,48 @@ exports.default = async function (socket, billids) {
);
const { bills, bodyshops } = await QueryBillData(socket, billids);
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) => {
//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 = {
[bodyshop.md_responsibility_centers.taxes.federal_itc.name]: {
Account:
bodyshop.md_responsibility_centers.taxes.federal_itc.dms_acctnumber,
- //ControlNumber: "String", //need to figure this out still?
+ ControlNumber: bill.vendor.dmsid,
Amount: Dinero(),
// Comment: "String",
- //AdditionalInfo: "String",
+ AdditionalInfo: bill.vendor.name,
InvoiceNumber: bill.invoice_number,
InvoiceDate: moment(bill.date).tz(bodyshop.timezone).toISOString(),
},
[bodyshop.md_responsibility_centers.taxes.state.name]: {
Account:
bodyshop.md_responsibility_centers.taxes.state.dms_acctnumber,
- //ControlNumber: "String", //need to figure this out still?
+ ControlNumber: bill.vendor.dmsid,
Amount: Dinero(),
// Comment: "String",
- //AdditionalInfo: "String",
+ AdditionalInfo: bill.vendor.name,
InvoiceNumber: bill.invoice_number,
InvoiceDate: moment(bill.date).tz(bodyshop.timezone).toISOString(),
},
@@ -59,17 +118,16 @@ exports.default = async function (socket, billids) {
if (!billHash[cc.name]) {
billHash[cc.name] = {
Account: cc.dms_acctnumber,
- //ControlNumber: "String", //need to figure this out still?
+ ControlNumber: bill.vendor.dmsid,
Amount: Dinero(),
// Comment: "String",
- //AdditionalInfo: "String",
+ AdditionalInfo: bill.vendor.name,
InvoiceNumber: bill.invoice_number,
InvoiceDate: moment(bill.date).tz(bodyshop.timezone).toISOString(),
};
}
//Add the line amount.
-
billHash[cc.name] = {
...billHash[cc.name],
Amount: billHash[cc.name].Amount.add(lineDinero),
@@ -79,36 +137,57 @@ exports.default = async function (socket, billids) {
if (bl.applicable_taxes.federal) {
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[
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) {
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[
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) => {
- transactionLines.push(billHash[key]);
+ transactionObject.Posting.Lines.push({
+ 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) {
CdkBase.createLogEvent(
socket,
"ERROR",
- `Error encountered in CdkCalculateAllocations. ${error}`
+ `Error encountered in PbsCalculateAllocationsAp. ${error}`
);
}
-};
+}
+
+exports.PbsCalculateAllocationsAp = PbsCalculateAllocationsAp;
async function QueryBillData(socket, billids) {
CdkBase.createLogEvent(
@@ -125,6 +204,7 @@ async function QueryBillData(socket, billids) {
"TRACE",
`Bill data query result ${JSON.stringify(result, null, 2)}`
);
+
return result;
}
@@ -136,3 +216,56 @@ function getCostAccount(billline, respcenters) {
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;
+}
diff --git a/server/accounting/pbs/pbs-job-export.js b/server/accounting/pbs/pbs-job-export.js
index 58173be58..ee0b3c8a8 100644
--- a/server/accounting/pbs/pbs-job-export.js
+++ b/server/accounting/pbs/pbs-job-export.js
@@ -182,6 +182,8 @@ async function CheckForErrors(socket, response) {
}
}
+exports.CheckForErrors = CheckForErrors;
+
async function QueryJobData(socket, jobid) {
CdkBase.createLogEvent(socket, "DEBUG", `Querying job data for id ${jobid}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js
index 9dc38386f..0320aa3d5 100644
--- a/server/graphql-client/queries.js
+++ b/server/graphql-client/queries.js
@@ -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 = `
mutation INSERT_EXPORT_LOG($log: exportlog_insert_input!) {
insert_exportlog_one(object: $log) {
@@ -1639,6 +1656,8 @@ query GET_PBS_AP_ALLOCATIONS($billids: [uuid!]) {
bodyshops(where: {associations: {active: {_eq: true}}}) {
md_responsibility_centers
timezone
+ pbs_serialnumber
+ id
}
bills(where: {id: {_in: $billids}}) {
id
@@ -1659,6 +1678,7 @@ query GET_PBS_AP_ALLOCATIONS($billids: [uuid!]) {
vendor {
id
name
+ dmsid
}
billlines {
id
diff --git a/server/web-sockets/web-socket.js b/server/web-sockets/web-socket.js
index 11b16cfbe..67886077e 100644
--- a/server/web-sockets/web-socket.js
+++ b/server/web-sockets/web-socket.js
@@ -22,8 +22,10 @@ const {
PbsSelectedCustomer,
} = require("../accounting/pbs/pbs-job-export");
-const PbsCalculateAllocationsAp =
- require("../accounting/pbs/pbs-ap-allocations").default;
+const {
+ PbsCalculateAllocationsAp,
+ PbsExportAp,
+} = require("../accounting/pbs/pbs-ap-allocations");
io.use(function (socket, next) {
try {
@@ -139,9 +141,15 @@ io.on("connection", (socket) => {
"TRACE",
`Allocations calculated. ${JSON.stringify(allocations, null, 2)}`
);
-
+ socket.apAllocations = allocations;
callback(allocations);
});
+
+ socket.on("pbs-export-ap", ({ billids, txEnvelope }) => {
+ socket.txEnvelope = txEnvelope;
+ PbsExportAp(socket, { billids, txEnvelope });
+ });
+
//END PBS AP
socket.on("disconnect", () => {