diff --git a/.platform/nginx/conf.d/proxy.conf b/.platform/nginx/conf.d/proxy.conf
index b68f5618f..ae3fb47f6 100644
--- a/.platform/nginx/conf.d/proxy.conf
+++ b/.platform/nginx/conf.d/proxy.conf
@@ -1 +1 @@
-client_max_body_size 15M;
\ No newline at end of file
+client_max_body_size 50M;
\ No newline at end of file
diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel
index 46f5e9f32..e407d53ed 100644
--- a/bodyshop_translations.babel
+++ b/bodyshop_translations.babel
@@ -11700,6 +11700,27 @@
+
+ pdfcopywillbeattached
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
preview
false
diff --git a/client/src/components/email-overlay/email-overlay.component.jsx b/client/src/components/email-overlay/email-overlay.component.jsx
index 3f6f8f72c..756f7b45f 100644
--- a/client/src/components/email-overlay/email-overlay.component.jsx
+++ b/client/src/components/email-overlay/email-overlay.component.jsx
@@ -1,5 +1,5 @@
import { UploadOutlined } from "@ant-design/icons";
-import { Divider, Form, Input, Select, Tabs, Upload } from "antd";
+import { Divider, Form, Input, Select, Tabs, Typography, Upload } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import EmailDocumentsComponent from "../email-documents/email-documents.component";
@@ -37,6 +37,8 @@ export default function EmailOverlayComponent({ form, selectedMediaState }) {
{t("emails.labels.preview")}
+ {t("emails.labels.pdfcopywillbeattached")}
+
{() => {
return (
diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json
index 8c444c2cd..0a7420617 100644
--- a/client/src/translations/en_us/common.json
+++ b/client/src/translations/en_us/common.json
@@ -743,6 +743,7 @@
"attachments": "Attachments",
"documents": "Documents",
"generatingemail": "Generating email...",
+ "pdfcopywillbeattached": "A PDF copy of this email will be attached when it is sent.",
"preview": "Email Preview"
},
"successes": {
diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json
index d8eb746a4..99407b22c 100644
--- a/client/src/translations/es/common.json
+++ b/client/src/translations/es/common.json
@@ -743,6 +743,7 @@
"attachments": "",
"documents": "",
"generatingemail": "",
+ "pdfcopywillbeattached": "",
"preview": ""
},
"successes": {
diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json
index 811471037..003fb315f 100644
--- a/client/src/translations/fr/common.json
+++ b/client/src/translations/fr/common.json
@@ -743,6 +743,7 @@
"attachments": "",
"documents": "",
"generatingemail": "",
+ "pdfcopywillbeattached": "",
"preview": ""
},
"successes": {
diff --git a/package.json b/package.json
index 3ef5a1d82..0c15fb68e 100644
--- a/package.json
+++ b/package.json
@@ -39,6 +39,7 @@
"phone": "^2.4.20",
"soap": "^0.39.0",
"socket.io": "^4.1.2",
+ "ssh2-sftp-client": "^7.0.0",
"stripe": "^8.148.0",
"twilio": "^3.62.0",
"xmlbuilder2": "^2.4.1"
diff --git a/server.js b/server.js
index 70186a202..d4122df1e 100644
--- a/server.js
+++ b/server.js
@@ -139,7 +139,7 @@ app.post("/qbo/authorize", qbo.authorize);
app.get("/qbo/callback", qbo.callback);
var data = require("./server/data/data");
-app.get("/data/ah", data.autohouse);
+app.post("/data/ah", data.autohouse);
var ioevent = require("./server/ioevent/ioevent");
app.post("/ioevent", ioevent.default);
diff --git a/server/data/autohouse.js b/server/data/autohouse.js
index 765e787cf..64a7bf6cd 100644
--- a/server/data/autohouse.js
+++ b/server/data/autohouse.js
@@ -11,42 +11,109 @@ require("dotenv").config({
`.env.${process.env.NODE_ENV || "development"}`
),
});
+let Client = require("ssh2-sftp-client");
const client = require("../graphql-client/graphql-client").client;
const AHDineroFormat = "0.00";
const AhDateFormat = "MMDDYYYY";
+
+const repairOpCodes = ["OP4", "OP9", "OP10"];
+const replaceOpCodes = ["OP2", "OP5", "OP11", "OP12"];
+
+const ftpSetup = {
+ host: process.env.AUTOHOUSE_HOST,
+ port: process.env.AUTOHOUSE_PORT,
+ username: process.env.AUTOHOUSE_USER,
+ password: process.env.AUTOHOUSE_PASSWORD,
+ //debug: console.log,
+};
+
exports.default = async (req, res) => {
- //Get Client Dataset.
- const { jobs } = await client.request(queries.AUTOHOUSE_QUERY);
+ //Query for the List of Bodyshop Clients.
+ const { bodyshops } = await client.request(queries.GET_AUTOHOUSE_SHOPS);
- const erroredJobs = [];
+ const allxmlsToUpload = [];
+ const allErrors = [];
- const autoHouseObject = {
- AutoHouseExport: {
- RepairOrder: jobs.map((j) =>
- CreateRepairOrderTag(j, (job, error) => {
- erroredJobs.push({ job, error });
+ for (const bodyshop of bodyshops) {
+ const erroredJobs = [];
+ try {
+ const { jobs } = await client.request(queries.AUTOHOUSE_QUERY, {
+ bodyshopid: bodyshop.id,
+ });
+
+ const autoHouseObject = {
+ AutoHouseExport: {
+ RepairOrder: jobs.map((j) =>
+ CreateRepairOrderTag({ ...j, bodyshop }, function ({ job, error }) {
+ erroredJobs.push({ job: job, error: error.toString() });
+ })
+ ),
+ },
+ };
+
+ console.log(
+ "***Number of Failed jobs***: ",
+ erroredJobs.length,
+ JSON.stringify(erroredJobs.map((j) => j.job.ro_number))
+ );
+
+ var ret = builder
+ .create(autoHouseObject, {
+ version: "1.0",
+ encoding: "UTF-8",
})
- ),
- },
- };
+ .end({ pretty: true, allowEmptyTags: true });
- console.log(
- "***Number of Failed jobs***: ",
- erroredJobs.length,
- JSON.stringify(erroredJobs.map((x) => x.error))
- );
- var ret = builder
- .create(autoHouseObject, {
- version: "1.0",
- encoding: "UTF-8",
- })
- .end({ pretty: true, allowEmptyTags: true });
+ allxmlsToUpload.push({
+ xml: ret,
+ filename: `IM_${bodyshop.imexshopid}_${moment().format(
+ "DDMMYYYY_HHMMSS"
+ )}.xml`,
+ });
+ } catch (error) {
+ //Error at the shop level.
+ allErrors.push({
+ bodyshopid: bodyshop.id,
+ imexshopid: bodyshop.imexshopid,
+ fatal: true,
+ errors: [error.toString()],
+ });
+ } finally {
+ allErrors.push({
+ bodyshopid: bodyshop.id,
+ imexshopid: bodyshop.imexshopid,
+ errors: erroredJobs,
+ });
+ }
+ }
- //***TODO Change filing naming when creating the cron job. IM_ShopInternalName_DDMMYYYY_HHMMSS.xml
- res.type("application/xml");
- //res.sendFile(ret);
- res.send(ret);
+ let sftp = new Client();
+ try {
+ //Connect to the FTP and upload all.
+
+ await sftp.connect(ftpSetup);
+
+ for (const xmlObj of allxmlsToUpload) {
+ console.log("Uploading", xmlObj.filename);
+ const uploadResult = await sftp.put(
+ Buffer.from(xmlObj.xml),
+ `/${xmlObj.filename}`
+ );
+ console.log(
+ "🚀 ~ file: autohouse.js ~ line 94 ~ uploadResult",
+ uploadResult
+ );
+ }
+
+ //***TODO Change filing naming when creating the cron job. IM_ShopInternalName_DDMMYYYY_HHMMSS.xml
+ } catch (error) {
+ console.log("Error when connecting to FTP", error);
+ } finally {
+ sftp.end();
+ }
+
+ res.sendStatus(200);
};
const CreateRepairOrderTag = (job, errorCallback) => {
@@ -410,7 +477,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
return ret;
} catch (error) {
console.log("Error calculating job", error);
- errorCallback(job, error);
+ errorCallback({ job, error });
}
};
@@ -611,6 +678,3 @@ const generateNullDetailLine = () => {
EstimateAmount: null,
};
};
-
-const repairOpCodes = ["OP4", "OP9", "OP10"];
-const replaceOpCodes = ["OP2", "OP5", "OP11", "OP12"];
diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js
index 32db12ae6..62a7eb4d9 100644
--- a/server/graphql-client/queries.js
+++ b/server/graphql-client/queries.js
@@ -357,8 +357,8 @@ exports.QUERY_EMPLOYEE_PIN = `query QUERY_EMPLOYEE_PIN($shopId: uuid!, $employee
}
}`;
-exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz) {
- jobs(where: {_and: [{updated_at: {_gt: $start}}, {bodyshop: {autohouseid: {_is_null: false}}}]}) {
+exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshopid: uuid!) {
+ jobs(where: {_and: [{updated_at: {_gt: $start}}, {shopid: {_eq: $bodyshopid}}]}) {
id
ro_number
status
@@ -433,10 +433,10 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz) {
md_ro_statuses
md_order_statuses
autohouseid
- md_responsibility_centers
- jc_hourly_rates
+ md_responsibility_centers
+ jc_hourly_rates
}
- joblines (where:{removed: {_eq:false}}){
+ joblines(where: {removed: {_eq: false}}) {
id
line_no
status
@@ -452,40 +452,40 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz) {
part_type
oem_partno
lbr_op
- profitcenter_part
- profitcenter_labor
- billlines (order_by:{bill:{date:desc_nulls_last}}) {
+ profitcenter_part
+ profitcenter_labor
+ billlines(order_by: {bill: {date: desc_nulls_last}}) {
actual_cost
actual_price
quantity
bill {
- vendor{
+ vendor {
name
}
invoice_number
}
}
-
- } bills {
- id
- federal_tax_rate
- local_tax_rate
- state_tax_rate
- is_credit_memo
- billlines {
- actual_cost
- cost_center
- id
- quantity
- }
- }
- timetickets {
- id
- rate
+ }
+ bills {
+ id
+ federal_tax_rate
+ local_tax_rate
+ state_tax_rate
+ is_credit_memo
+ billlines {
+ actual_cost
cost_center
- actualhrs
- productivehrs
+ id
+ quantity
}
+ }
+ timetickets {
+ id
+ rate
+ cost_center
+ actualhrs
+ productivehrs
+ }
area_of_damage
employee_prep_rel {
first_name
@@ -507,6 +507,7 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz) {
}
}
}
+
`;
exports.UPDATE_JOB = `
@@ -906,3 +907,24 @@ exports.INSERT_IOEVENT = ` mutation INSERT_IOEVENT($event: ioevents_insert_input
}
}
`;
+
+exports.GET_AUTOHOUSE_SHOPS = `query GET_AUTOHOUSE_SHOPS {
+ bodyshops(where: {autohouseid: {_is_null: false}}){
+
+ id
+ shopname
+ address1
+ city
+ state
+ zip_post
+ country
+ phone
+ md_ro_statuses
+ md_order_statuses
+ autohouseid
+ md_responsibility_centers
+ jc_hourly_rates
+ imexshopid
+ }
+}
+`;
diff --git a/yarn.lock b/yarn.lock
index 53ba85566..52ad9c473 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -529,7 +529,7 @@ asap@^2.0.0:
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
-asn1@~0.2.3:
+asn1@^0.2.4, asn1@~0.2.3:
version "0.2.4"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
@@ -644,7 +644,7 @@ batch@^0.6.1:
resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=
-bcrypt-pbkdf@^1.0.0:
+bcrypt-pbkdf@^1.0.0, bcrypt-pbkdf@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
@@ -915,6 +915,16 @@ concat-stream@^1.4.7:
readable-stream "^2.2.2"
typedarray "^0.0.6"
+concat-stream@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1"
+ integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==
+ dependencies:
+ buffer-from "^1.0.0"
+ inherits "^2.0.3"
+ readable-stream "^3.0.2"
+ typedarray "^0.0.6"
+
concurrently@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-6.0.2.tgz#4ecdfc78a72a6f626a3a5d3c2a7a81962f3663e3"
@@ -999,6 +1009,13 @@ cors@2.8.5, cors@~2.8.5:
object-assign "^4"
vary "^1"
+cpu-features@0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/cpu-features/-/cpu-features-0.0.2.tgz#9f636156f1155fd04bdbaa028bb3c2fbef3cea7a"
+ integrity sha512-/2yieBqvMcRj8McNzkycjW2v3OIUOibBfd2dLEJ0nWts8NobAxwiyw9phVNS6oDL8x8tz9F7uNVFEVpJncQpeA==
+ dependencies:
+ nan "^2.14.1"
+
cross-fetch@^3.0.6:
version "3.1.4"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39"
@@ -1345,6 +1362,11 @@ entities@^2.0.0:
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
+err-code@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9"
+ integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==
+
error-ex@^1.3.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
@@ -2839,6 +2861,11 @@ ms@^2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+nan@^2.14.1, nan@^2.14.2:
+ version "2.14.2"
+ resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
+ integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
+
natural-compare@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
@@ -3136,6 +3163,14 @@ progress@^2.0.0:
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
+promise-retry@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22"
+ integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==
+ dependencies:
+ err-code "^2.0.2"
+ retry "^0.12.0"
+
protobufjs@^6.10.2, protobufjs@^6.8.6:
version "6.10.2"
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.10.2.tgz#b9cb6bd8ec8f87514592ba3fdfd28e93f33a469b"
@@ -3347,7 +3382,7 @@ readable-stream@2, readable-stream@^2.2.2, readable-stream@^2.3.5, readable-stre
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
-readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
+readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@@ -3431,7 +3466,7 @@ retry-request@^4.0.0, retry-request@^4.1.1:
dependencies:
debug "^4.1.1"
-retry@0.12.0:
+retry@0.12.0, retry@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
@@ -3766,6 +3801,26 @@ sprintf-js@~1.0.2:
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
+ssh2-sftp-client@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/ssh2-sftp-client/-/ssh2-sftp-client-7.0.0.tgz#38c3420319156d030a80ac9db1df7459d2ba42a0"
+ integrity sha512-o++ryEeSbAQ6GzjuXs6BHnST6zsoWUZYt9cLy6XQ4+WdL6jNuU6UjyQzvg1J3IgN4LpCSAI+9EyTeKeIb0AfSQ==
+ dependencies:
+ concat-stream "^2.0.0"
+ promise-retry "^2.0.1"
+ ssh2 "^1.1.0"
+
+ssh2@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.1.0.tgz#43dd24930e15e317687f519d6b40270d9cd00d00"
+ integrity sha512-CidQLG2ZacoT0Z7O6dOyisj4JdrOrLVJ4KbHjVNz9yI1vO08FAYQPcnkXY9BP8zeYo+J/nBgY6Gg4R7w4WFWtg==
+ dependencies:
+ asn1 "^0.2.4"
+ bcrypt-pbkdf "^1.0.2"
+ optionalDependencies:
+ cpu-features "0.0.2"
+ nan "^2.14.2"
+
sshpk@^1.7.0:
version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"