From 9a60149d7559d2ad4ecd0d74713c041ab1e578c1 Mon Sep 17 00:00:00 2001 From: Patrick Fic <> Date: Tue, 20 Jul 2021 10:34:03 -0700 Subject: [PATCH 1/7] IO-1266 attach pdf copy of email. --- bodyshop_translations.babel | 21 +++++ .../email-overlay/email-overlay.container.jsx | 72 ++++++++++----- .../shop-info/shop-info.general.component.jsx | 8 ++ client/src/graphql/bodyshop.queries.js | 2 + client/src/translations/en_us/common.json | 1 + client/src/translations/es/common.json | 1 + client/src/translations/fr/common.json | 1 + client/src/utils/RenderTemplate.js | 18 +++- .../down.yaml | 5 ++ .../up.yaml | 6 ++ .../down.yaml | 87 ++++++++++++++++++ .../up.yaml | 88 +++++++++++++++++++ .../down.yaml | 79 +++++++++++++++++ .../up.yaml | 80 +++++++++++++++++ hasura/migrations/metadata.yaml | 2 + server/email/sendemail.js | 3 +- 16 files changed, 451 insertions(+), 23 deletions(-) create mode 100644 hasura/migrations/1626795754549_alter_table_public_bodyshops_add_column_attach_pdf_to_email/down.yaml create mode 100644 hasura/migrations/1626795754549_alter_table_public_bodyshops_add_column_attach_pdf_to_email/up.yaml create mode 100644 hasura/migrations/1626795768315_update_permission_user_public_table_bodyshops/down.yaml create mode 100644 hasura/migrations/1626795768315_update_permission_user_public_table_bodyshops/up.yaml create mode 100644 hasura/migrations/1626795775694_update_permission_user_public_table_bodyshops/down.yaml create mode 100644 hasura/migrations/1626795775694_update_permission_user_public_table_bodyshops/up.yaml diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 3bc1ff5f1..46f5e9f32 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -3014,6 +3014,27 @@ + + attach_pdf_to_email + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + bill_federal_tax_rate false diff --git a/client/src/components/email-overlay/email-overlay.container.jsx b/client/src/components/email-overlay/email-overlay.container.jsx index e776a156e..f931728d9 100644 --- a/client/src/components/email-overlay/email-overlay.container.jsx +++ b/client/src/components/email-overlay/email-overlay.container.jsx @@ -43,6 +43,10 @@ export function EmailOverlayContainer({ const [loading, setLoading] = useState(false); const [sending, setSending] = useState(false); const [rawHtml, setRawHtml] = useState(""); + const [pdfCopytoAttach, setPdfCopytoAttach] = useState({ + filename: null, + pdf: null, + }); const [selectedMedia, setSelectedMedia] = useState([]); const defaultEmailFrom = { @@ -59,17 +63,17 @@ export function EmailOverlayContainer({ const handleFinish = async (values) => { logImEXEvent("email_send_from_modal"); - const attachments = []; + //const attachments = []; - if (values.fileList) - await asyncForEach(values.fileList, async (f) => { - const t = { - ContentType: f.type, - Filename: f.name, - Base64Content: (await toBase64(f.originFileObj)).split(",")[1], - }; - attachments.push(t); - }); + // if (values.fileList) + // await asyncForEach(values.fileList, async (f) => { + // const t = { + // ContentType: f.type, + // Filename: f.name, + // Base64Content: (await toBase64(f.originFileObj)).split(",")[1], + // }; + // attachments.push(t); + // }); setSending(true); try { @@ -77,11 +81,28 @@ export function EmailOverlayContainer({ ...defaultEmailFrom, ...values, html: rawHtml, - attachments: - values.fileList && - (await Promise.all( - values.fileList.map(async (f) => await toBase64(f.originFileObj)) - )), + attachments: [ + ...(values.fileList + ? await Promise.all( + values.fileList.map(async (f) => { + return { + filename: f.name, + path: await toBase64(f.originFileObj), + }; + }) + ) + : []), + ...(pdfCopytoAttach.pdf + ? [ + { + path: pdfCopytoAttach.pdf, + filename: + pdfCopytoAttach.filename && + `${pdfCopytoAttach.filename}.pdf`, + }, + ] + : []), + ], media: selectedMedia.filter((m) => m.isSelected).map((m) => m.src), //attachments, }); @@ -99,13 +120,22 @@ export function EmailOverlayContainer({ const render = async () => { logImEXEvent("email_render_template", { template: emailConfig.template }); setLoading(true); - let html = await RenderTemplate(emailConfig.template, bodyshop, true); + let { html, pdf, filename } = await RenderTemplate( + emailConfig.template, + bodyshop, + true + ); const response = await axios.post("/render/inlinecss", { html: html, url: `${window.location.protocol}://${window.location.host}/`, }); setRawHtml(response.data); + + if (pdf) { + setPdfCopytoAttach({ pdf, filename }); + } + form.setFieldsValue({ ...emailConfig.messageOptions, cc: @@ -166,8 +196,8 @@ const toBase64 = (file) => reader.onerror = (error) => reject(error); }); -const asyncForEach = async (array, callback) => { - for (let index = 0; index < array.length; index++) { - await callback(array[index], index, array); - } -}; +// const asyncForEach = async (array, callback) => { +// for (let index = 0; index < array.length; index++) { +// await callback(array[index], index, array); +// } +// }; diff --git a/client/src/components/shop-info/shop-info.general.component.jsx b/client/src/components/shop-info/shop-info.general.component.jsx index d7b40f1d8..26a92dcab 100644 --- a/client/src/components/shop-info/shop-info.general.component.jsx +++ b/client/src/components/shop-info/shop-info.general.component.jsx @@ -436,6 +436,14 @@ export default function ShopInfoGeneral({ form }) { > + + + + diff --git a/client/src/graphql/bodyshop.queries.js b/client/src/graphql/bodyshop.queries.js index 3916c029e..0b65d35d8 100644 --- a/client/src/graphql/bodyshop.queries.js +++ b/client/src/graphql/bodyshop.queries.js @@ -91,6 +91,7 @@ export const QUERY_BODYSHOP = gql` md_jobline_presets cdk_dealerid features + attach_pdf_to_email employees { id active @@ -178,6 +179,7 @@ export const UPDATE_SHOP = gql` jc_hourly_rates md_jobline_presets cdk_dealerid + attach_pdf_to_email employees { id first_name diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 20653eb85..8c444c2cd 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -199,6 +199,7 @@ "label": "Label" }, "appt_length": "Default Appointment Length", + "attach_pdf_to_email": "Attach PDF copy to sent emails?", "bill_federal_tax_rate": "Bills - Federal Tax Rate %", "bill_local_tax_rate": "Bill - Provincial/State Tax Rate %", "bill_state_tax_rate": "Bill - Provincial/State Tax Rate %", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 2b093063c..d8eb746a4 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -199,6 +199,7 @@ "label": "" }, "appt_length": "", + "attach_pdf_to_email": "", "bill_federal_tax_rate": "", "bill_local_tax_rate": "", "bill_state_tax_rate": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index a25172c23..811471037 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -199,6 +199,7 @@ "label": "" }, "appt_length": "", + "attach_pdf_to_email": "", "bill_federal_tax_rate": "", "bill_local_tax_rate": "", "bill_state_tax_rate": "", diff --git a/client/src/utils/RenderTemplate.js b/client/src/utils/RenderTemplate.js index 1e886fcdc..78452b1b6 100644 --- a/client/src/utils/RenderTemplate.js +++ b/client/src/utils/RenderTemplate.js @@ -8,6 +8,7 @@ import { setEmailOptions } from "../redux/email/email.actions"; import { store } from "../redux/store"; import client from "../utils/GraphQLClient"; import { TemplateList } from "./TemplateConstants"; +import _ from "lodash"; const server = process.env.REACT_APP_REPORTS_SERVER_URL; jsreport.serverUrl = server; @@ -39,8 +40,10 @@ export default async function RenderTemplate( offset: moment().utcOffset(), }, }; + try { const render = await jsreport.renderAsync(reportRequest); + if (!renderAsHtml) { render.download( (Templates[templateObject.name] && @@ -48,8 +51,21 @@ export default async function RenderTemplate( "" ); } else { + let pdf; + if (bodyshop.attach_pdf_to_email) { + const pdfRequest = _.cloneDeep(reportRequest); + pdfRequest.template.recipe = "chrome-pdf"; + const pdfRender = await jsreport.renderAsync(pdfRequest); + pdf = pdfRender.toDataURI(); + } return new Promise((resolve, reject) => { - resolve(render.toString()); + resolve({ + pdf, + filename: + Templates[templateObject.name] && + Templates[templateObject.name].title, + html: render.toString(), + }); }); } } catch (error) { diff --git a/hasura/migrations/1626795754549_alter_table_public_bodyshops_add_column_attach_pdf_to_email/down.yaml b/hasura/migrations/1626795754549_alter_table_public_bodyshops_add_column_attach_pdf_to_email/down.yaml new file mode 100644 index 000000000..611f63c09 --- /dev/null +++ b/hasura/migrations/1626795754549_alter_table_public_bodyshops_add_column_attach_pdf_to_email/down.yaml @@ -0,0 +1,5 @@ +- args: + cascade: false + read_only: false + sql: ALTER TABLE "public"."bodyshops" DROP COLUMN "attach_pdf_to_email"; + type: run_sql diff --git a/hasura/migrations/1626795754549_alter_table_public_bodyshops_add_column_attach_pdf_to_email/up.yaml b/hasura/migrations/1626795754549_alter_table_public_bodyshops_add_column_attach_pdf_to_email/up.yaml new file mode 100644 index 000000000..0730da959 --- /dev/null +++ b/hasura/migrations/1626795754549_alter_table_public_bodyshops_add_column_attach_pdf_to_email/up.yaml @@ -0,0 +1,6 @@ +- args: + cascade: false + read_only: false + sql: ALTER TABLE "public"."bodyshops" ADD COLUMN "attach_pdf_to_email" boolean + NOT NULL DEFAULT False; + type: run_sql diff --git a/hasura/migrations/1626795768315_update_permission_user_public_table_bodyshops/down.yaml b/hasura/migrations/1626795768315_update_permission_user_public_table_bodyshops/down.yaml new file mode 100644 index 000000000..24984ef56 --- /dev/null +++ b/hasura/migrations/1626795768315_update_permission_user_public_table_bodyshops/down.yaml @@ -0,0 +1,87 @@ +- args: + role: user + table: + name: bodyshops + schema: public + type: drop_select_permission +- args: + permission: + allow_aggregations: false + columns: + - accountingconfig + - address1 + - address2 + - appt_alt_transport + - appt_colors + - appt_length + - bill_tax_rates + - cdk_dealerid + - city + - country + - created_at + - default_adjustment_rate + - deliverchecklist + - email + - enforce_class + - enforce_referral + - features + - federal_tax_id + - id + - imexshopid + - inhousevendorid + - insurance_vendor_id + - intakechecklist + - jc_hourly_rates + - jobsizelimit + - logo_img_path + - md_categories + - md_ccc_rates + - md_classes + - md_hour_split + - md_ins_cos + - md_jobline_presets + - md_labor_rates + - md_messaging_presets + - md_notes_presets + - md_order_statuses + - md_parts_locations + - md_payment_types + - md_rbac + - md_referral_sources + - md_responsibility_centers + - md_ro_statuses + - messagingservicesid + - phone + - prodtargethrs + - production_config + - region_config + - schedule_end_time + - schedule_start_time + - scoreboard_target + - shopname + - shoprates + - speedprint + - ssbuckets + - state + - state_tax_id + - stripe_acct_id + - sub_status + - target_touchtime + - template_header + - textid + - updated_at + - use_fippa + - website + - workingdays + - zip_post + computed_fields: [] + filter: + associations: + user: + authid: + _eq: X-Hasura-User-Id + role: user + table: + name: bodyshops + schema: public + type: create_select_permission diff --git a/hasura/migrations/1626795768315_update_permission_user_public_table_bodyshops/up.yaml b/hasura/migrations/1626795768315_update_permission_user_public_table_bodyshops/up.yaml new file mode 100644 index 000000000..c8a09ddd4 --- /dev/null +++ b/hasura/migrations/1626795768315_update_permission_user_public_table_bodyshops/up.yaml @@ -0,0 +1,88 @@ +- args: + role: user + table: + name: bodyshops + schema: public + type: drop_select_permission +- args: + permission: + allow_aggregations: false + columns: + - accountingconfig + - address1 + - address2 + - appt_alt_transport + - appt_colors + - appt_length + - attach_pdf_to_email + - bill_tax_rates + - cdk_dealerid + - city + - country + - created_at + - default_adjustment_rate + - deliverchecklist + - email + - enforce_class + - enforce_referral + - features + - federal_tax_id + - id + - imexshopid + - inhousevendorid + - insurance_vendor_id + - intakechecklist + - jc_hourly_rates + - jobsizelimit + - logo_img_path + - md_categories + - md_ccc_rates + - md_classes + - md_hour_split + - md_ins_cos + - md_jobline_presets + - md_labor_rates + - md_messaging_presets + - md_notes_presets + - md_order_statuses + - md_parts_locations + - md_payment_types + - md_rbac + - md_referral_sources + - md_responsibility_centers + - md_ro_statuses + - messagingservicesid + - phone + - prodtargethrs + - production_config + - region_config + - schedule_end_time + - schedule_start_time + - scoreboard_target + - shopname + - shoprates + - speedprint + - ssbuckets + - state + - state_tax_id + - stripe_acct_id + - sub_status + - target_touchtime + - template_header + - textid + - updated_at + - use_fippa + - website + - workingdays + - zip_post + computed_fields: [] + filter: + associations: + user: + authid: + _eq: X-Hasura-User-Id + role: user + table: + name: bodyshops + schema: public + type: create_select_permission diff --git a/hasura/migrations/1626795775694_update_permission_user_public_table_bodyshops/down.yaml b/hasura/migrations/1626795775694_update_permission_user_public_table_bodyshops/down.yaml new file mode 100644 index 000000000..b09c84f9e --- /dev/null +++ b/hasura/migrations/1626795775694_update_permission_user_public_table_bodyshops/down.yaml @@ -0,0 +1,79 @@ +- args: + role: user + table: + name: bodyshops + schema: public + type: drop_update_permission +- args: + permission: + columns: + - accountingconfig + - address1 + - address2 + - appt_alt_transport + - appt_colors + - appt_length + - bill_tax_rates + - city + - country + - created_at + - default_adjustment_rate + - deliverchecklist + - email + - enforce_class + - enforce_referral + - federal_tax_id + - id + - inhousevendorid + - insurance_vendor_id + - intakechecklist + - jc_hourly_rates + - logo_img_path + - md_categories + - md_ccc_rates + - md_classes + - md_hour_split + - md_ins_cos + - md_jobline_presets + - md_labor_rates + - md_messaging_presets + - md_notes_presets + - md_order_statuses + - md_parts_locations + - md_payment_types + - md_rbac + - md_referral_sources + - md_responsibility_centers + - md_ro_statuses + - phone + - prodtargethrs + - production_config + - schedule_end_time + - schedule_start_time + - scoreboard_target + - shopname + - shoprates + - speedprint + - ssbuckets + - state + - state_tax_id + - target_touchtime + - updated_at + - use_fippa + - website + - workingdays + - zip_post + filter: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + set: {} + role: user + table: + name: bodyshops + schema: public + type: create_update_permission diff --git a/hasura/migrations/1626795775694_update_permission_user_public_table_bodyshops/up.yaml b/hasura/migrations/1626795775694_update_permission_user_public_table_bodyshops/up.yaml new file mode 100644 index 000000000..adce758ee --- /dev/null +++ b/hasura/migrations/1626795775694_update_permission_user_public_table_bodyshops/up.yaml @@ -0,0 +1,80 @@ +- args: + role: user + table: + name: bodyshops + schema: public + type: drop_update_permission +- args: + permission: + columns: + - accountingconfig + - address1 + - address2 + - appt_alt_transport + - appt_colors + - appt_length + - attach_pdf_to_email + - bill_tax_rates + - city + - country + - created_at + - default_adjustment_rate + - deliverchecklist + - email + - enforce_class + - enforce_referral + - federal_tax_id + - id + - inhousevendorid + - insurance_vendor_id + - intakechecklist + - jc_hourly_rates + - logo_img_path + - md_categories + - md_ccc_rates + - md_classes + - md_hour_split + - md_ins_cos + - md_jobline_presets + - md_labor_rates + - md_messaging_presets + - md_notes_presets + - md_order_statuses + - md_parts_locations + - md_payment_types + - md_rbac + - md_referral_sources + - md_responsibility_centers + - md_ro_statuses + - phone + - prodtargethrs + - production_config + - schedule_end_time + - schedule_start_time + - scoreboard_target + - shopname + - shoprates + - speedprint + - ssbuckets + - state + - state_tax_id + - target_touchtime + - updated_at + - use_fippa + - website + - workingdays + - zip_post + filter: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + set: {} + role: user + table: + name: bodyshops + schema: public + type: create_update_permission diff --git a/hasura/migrations/metadata.yaml b/hasura/migrations/metadata.yaml index 3481eec42..bd0c4200f 100644 --- a/hasura/migrations/metadata.yaml +++ b/hasura/migrations/metadata.yaml @@ -756,6 +756,7 @@ tables: - appt_alt_transport - appt_colors - appt_length + - attach_pdf_to_email - bill_tax_rates - cdk_dealerid - city @@ -831,6 +832,7 @@ tables: - appt_alt_transport - appt_colors - appt_length + - attach_pdf_to_email - bill_tax_rates - city - country diff --git a/server/email/sendemail.js b/server/email/sendemail.js index 6f54b00f1..39acfaeac 100644 --- a/server/email/sendemail.js +++ b/server/email/sendemail.js @@ -48,7 +48,8 @@ exports.sendEmail = async (req, res) => { ...((req.body.attachments && req.body.attachments.map((a) => { return { - path: a, + filename: a.filename, + path: a.path, }; })) || []), From 3e1663bf18c0e31c5db3e20dea3c200e214cc1e4 Mon Sep 17 00:00:00 2001 From: Patrick Fic <> Date: Tue, 20 Jul 2021 10:36:49 -0700 Subject: [PATCH 2/7] IO-1267 missing query info in detail cards. --- client/src/graphql/jobs.queries.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js index 7a8974a5b..8bbcc6e1a 100644 --- a/client/src/graphql/jobs.queries.js +++ b/client/src/graphql/jobs.queries.js @@ -689,6 +689,8 @@ export const QUERY_JOB_CARD_DETAILS = gql` v_make_desc v_model_desc v_color + v_vin + plate_st plate_no vehicle { id From 3b992edc21ca7c25ad1158f80f710d34dd4e4a4c Mon Sep 17 00:00:00 2001 From: Patrick Fic <> Date: Tue, 20 Jul 2021 16:25:08 -0700 Subject: [PATCH 3/7] IO-594 Create schedulable AH export. --- .platform/nginx/conf.d/proxy.conf | 2 +- bodyshop_translations.babel | 21 +++ .../email-overlay/email-overlay.component.jsx | 4 +- client/src/translations/en_us/common.json | 1 + client/src/translations/es/common.json | 1 + client/src/translations/fr/common.json | 1 + package.json | 1 + server.js | 2 +- server/data/autohouse.js | 124 +++++++++++++----- server/graphql-client/queries.js | 78 +++++++---- yarn.lock | 63 ++++++++- 11 files changed, 233 insertions(+), 65 deletions(-) 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" From f5003080db8df2e8d978c339ea8b2b9491a0caea Mon Sep 17 00:00:00 2001 From: Patrick Fic <> Date: Tue, 20 Jul 2021 16:37:19 -0700 Subject: [PATCH 4/7] Removed unnecessary import. --- client/src/components/email-overlay/email-overlay.component.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/email-overlay/email-overlay.component.jsx b/client/src/components/email-overlay/email-overlay.component.jsx index 756f7b45f..d22000152 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, Typography, Upload } from "antd"; +import { Divider, Form, Input, Select, Tabs, Upload } from "antd"; import React from "react"; import { useTranslation } from "react-i18next"; import EmailDocumentsComponent from "../email-documents/email-documents.component"; From 27d9322ced3ac0653ff999362446938d7d90f8dd Mon Sep 17 00:00:00 2001 From: Patrick Fic <> Date: Wed, 21 Jul 2021 08:50:29 -0700 Subject: [PATCH 5/7] IO-594 Include AH Requested changes --- server/data/autohouse.js | 12 ++++++++---- server/graphql-client/queries.js | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/server/data/autohouse.js b/server/data/autohouse.js index 64a7bf6cd..bed1e2a1f 100644 --- a/server/data/autohouse.js +++ b/server/data/autohouse.js @@ -125,9 +125,9 @@ const CreateRepairOrderTag = (job, errorCallback) => { const ret = { RepairOrderInformation: { ShopInternalName: job.bodyshop.autohouseid, - ID: job.id, + ID: parseInt(job.ro_number.match(/\d/g).join(""), 10), RO: job.ro_number, - Est: job.id, //We no longer use estimate id. + Est: parseInt(job.ro_number.match(/\d/g).join(""), 10), //We no longer use estimate id. GUID: job.id, TransType: StatusMapping(job.status, job.bodyshop.md_ro_statuses), ShopName: job.bodyshop.shopname, @@ -136,8 +136,12 @@ const CreateRepairOrderTag = (job, errorCallback) => { ShopState: job.bodyshop.state, ShopZip: job.bodyshop.zip_post, ShopPhone: job.bodyshop.phone, - EstimatorID: `${job.est_ct_fn || ""} ${job.est_ct_ln || ""}`, - EstimatorName: `${job.est_ct_fn || ""} ${job.est_ct_ln || ""}`, + EstimatorID: `${job.est_ct_ln ? job.est_ct_ln : ""}${ + job.est_ct_ln ? ", " : "" + }${job.est_ct_fn ? job.est_ct_fn : ""}`, + EstimatorName: `${job.est_ct_ln ? job.est_ct_ln : ""}${ + job.est_ct_ln ? ", " : "" + }${job.est_ct_fn ? job.est_ct_fn : ""}`, }, CustomerInformation: { FirstName: job.ownr_fn, diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index 62a7eb4d9..d9d66aeca 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -358,7 +358,7 @@ exports.QUERY_EMPLOYEE_PIN = `query QUERY_EMPLOYEE_PIN($shopId: uuid!, $employee }`; exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshopid: uuid!) { - jobs(where: {_and: [{updated_at: {_gt: $start}}, {shopid: {_eq: $bodyshopid}}]}) { + jobs(where: {_and: [{converted :{_eq: true}},{updated_at: {_gt: $start}}, {shopid: {_eq: $bodyshopid}}]}) { id ro_number status From ae4a86453307e972e20c9105ab679b838f2c3d61 Mon Sep 17 00:00:00 2001 From: Patrick Fic <> Date: Wed, 21 Jul 2021 11:19:23 -0700 Subject: [PATCH 6/7] IO-1264 IO-1271 Report Center additions & format. --- bodyshop_translations.babel | 126 ++++++++++++++++++ .../job-3rd-party-modal.component.jsx | 2 +- .../report-center-modal.component.jsx | 25 +++- .../report-center-modal.container.jsx | 2 +- .../report-center-modal.styles.scss | 11 ++ client/src/translations/en_us/common.json | 8 +- client/src/translations/es/common.json | 6 + client/src/translations/fr/common.json | 6 + client/src/utils/TemplateConstants.js | 73 ++++++++++ 9 files changed, 249 insertions(+), 10 deletions(-) create mode 100644 client/src/components/report-center-modal/report-center-modal.styles.scss diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index e407d53ed..2f1cf0bf6 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -29998,6 +29998,27 @@ + + refnumber + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + sendtype false @@ -32841,6 +32862,27 @@ + + hours_sold_detail_closed_csr + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + hours_sold_detail_closed_ins_co false @@ -32883,6 +32925,27 @@ + + hours_sold_detail_open_csr + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + hours_sold_detail_open_ins_co false @@ -32925,6 +32988,27 @@ + + hours_sold_summary_closed_csr + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + hours_sold_summary_closed_ins_co false @@ -32967,6 +33051,27 @@ + + hours_sold_summary_open_csr + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + hours_sold_summary_open_ins_co false @@ -32988,6 +33093,27 @@ + + job_costing_ro_csr + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + job_costing_ro_date_detail false diff --git a/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx b/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx index f597d156e..2954bbe0d 100644 --- a/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx +++ b/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx @@ -161,7 +161,7 @@ export function Jobd3RdPartyModal({ bodyshop, jobId }) { diff --git a/client/src/components/report-center-modal/report-center-modal.component.jsx b/client/src/components/report-center-modal/report-center-modal.component.jsx index 51444f1e3..75cbf0454 100644 --- a/client/src/components/report-center-modal/report-center-modal.component.jsx +++ b/client/src/components/report-center-modal/report-center-modal.component.jsx @@ -1,5 +1,5 @@ import { useLazyQuery } from "@apollo/client"; -import { Button, DatePicker, Form, Radio } from "antd"; +import { Button, DatePicker, Form, Radio, Space } from "antd"; import moment from "moment"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; @@ -13,6 +13,7 @@ import { GenerateDocument } from "../../utils/RenderTemplate"; import { TemplateList } from "../../utils/TemplateConstants"; import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component"; import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component"; +import "./report-center-modal.styles.scss"; const mapStateToProps = createStructuredSelector({ reportCenterModal: selectReportCenter, @@ -86,6 +87,7 @@ export function ReportCenterModalComponent({ reportCenterModal }) { - - {Object.keys(Templates).map((key) => ( - - {Templates[key].title} - - ))} + + + {Object.keys(Templates).map((key) => ( + + {Templates[key].title} + + ))} + diff --git a/client/src/components/report-center-modal/report-center-modal.container.jsx b/client/src/components/report-center-modal/report-center-modal.container.jsx index ea8167e7d..f0d361785 100644 --- a/client/src/components/report-center-modal/report-center-modal.container.jsx +++ b/client/src/components/report-center-modal/report-center-modal.container.jsx @@ -31,7 +31,7 @@ export function ReportCenterModalContainer({ onCancel={() => toggleModalVisible()} cancelButtonProps={{ style: { display: "none" } }} destroyOnClose - width="60%" + width="80%" > diff --git a/client/src/components/report-center-modal/report-center-modal.styles.scss b/client/src/components/report-center-modal/report-center-modal.styles.scss new file mode 100644 index 000000000..ccd2dcfee --- /dev/null +++ b/client/src/components/report-center-modal/report-center-modal.styles.scss @@ -0,0 +1,11 @@ +.radio-group-columns { + .ant-radio-group { + // display: block; + } + .ant-radio-wrapper { + display: block; + span { + word-wrap: break-word; + } + } +} diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 0a7420617..58a8bfd4c 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -1809,6 +1809,7 @@ "depreciation": "Depreciation", "other": "Other", "ponumber": "PO Number", + "refnumber": "Reference Number", "sendtype": "Send by", "state": "Province/State", "zip": "Postal Code/Zip" @@ -1971,19 +1972,24 @@ "gsr_by_delivery_date": "Gross Sales by Delivery Date", "gsr_by_estimator": "Gross Sales by Estimator", "gsr_by_exported_date": "Gross Sales by Export Date", - "gsr_by_ins_co": "Gross Sales by Insurance Company'", + "gsr_by_ins_co": "Gross Sales by Insurance Company", "gsr_by_make": "Gross Sales by Vehicle Make", "gsr_by_referral": "Gross Sales by Referral Source", "gsr_by_ro": "Gross Sales by RO", "gsr_labor_only": "Gross Sales - Labor Only", "hours_sold_detail_closed": "Hours Sold Detail - Closed", + "hours_sold_detail_closed_csr": "Hours Sold Detail - Closed by CSR", "hours_sold_detail_closed_ins_co": "Hours Sold Detail - Closed by Source", "hours_sold_detail_open": "Hours Sold Detail - Open", + "hours_sold_detail_open_csr": "Hours Sold Detail - Open by CSR", "hours_sold_detail_open_ins_co": "Hours Sold Detail - Open by Source", "hours_sold_summary_closed": "Hours Sold Summary - Closed", + "hours_sold_summary_closed_csr": "Hours Sold Summary - Closed by CSR", "hours_sold_summary_closed_ins_co": "Hours Sold Summary - Closed by Source", "hours_sold_summary_open": "Hours Sold Summary - Open", + "hours_sold_summary_open_csr": "Hours Sold Summary - Open CSR", "hours_sold_summary_open_ins_co": "Hours Sold Summary - Open by Source", + "job_costing_ro_csr": "Job Costing by CSR", "job_costing_ro_date_detail": "Job Costing by RO - Detail", "job_costing_ro_date_summary": "Job Costing by RO - Summary", "job_costing_ro_estimator": "Job Costing by Estimator", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 99407b22c..8eeb24895 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -1809,6 +1809,7 @@ "depreciation": "", "other": "", "ponumber": "", + "refnumber": "", "sendtype": "", "state": "", "zip": "" @@ -1977,13 +1978,18 @@ "gsr_by_ro": "", "gsr_labor_only": "", "hours_sold_detail_closed": "", + "hours_sold_detail_closed_csr": "", "hours_sold_detail_closed_ins_co": "", "hours_sold_detail_open": "", + "hours_sold_detail_open_csr": "", "hours_sold_detail_open_ins_co": "", "hours_sold_summary_closed": "", + "hours_sold_summary_closed_csr": "", "hours_sold_summary_closed_ins_co": "", "hours_sold_summary_open": "", + "hours_sold_summary_open_csr": "", "hours_sold_summary_open_ins_co": "", + "job_costing_ro_csr": "", "job_costing_ro_date_detail": "", "job_costing_ro_date_summary": "", "job_costing_ro_estimator": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 003fb315f..33c8630d5 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -1809,6 +1809,7 @@ "depreciation": "", "other": "", "ponumber": "", + "refnumber": "", "sendtype": "", "state": "", "zip": "" @@ -1977,13 +1978,18 @@ "gsr_by_ro": "", "gsr_labor_only": "", "hours_sold_detail_closed": "", + "hours_sold_detail_closed_csr": "", "hours_sold_detail_closed_ins_co": "", "hours_sold_detail_open": "", + "hours_sold_detail_open_csr": "", "hours_sold_detail_open_ins_co": "", "hours_sold_summary_closed": "", + "hours_sold_summary_closed_csr": "", "hours_sold_summary_closed_ins_co": "", "hours_sold_summary_open": "", + "hours_sold_summary_open_csr": "", "hours_sold_summary_open_ins_co": "", + "job_costing_ro_csr": "", "job_costing_ro_date_detail": "", "job_costing_ro_date_summary": "", "job_costing_ro_estimator": "", diff --git a/client/src/utils/TemplateConstants.js b/client/src/utils/TemplateConstants.js index b6627b452..814ca715b 100644 --- a/client/src/utils/TemplateConstants.js +++ b/client/src/utils/TemplateConstants.js @@ -752,6 +752,67 @@ export const TemplateList = (type, context) => { field: i18n.t("jobs.fields.date_open"), }, }, + + hours_sold_detail_closed_csr: { + title: i18n.t( + "reportcenter.templates.hours_sold_detail_closed_csr" + ), + description: "", + subject: i18n.t( + "reportcenter.templates.hours_sold_detail_closed_csr" + ), + key: "hours_sold_detail_closed_csr", + //idtype: "vendor", + disabled: false, + rangeFilter: { + object: i18n.t("reportcenter.labels.objects.jobs"), + field: i18n.t("jobs.fields.date_invoiced"), + }, + }, + hours_sold_detail_open_csr: { + title: i18n.t("reportcenter.templates.hours_sold_detail_open_csr"), + description: "", + subject: i18n.t( + "reportcenter.templates.hours_sold_detail_open_csr" + ), + key: "hours_sold_detail_open_csr", + //idtype: "vendor", + disabled: false, + rangeFilter: { + object: i18n.t("reportcenter.labels.objects.jobs"), + field: i18n.t("jobs.fields.date_open"), + }, + }, + hours_sold_summary_closed_csr: { + title: i18n.t( + "reportcenter.templates.hours_sold_summary_closed_csr" + ), + description: "", + subject: i18n.t( + "reportcenter.templates.hours_sold_summary_closed_csr" + ), + key: "hours_sold_summary_closed_csr", + //idtype: "vendor", + disabled: false, + rangeFilter: { + object: i18n.t("reportcenter.labels.objects.jobs"), + field: i18n.t("jobs.fields.date_invoiced"), + }, + }, + hours_sold_summary_open_csr: { + title: i18n.t("reportcenter.templates.hours_sold_summary_open_csr"), + description: "", + subject: i18n.t( + "reportcenter.templates.hours_sold_summary_open_csr" + ), + key: "hours_sold_summary_open_csr", + //idtype: "vendor", + disabled: false, + rangeFilter: { + object: i18n.t("reportcenter.labels.objects.jobs"), + field: i18n.t("jobs.fields.date_invoiced"), + }, + }, estimator_detail: { title: i18n.t("reportcenter.templates.estimator_detail"), description: "", @@ -814,6 +875,18 @@ export const TemplateList = (type, context) => { field: i18n.t("jobs.fields.date_invoiced"), }, }, + job_costing_ro_csr: { + title: i18n.t("reportcenter.templates.job_costing_ro_csr"), + description: "", + subject: i18n.t("reportcenter.templates.job_costing_ro_csr"), + key: "job_costing_ro_csr", + //idtype: "vendor", + disabled: false, + rangeFilter: { + object: i18n.t("reportcenter.labels.objects.jobs"), + field: i18n.t("jobs.fields.date_open"), + }, + }, job_costing_ro_ins_co: { title: i18n.t("reportcenter.templates.job_costing_ro_ins_co"), description: "", From 098754125bf051a956df1018c613ce0a016244f4 Mon Sep 17 00:00:00 2001 From: Patrick Fic <> Date: Wed, 21 Jul 2021 13:11:51 -0700 Subject: [PATCH 7/7] IO-594 Add AH Settings --- server/data/autohouse.js | 163 +++++++++++++++++++++------------------ 1 file changed, 89 insertions(+), 74 deletions(-) diff --git a/server/data/autohouse.js b/server/data/autohouse.js index bed1e2a1f..f8873c684 100644 --- a/server/data/autohouse.js +++ b/server/data/autohouse.js @@ -30,90 +30,105 @@ const ftpSetup = { exports.default = async (req, res) => { //Query for the List of Bodyshop Clients. + console.log("Starting Autohouse datapump request."); const { bodyshops } = await client.request(queries.GET_AUTOHOUSE_SHOPS); const allxmlsToUpload = []; const allErrors = []; - - 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 }); - - 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, - }); - } - } - - let sftp = new Client(); try { - //Connect to the FTP and upload all. + for (const bodyshop of bodyshops) { + console.log("Starting extract for ", bodyshop.shopname); + const erroredJobs = []; + try { + const { jobs } = await client.request(queries.AUTOHOUSE_QUERY, { + bodyshopid: bodyshop.id, + }); - await sftp.connect(ftpSetup); + const autoHouseObject = { + AutoHouseExport: { + RepairOrder: jobs.map((j) => + CreateRepairOrderTag( + { ...j, bodyshop }, + function ({ job, error }) { + erroredJobs.push({ job: job, error: error.toString() }); + } + ) + ), + }, + }; - 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 - ); + console.log( + bodyshop.shopname, + "***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 }); + + allxmlsToUpload.push({ + xml: ret, + filename: `IM_${bodyshop.imexshopid}_${moment().format( + "DDMMYYYY_HHMMSS" + )}.xml`, + }); + + console.log("Finished extract for shop ", bodyshop.shopname); + } catch (error) { + //Error at the shop level. + console.log("Error at shop level", bodyshop.shopname, error); + 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 - } catch (error) { - console.log("Error when connecting to FTP", error); - } finally { - sftp.end(); - } + let sftp = new Client(); + sftp.on("error", (errors) => + console.log("Error in FTP client", JSON.stringify(errors)) + ); + try { + //Connect to the FTP and upload all. - res.sendStatus(200); + 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); + } catch (error) { + res.JSON(error).sendStatus(500); + } }; const CreateRepairOrderTag = (job, errorCallback) => {