From b2626da09d43b036609637af58fcbdbc383c7a74 Mon Sep 17 00:00:00 2001 From: Patrick Fic <> Date: Fri, 19 Feb 2021 16:43:28 -0800 Subject: [PATCH] Begin Autohouse Integration IO-594 --- .eslintrc.json | 21 +- .../down.yaml | 5 + .../up.yaml | 5 + server.js | 3 + server/data/autohouse.js | 399 ++++++++++++++++++ server/data/data.js | 1 + server/graphql-client/queries.js | 126 ++++++ 7 files changed, 549 insertions(+), 11 deletions(-) create mode 100644 hasura/migrations/1613772179401_alter_table_public_bodyshops_add_column_autohouseid/down.yaml create mode 100644 hasura/migrations/1613772179401_alter_table_public_bodyshops_add_column_autohouseid/up.yaml create mode 100644 server/data/autohouse.js create mode 100644 server/data/data.js diff --git a/.eslintrc.json b/.eslintrc.json index 6f00e3152..8eb0cfae8 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,13 +1,12 @@ { - "env": { - "browser": true, - "commonjs": true, - "es2021": true - }, - "extends": "eslint:recommended", - "parserOptions": { - "ecmaVersion": 12 - }, - "rules": { - } + "env": { + "browser": false, + "commonjs": true, + "es2021": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 12 + }, + "rules": {} } diff --git a/hasura/migrations/1613772179401_alter_table_public_bodyshops_add_column_autohouseid/down.yaml b/hasura/migrations/1613772179401_alter_table_public_bodyshops_add_column_autohouseid/down.yaml new file mode 100644 index 000000000..7e2c379bf --- /dev/null +++ b/hasura/migrations/1613772179401_alter_table_public_bodyshops_add_column_autohouseid/down.yaml @@ -0,0 +1,5 @@ +- args: + cascade: false + read_only: false + sql: ALTER TABLE "public"."bodyshops" DROP COLUMN "autohouseid"; + type: run_sql diff --git a/hasura/migrations/1613772179401_alter_table_public_bodyshops_add_column_autohouseid/up.yaml b/hasura/migrations/1613772179401_alter_table_public_bodyshops_add_column_autohouseid/up.yaml new file mode 100644 index 000000000..b710cc316 --- /dev/null +++ b/hasura/migrations/1613772179401_alter_table_public_bodyshops_add_column_autohouseid/up.yaml @@ -0,0 +1,5 @@ +- args: + cascade: false + read_only: false + sql: ALTER TABLE "public"."bodyshops" ADD COLUMN "autohouseid" text NULL UNIQUE; + type: run_sql diff --git a/server.js b/server.js index 617a3b8ba..639b623bb 100644 --- a/server.js +++ b/server.js @@ -133,6 +133,9 @@ var qbo = require("./server/accounting/qbo/qbo"); 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.get("/", async function (req, res) { res.status(200).send("Access Forbidden."); }); diff --git a/server/data/autohouse.js b/server/data/autohouse.js new file mode 100644 index 000000000..c87aa3337 --- /dev/null +++ b/server/data/autohouse.js @@ -0,0 +1,399 @@ +const GraphQLClient = require("graphql-request").GraphQLClient; +const path = require("path"); +const queries = require("../graphql-client/queries"); +const Dinero = require("dinero.js"); +const moment = require("moment"); +var builder = require("xmlbuilder"); + +require("dotenv").config({ + path: path.resolve( + process.cwd(), + `.env.${process.env.NODE_ENV || "development"}` + ), +}); +const client = require("../graphql-client/graphql-client").client; + +const AhDateFormat = "MMDDYYYY"; + +exports.default = async (req, res) => { + //Get Client Dataset. + const { jobs } = await client.request(queries.AUTOHOUSE_QUERY); + + const autoHouseObject = { + AutoHouseExport: { RepairOrder: jobs.map((j) => CreateRepairOrderTag(j)) }, + }; + + var ret = builder + .create(autoHouseObject, { + version: "1.0", + encoding: "UTF-8", + headless: false, + }) + .end({ pretty: true }); + + res.type("application/xml"); + res.send(ret); +}; + +const CreateRepairOrderTag = (job) => { + //Level 2 + const ret = { + RepairOrderInformation: { + ShopInternalName: job.bodyshop.autohouseid, + ID: job.id, + RO: job.ro_number, + Est: job.id, //We no longer use estimate id. + GUID: job.id, + TransType: StatusMapping(job.status, job.bodyshop.md_ro_statuses), + ShopName: job.bodyshop.shopname, + ShopAddress: job.bodyshop.address1, + ShopCity: job.bodyshop.city, + 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}`, + }, + CustomerInformation: { + FirstName: null, + LastName: null, + Street: null, + City: null, + State: null, + Zip: "N6G", + Phone1: null, + Phone2: null, + Phone2Extension: null, + Phone3: null, + Phone3Extension: null, + FileComments: null, + Source: null, + Email: null, + RetWhsl: null, + Cat: null, + InsuredorClaimantFlag: null, + }, + VehicleInformation: { + Year: job.v_model_yr, + Make: job.v_make_desc, + Model: job.v_model_desc, + VIN: job.v_vin, + License: job.plate_no, + MileageIn: job.kmin, + Vehiclecolor: job.v_color, + VehicleProductionDate: null, + VehiclePaintCode: null, + VehicleTrimCode: null, + VehicleBodyStyle: null, + DriveableFlag: job.tlos_ind ? "Y" : "N", + }, + + InsuranceInformation: { + InsuranceCo: job.ins_co_nm, + CompanyName: job.ins_co_nm, + Address: job.ins_addr1, + City: job.ins_addr1, + State: job.ins_city, + Zip: job.ins_zip, + Phone: job.ins_ph1, + Fax: null, + ClaimType: null, + LossType: null, + Policy: null, + Claim: job.clm_no, + InsuredLastName: null, + InsuredFirstName: null, + ClaimantLastName: null, + ClaimantFirstName: null, + Assignment: null, + InsuranceAgentLastName: null, + InsuranceAgentFirstName: null, + InsAgentPhone: null, + InsideAdjuster: null, + OutsideAdjuster: null, + }, + Dates: { + DateofLoss: job.loss_date && moment(job.loss_date).format(AhDateFormat), + InitialCustomerContactDate: null, + FirstFollowUpDate: null, + ReferralDate: null, + EstimateAppointmentDate: null, + SecondFollowUpDate: null, + AssignedDate: null, + EstComplete: null, + CustomerAuthorizationDate: null, + InsuranceAuthorizationDate: null, + DateOpened: job.date_open && moment(job.date_open).format(AhDateFormat), + ScheduledArrivalDate: + job.scheduled_in && moment(job.scheduled_in).format(AhDateFormat), + CarinShop: job.actual_in && moment(job.actual_in).format(AhDateFormat), + InsInspDate: null, + StartDate: null, + PartsOrder: null, + TeardownHold: null, + SupplementSubmittedDate: null, + SupplementApprovedDate: null, + AssntoBody: null, + AssntoMech: null, + AssntoPaint: null, + AssntoDetail: null, + PromiseDate: + job.scheduled_completion && + moment(job.scheduled_completion).format(AhDateFormat), + InsuranceTargetOut: null, + CarComplete: + job.actual_completion && + moment(job.actual_completion).format(AhDateFormat), + DeliveryAppointmentDate: + job.scheduled_delivery && + moment(job.scheduled_delivery).format(AhDateFormat), + DateClosed: + job.date_invoiced && moment(job.date_invoiced).format(AhDateFormat), + CustomerPaidInFullDate: null, + InsurancePaidInFullDate: null, + CustPickup: + job.actual_delivery && moment(job.actual_delivery).format(AhDateFormat), + AccountPostedDate: + job.date_exported && moment(job.date_exported).format(AhDateFormat), + CSIProcessedDate: null, + ThankYouLetterSent: null, + AdditionalFollowUpDate: null, + }, + Rates: { + BodyRate: job.rate_lab, + RefinishRate: job.rate_lar, + MechanicalRate: job.rate_lam, + StructuralRate: job.rate_las, + PMRate: job.rate_mapa, + BMRate: job.rate_mash, + TaxRate: null, + StorageRateperDay: null, + DaysStored: null, + }, + EstimateTotals: { + BodyHours: null, + RefinishHours: null, + MechanicalHours: null, + StructuralHours: null, + PartsTotal: null, + PartsOEM: null, + PartsAM: null, + PartsReconditioned: null, + PartsRecycled: null, + PartsOther: null, + SubletTotal: null, + BodyLaborTotal: null, + RefinishLaborTotal: null, + MechanicalLaborTotal: null, + StructuralLaborTotal: null, + MiscellaneousChargeTotal: null, + PMTotal: null, + BMTotal: null, + MiscTotal: null, + TowingTotal: null, + StorageTotal: null, + DetailTotal: null, + SalesTaxTotal: null, + GrossTotal: null, + DeductibleTotal: null, + DepreciationTotal: null, + Discount: null, + CustomerPay: null, + InsurancePay: null, + Deposit: null, + AmountDue: null, + }, + SupplementTotals: { + BodyHours: null, + RefinishHours: null, + MechanicalHours: null, + StructuralHours: null, + PartsTotal: null, + PartsOEM: null, + PartsAM: null, + PartsReconditioned: null, + PartsRecycled: null, + PartsOther: null, + SubletTotal: null, + BodyLaborTotal: null, + RefinishLaborTotal: null, + MechanicalLaborTotal: null, + StructuralLaborTotal: null, + MiscellaneousChargeTotal: null, + PMTotal: null, + BMTotal: null, + MiscTotal: null, + TowingTotal: null, + StorageTotal: null, + DetailTotal: null, + SalesTaxTotal: null, + GrossTotal: null, + DeductibleTotal: null, + DepreciationTotal: null, + Discount: null, + CustomerPay: null, + InsurancePay: null, + Deposit: null, + AmountDue: null, + }, + RevisedTotals: { + BodyHours: "10.10", + RefinishHours: "4.70", + MechanicalHours: "2.90", + StructuralHours: null, + PartsTotal: "2630.24", + PartsTotalCost: "1655.67", + PartsOEM: "969.49", + PartsOEMCost: "761.91", + PartsAM: "1660.75", + PartsAMCost: "893.76", + PartsReconditioned: null, + PartsReconditionedCost: null, + PartsRecycled: null, + PartsRecycledCost: null, + PartsOther: null, + PartsOtherCost: null, + SubletTotal: "139.95", + SubletTotalCost: "0.00", + BodyLaborTotal: "642.46", + BodyLaborTotalCost: "0.00", + RefinishLaborTotal: "298.97", + RefinishLaborTotalCost: "0.00", + MechanicalLaborTotal: "276.69", + MechanicalLaborTotalCost: "0.00", + StructuralLaborTotal: null, + StructuralLaborTotalCost: null, + MiscellaneousChargeTotal: null, + MiscellaneousChargeTotalCost: null, + PMTotal: "159.42", + PMTotalCost: "0.00", + BMTotal: "40.30", + BMTotalCost: "36.27", + MiscTotal: "60.00", + MiscTotalCost: "9.00", + TowingTotal: null, + TowingTotalCost: null, + StorageTotal: null, + StorageTotalCost: null, + DetailTotal: null, + DetailTotalCost: null, + SalesTaxTotal: "552.24", + SalesTaxTotalCost: null, + GrossTotal: "4800.27", + DeductibleTotal: "500.00", + DepreciationTotal: "0.00", + Discount: "0", + CustomerPay: "500.00", + InsurancePay: "4300.27", + Deposit: null, + AmountDue: "4800.27", + }, + Misc: { + ProductionStatus: null, + StatusDescription: null, + Hub50Comment: null, + DateofChange: null, + BodyTechName: null, + TotalLossYN: null, + InsScreenCommentsLine1: null, + InsScreenCommentsLine2: null, + AssignmentCaller: null, + AssignmentDivision: null, + LocationofPrimaryImpact: "12", + LocationofSecondaryImpact: null, + PaintTechID: null, + PaintTechName: null, + ImportType: null, + ImportFile: null, + GSTTax: null, + RepairDelayStatusCode: null, + RepairDelaycomment: null, + AgentMktgID: null, + AgentCity: null, + Picture1: null, + Picture2: null, + ExtNoteDate: null, + RentalOrdDate: null, + RentalPUDate: null, + RentalDueDate: null, + RentalActRetDate: null, + RentalCompanyID: null, + CSIID: null, + InsGroupCode: null, + }, + + DetailLines: { + DetailLine: job.joblines.map((jl) => + GenerateDetailLines(jl, job.bodyshop.md_order_statuses) + ), + }, + }; + + return ret; +}; + +const StatusMapping = (status, md_ro_statuses) => { + //EST, SCH, ARR, IPR, RDY, DEL, CLO, CAN, UNDEFINED. + const { + default_imported, + default_open, + default_scheduled, + default_arrived, + default_completed, + default_delivered, + default_invoiced, + default_exported, + default_void, + } = md_ro_statuses; + + if (status === default_open || status === default_imported) return "EST"; + else if (status === default_scheduled) return "SCH"; + else if (status === default_arrived) return "ARR"; + else if (status === default_completed) return "RDY"; + else if (status === default_delivered) return "DEL"; + else if (status === default_invoiced || status === default_exported) + return "CLO"; + else if (status === default_void) return "CLO"; + else if (md_ro_statuses.production_statuses.include(status)) return "IPR"; + else return "UNDEFINED"; + + // default: return "UNDEFINED" +}; + +const GenerateDetailLines = (line, statuses) => { + const ret = { + BackOrdered: line.status === statuses.default_bo ? "Y" : "N", + Cost: + line.billlines[0] && + (line.billlines[0].actual_cost * line.billlines[0].quantity).toFixed(2), + Critical: null, + Description: line.desc, + DiscountMarkup: null, + InvoiceNumber: line.billlines[0] && line.billlines[0].bill.invoice_number, + IOUPart: null, + LineNumber: line.line_no, + MarkUp: null, + OrderedOn: null, + OriginalCost: null, + OriginalInvoiceNumber: null, + PriceEach: line.billlines[0] && line.billlines[0].actual_cost, + PartNumber: line.oem_partno, + ProfitPercent: null, + PurchaseOrderNumber: null, + Qty: line.part_qty, + Status: line.status, + SupplementNumber: null, + Type: line.part_type, + Vendor: line.billlines[0] && line.billlines[0].bill.vendor.name, + VendorPaid: null, + VendorPrice: line.billlines[0] && line.billlines[0].actual_price, + Deleted: null, + ExpectedOn: null, + ReceivedOn: null, + OrderedBy: null, + ShipVia: null, + VendorContact: null, + EstimateAmount: line.act_price, + }; + return ret; +}; diff --git a/server/data/data.js b/server/data/data.js new file mode 100644 index 000000000..a520e00cd --- /dev/null +++ b/server/data/data.js @@ -0,0 +1 @@ +exports.autohouse = require("./autohouse").default; diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index 03777d597..9f6e33032 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -256,3 +256,129 @@ exports.QUERY_EMPLOYEE_PIN = `query QUERY_EMPLOYEE_PIN($shopId: uuid!, $employee pin } }`; + +exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz) { + jobs(where: {_and: [{updated_at: {_gt: $start}}, {bodyshop: {autohouseid: {_is_null: false}}}]}) { + id + ro_number + status + est_ct_fn + est_ct_ln + ownr_zip + referral_source + v_model_yr + v_model_desc + v_make_desc + v_vin + plate_no + plate_st + kmin + v_color + tlos_ind + ins_co_nm + ins_addr1 + ins_city + ins_st + ins_zip + ins_ph1 + loss_type + policy_no + clm_no + loss_date + asgn_date + date_estimated + date_open + scheduled_in + actual_in + scheduled_completion + actual_completion + scheduled_delivery + actual_delivery + date_invoiced + date_exported + rate_la1 + rate_la2 + rate_la3 + rate_la4 + rate_laa + rate_lab + rate_lad + rate_lae + rate_laf + rate_lag + rate_lam + rate_lar + rate_las + rate_lau + rate_ma2s + rate_ma2t + rate_ma3s + rate_mabl + rate_macs + rate_mahw + rate_matd + rate_mapa + rate_mash + job_totals + bodyshop { + id + shopname + address1 + city + state + zip_post + country + phone + md_ro_statuses + md_order_statuses + autohouseid + } + joblines (where:{removed: {_eq:false}}){ + id + line_no + status + line_ind + db_price + act_price + mod_lb_hrs + mod_lbr_ty + line_desc + prt_dsmk_m + prt_dsmk_p + part_qty + part_type + oem_partno + billlines (order_by:{bill:{date:desc_nulls_last}}) { + actual_cost + actual_price + quantity + bill { + vendor{ + name + } + invoice_number + } + } + } + area_of_damage + employee_prep_rel { + first_name + last_name + employee_number + id + } + employee_refinish_rel { + first_name + last_name + employee_number + id + } + employee_body_rel { + first_name + last_name + employee_number + id + } + } +} +`;