diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel
index c18ffea89..5879459cd 100644
--- a/bodyshop_translations.babel
+++ b/bodyshop_translations.babel
@@ -7794,6 +7794,27 @@
+
+ timezone
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
tt_allow_post_to_invoiced
false
diff --git a/client/package.json b/client/package.json
index 1153baef0..8aa8c9d56 100644
--- a/client/package.json
+++ b/client/package.json
@@ -33,6 +33,7 @@
"logrocket": "^2.1.2",
"markerjs2": "^2.17.2",
"moment-business-days": "^1.2.0",
+ "moment-timezone": "^0.5.34",
"phone": "^3.1.10",
"preval.macro": "^5.0.0",
"prop-types": "^15.7.2",
diff --git a/client/src/components/dashboard-components/monthly-employee-efficiency/monthly-employee-efficiency.component.jsx b/client/src/components/dashboard-components/monthly-employee-efficiency/monthly-employee-efficiency.component.jsx
index e90220698..b29f7ba4c 100644
--- a/client/src/components/dashboard-components/monthly-employee-efficiency/monthly-employee-efficiency.component.jsx
+++ b/client/src/components/dashboard-components/monthly-employee-efficiency/monthly-employee-efficiency.component.jsx
@@ -38,6 +38,7 @@ export default function DashboardMonthlyEmployeeEfficiency({
if (!!ticketsByDate[val]) {
dailyHrs = ticketsByDate[val].reduce(
(dayAcc, dayVal) => {
+ console.log(dayAcc);
return {
actual: dayAcc.actual + dayVal.actualhrs,
productive: dayAcc.actual + dayVal.productivehrs,
diff --git a/client/src/components/form-date-picker/form-date-picker.component.jsx b/client/src/components/form-date-picker/form-date-picker.component.jsx
index ba97b051e..e8c3eb7d1 100644
--- a/client/src/components/form-date-picker/form-date-picker.component.jsx
+++ b/client/src/components/form-date-picker/form-date-picker.component.jsx
@@ -1,11 +1,24 @@
import { DatePicker } from "antd";
-import moment from "moment";
+import moment from "moment-timezone";
import React, { useRef } from "react";
//To be used as a form element only.
+import { connect } from "react-redux";
+import { createStructuredSelector } from "reselect";
+import { selectBodyshop } from "../../redux/user/user.selectors";
+const mapStateToProps = createStructuredSelector({
+ //currentUser: selectCurrentUser
+ bodyshop: selectBodyshop,
+});
+const mapDispatchToProps = (dispatch) => ({
+ //setUserLanguage: language => dispatch(setUserLanguage(language))
+});
+export default connect(mapStateToProps, mapDispatchToProps)(FormDatePicker);
+
const dateFormat = "MM/DD/YYYY";
-export default function FormDatePicker({
+export function FormDatePicker({
+ bodyshop,
value,
onChange,
onBlur,
@@ -23,7 +36,7 @@ export default function FormDatePicker({
const handleKeyDown = (e) => {
if (e.key.toLowerCase() === "t") {
if (onChange) {
- onChange(new moment());
+ onChange(moment());
// if (ref.current && ref.current.blur) ref.current.blur();
}
} else if (e.key.toLowerCase() === "enter") {
diff --git a/client/src/components/schedule-calendar/schedule-calendar.container.jsx b/client/src/components/schedule-calendar/schedule-calendar.container.jsx
index aff134b85..27e05258a 100644
--- a/client/src/components/schedule-calendar/schedule-calendar.container.jsx
+++ b/client/src/components/schedule-calendar/schedule-calendar.container.jsx
@@ -65,6 +65,7 @@ export function ScheduleCalendarContainer({ calculateScheduleLoad }) {
color: "red",
start: moment(e.start).startOf("day").toDate(),
end: moment(e.end).startOf("day").toDate(),
+ allDay: true,
vacation: true,
};
}),
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 2a167fdb2..603ef790f 100644
--- a/client/src/components/shop-info/shop-info.general.component.jsx
+++ b/client/src/components/shop-info/shop-info.general.component.jsx
@@ -19,6 +19,9 @@ import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import FormItemEmail from "../form-items-formatted/email-form-item.component";
+import momentTZ from "moment-timezone";
+const timeZonesList = momentTZ.tz.names();
+
export default function ShopInfoGeneral({ form }) {
const { t } = useTranslation();
return (
@@ -84,6 +87,7 @@ export default function ShopInfoGeneral({ form }) {
+
+
+
state.user.currentUser.email);
+ try {
+ console.log("Setting shop timezone.");
+ moment.tz.setDefault(payload.timezone);
+ } catch (error) {
+ console.log(error);
+ }
factory.client(payload.imexshopid);
diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json
index 1461ccc73..0f537f652 100644
--- a/client/src/translations/en_us/common.json
+++ b/client/src/translations/en_us/common.json
@@ -483,6 +483,7 @@
"production_statuses": "Production Statuses"
},
"target_touchtime": "Target Touch Time",
+ "timezone": "Timezone",
"tt_allow_post_to_invoiced": "Allow Time Tickets to be posted to Invoiced & Exported Jobs",
"use_fippa": "Use FIPPA for Names on Generated Documents?",
"website": "Website",
diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json
index 5db600799..aa5169aa4 100644
--- a/client/src/translations/es/common.json
+++ b/client/src/translations/es/common.json
@@ -483,6 +483,7 @@
"production_statuses": ""
},
"target_touchtime": "",
+ "timezone": "",
"tt_allow_post_to_invoiced": "",
"use_fippa": "",
"website": "",
diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json
index 0885b35ac..37be43e40 100644
--- a/client/src/translations/fr/common.json
+++ b/client/src/translations/fr/common.json
@@ -483,6 +483,7 @@
"production_statuses": ""
},
"target_touchtime": "",
+ "timezone": "",
"tt_allow_post_to_invoiced": "",
"use_fippa": "",
"website": "",
diff --git a/client/yarn.lock b/client/yarn.lock
index cb4de1d5b..fd291061b 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -9460,7 +9460,14 @@ moment-business-days@^1.2.0:
resolved "https://registry.yarnpkg.com/moment-business-days/-/moment-business-days-1.2.0.tgz#6172f9f38dbf443c2f859baabeabbd2935f63d65"
integrity sha512-QJlceLfMSxy/jZSOgJYCKeKw+qGYHj8W0jMa/fYruyoJ85+bJuLRiYv5DIaflyuRipmYRfD4kDlSwVYteLN+Jw==
-moment@^2.24.0, moment@^2.25.3:
+moment-timezone@^0.5.34:
+ version "0.5.34"
+ resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.34.tgz#a75938f7476b88f155d3504a9343f7519d9a405c"
+ integrity sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==
+ dependencies:
+ moment ">= 2.9.0"
+
+"moment@>= 2.9.0", moment@^2.24.0, moment@^2.25.3:
version "2.29.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml
index d4543e4e9..0ce2ea143 100644
--- a/hasura/metadata/tables.yaml
+++ b/hasura/metadata/tables.yaml
@@ -867,6 +867,7 @@
- target_touchtime
- template_header
- textid
+ - timezone
- tt_allow_post_to_invoiced
- updated_at
- use_fippa
@@ -939,6 +940,7 @@
- state
- state_tax_id
- target_touchtime
+ - timezone
- tt_allow_post_to_invoiced
- updated_at
- use_fippa
diff --git a/hasura/migrations/1644276091201_alter_table_public_bodyshops_add_column_timezone/down.sql b/hasura/migrations/1644276091201_alter_table_public_bodyshops_add_column_timezone/down.sql
new file mode 100644
index 000000000..b71892c9d
--- /dev/null
+++ b/hasura/migrations/1644276091201_alter_table_public_bodyshops_add_column_timezone/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"."bodyshops" add column "timezone" text
+-- null;
diff --git a/hasura/migrations/1644276091201_alter_table_public_bodyshops_add_column_timezone/up.sql b/hasura/migrations/1644276091201_alter_table_public_bodyshops_add_column_timezone/up.sql
new file mode 100644
index 000000000..f855e6a15
--- /dev/null
+++ b/hasura/migrations/1644276091201_alter_table_public_bodyshops_add_column_timezone/up.sql
@@ -0,0 +1,2 @@
+alter table "public"."bodyshops" add column "timezone" text
+ null;
diff --git a/package.json b/package.json
index 7438dcc13..526764a1a 100644
--- a/package.json
+++ b/package.json
@@ -37,6 +37,7 @@
"intuit-oauth": "^4.0.0",
"lodash": "^4.17.21",
"moment": "^2.29.1",
+ "moment-timezone": "^0.5.34",
"node-mailjet": "^3.3.4",
"node-quickbooks": "^2.0.39",
"nodemailer": "^6.7.1",
diff --git a/server/accounting/pbs/pbs-job-export.js b/server/accounting/pbs/pbs-job-export.js
index f9d1cb96e..e7bdcdb80 100644
--- a/server/accounting/pbs/pbs-job-export.js
+++ b/server/accounting/pbs/pbs-job-export.js
@@ -14,9 +14,10 @@ const { PBS_ENDPOINTS, PBS_CREDENTIALS } = require("./pbs-constants");
const CalculateAllocations =
require("../../cdk/cdk-calculate-allocations").default;
const CdkBase = require("../../web-sockets/web-socket");
-const moment = require("moment");
+const moment = require("moment-timezone");
const Dinero = require("dinero.js");
const axios = AxiosLib.create();
+
axios.interceptors.request.use((x) => {
const socket = x.socket;
@@ -553,7 +554,9 @@ async function InsertAccountPostingData(socket) {
//Comment: "String",
//AdditionalInfo: "String",
InvoiceNumber: socket.JobData.ro_number,
- InvoiceDate: moment(socket.JobData.date_invoiced).toISOString(),
+ InvoiceDate: moment(socket.JobData.date_invoiced)
+ .tz(socket.JobData.bodyshop.timezone)
+ .toISOString(),
};
wips.push(item);
}
@@ -567,7 +570,9 @@ async function InsertAccountPostingData(socket) {
//Comment: "String",
//AdditionalInfo: "String",
InvoiceNumber: socket.JobData.ro_number,
- InvoiceDate: moment(socket.JobData.date_invoiced).toISOString(),
+ InvoiceDate: moment(socket.JobData.date_invoiced)
+ .tz(socket.JobData.bodyshop.timezone)
+ .toISOString(),
};
wips.push(item);
@@ -578,7 +583,9 @@ async function InsertAccountPostingData(socket) {
//Comment: "String",
//AdditionalInfo: "String",
InvoiceNumber: socket.JobData.ro_number,
- InvoiceDate: moment(socket.JobData.date_invoiced).toISOString(),
+ InvoiceDate: moment(socket.JobData.date_invoiced)
+ .tz(socket.JobData.bodyshop.timezone)
+ .toISOString(),
};
wips.push(itemWip);
//Add to the WIP account.
@@ -593,7 +600,9 @@ async function InsertAccountPostingData(socket) {
//Comment: "String",
//AdditionalInfo: "String",
InvoiceNumber: socket.JobData.ro_number,
- InvoiceDate: moment(socket.JobData.date_invoiced).toISOString(),
+ InvoiceDate: moment(socket.JobData.date_invoiced)
+ .tz(socket.JobData.bodyshop.timezone)
+ .toISOString(),
};
wips.push(item2);
}
@@ -609,7 +618,9 @@ async function InsertAccountPostingData(socket) {
//Comment: "String",
//AdditionalInfo: "String",
InvoiceNumber: socket.JobData.ro_number,
- InvoiceDate: moment(socket.JobData.date_invoiced).toISOString(),
+ InvoiceDate: moment(socket.JobData.date_invoiced)
+ .tz(socket.JobData.bodyshop.timezone)
+ .toISOString(),
};
wips.push(item);
@@ -622,7 +633,9 @@ async function InsertAccountPostingData(socket) {
Posting: {
Reference: socket.JobData.ro_number,
JournalCode: socket.txEnvelope.journal,
- TransactionDate: moment(socket.JobData.date_invoiced).toISOString(), //"0001-01-01T00:00:00.0000000Z",
+ TransactionDate: moment(socket.JobData.date_invoiced)
+ .tz(socket.JobData.bodyshop.timezone)
+ .toISOString(), //"0001-01-01T00:00:00.0000000Z",
Description: socket.txEnvelope.story,
//AdditionalInfo: "String",
Source: "ImEX Online",
diff --git a/server/accounting/qbo/qbo-payables.js b/server/accounting/qbo/qbo-payables.js
index 7610537d2..abf470511 100644
--- a/server/accounting/qbo/qbo-payables.js
+++ b/server/accounting/qbo/qbo-payables.js
@@ -17,7 +17,7 @@ const {
setNewRefreshToken,
} = require("./qbo-callback");
const OAuthClient = require("intuit-oauth");
-const moment = require("moment");
+const moment = require("moment-timezone");
const GraphQLClient = require("graphql-request").GraphQLClient;
const findTaxCode = require("../qb-receivables-lines").findTaxCode;
@@ -178,7 +178,9 @@ async function InsertBill(oauthClient, qbo_realmId, req, bill, vendor) {
VendorRef: {
value: vendor.Id,
},
- TxnDate: moment(bill.date).format("YYYY-MM-DD"),
+ TxnDate: moment(bill.date)
+ .tz(bill.job.bodyshop.timezone)
+ .format("YYYY-MM-DD"),
//DueDate: bill.due_date && moment(bill.due_date).format("YYYY-MM-DD"),
DocNumber: bill.invoice_number,
//...(bill.job.class ? { ClassRef: { Id: classes[bill.job.class] } } : {}),
diff --git a/server/accounting/qbo/qbo-payments.js b/server/accounting/qbo/qbo-payments.js
index db503458a..4ff7762a8 100644
--- a/server/accounting/qbo/qbo-payments.js
+++ b/server/accounting/qbo/qbo-payments.js
@@ -15,7 +15,7 @@ const {
setNewRefreshToken,
} = require("./qbo-callback");
const OAuthClient = require("intuit-oauth");
-const moment = require("moment");
+const moment = require("moment-timezone");
const GraphQLClient = require("graphql-request").GraphQLClient;
const {
QueryInsuranceCo,
@@ -137,14 +137,22 @@ exports.default = async (req, res) => {
}
if (payment.amount > 0) {
- await InsertPayment(oauthClient, qbo_realmId, req, payment, jobTier);
+ await InsertPayment(
+ oauthClient,
+ qbo_realmId,
+ req,
+ payment,
+ jobTier,
+ bodyshop
+ );
} else {
await InsertCreditMemo(
oauthClient,
qbo_realmId,
req,
payment,
- jobTier
+ jobTier,
+ bodyshop
);
}
ret.push({ paymentid: payment.id, success: true });
@@ -178,7 +186,8 @@ async function InsertPayment(
qbo_realmId,
req,
payment,
- parentRef
+ parentRef,
+ bodyshop
) {
const { paymentMethods, invoices } = await QueryMetaData(
oauthClient,
@@ -198,7 +207,7 @@ async function InsertPayment(
CustomerRef: {
value: parentRef.Id,
},
- TxnDate: moment(payment.date).format("YYYY-MM-DD"),
+ TxnDate: moment(payment.date).tz(bodyshop.timezone).format("YYYY-MM-DD"),
//DueDate: bill.due_date && moment(bill.due_date).format("YYYY-MM-DD"),
DocNumber: payment.paymentnum,
TotalAmt: Dinero({
@@ -362,7 +371,8 @@ async function InsertCreditMemo(
qbo_realmId,
req,
payment,
- parentRef
+ parentRef,
+ bodyshop
) {
const { paymentMethods, invoices, items, taxCodes } = await QueryMetaData(
oauthClient,
@@ -382,7 +392,7 @@ async function InsertCreditMemo(
CustomerRef: {
value: parentRef.Id,
},
- TxnDate: moment(payment.date).format("YYYY-MM-DD"),
+ TxnDate: moment(payment.date).tz(bodyshop.timezone).format("YYYY-MM-DD"),
DocNumber: payment.paymentnum,
...(invoices && invoices[0]
? { InvoiceRef: { value: invoices[0].Id } }
diff --git a/server/accounting/qbo/qbo-receivables.js b/server/accounting/qbo/qbo-receivables.js
index 627c6d2c7..74e4b0626 100644
--- a/server/accounting/qbo/qbo-receivables.js
+++ b/server/accounting/qbo/qbo-receivables.js
@@ -17,7 +17,7 @@ const {
} = require("./qbo-callback");
const OAuthClient = require("intuit-oauth");
const CreateInvoiceLines = require("../qb-receivables-lines").default;
-const moment = require("moment");
+const moment = require("moment-timezone");
const GraphQLClient = require("graphql-request").GraphQLClient;
const { generateOwnerTier } = require("../qbxml/qbxml-utils");
@@ -440,7 +440,9 @@ async function InsertInvoice(
const invoiceObj = {
Line: InvoiceLineAdd,
- TxnDate: moment(job.date_invoiced).format("YYYY-MM-DD"),
+ TxnDate: moment(job.date_invoiced)
+ .tz(bodyshop.timezone)
+ .format("YYYY-MM-DD"),
DocNumber: job.ro_number,
...(job.class ? { ClassRef: { value: classes[job.class] } } : {}),
CustomerMemo: {
diff --git a/server/accounting/qbxml/qbxml-payables.js b/server/accounting/qbxml/qbxml-payables.js
index 97f7f7c90..90b8e0b55 100644
--- a/server/accounting/qbxml/qbxml-payables.js
+++ b/server/accounting/qbxml/qbxml-payables.js
@@ -5,7 +5,7 @@ const queries = require("../../graphql-client/queries");
const Dinero = require("dinero.js");
var builder = require("xmlbuilder2");
const QbXmlUtils = require("./qbxml-utils");
-const moment = require("moment");
+const moment = require("moment-timezone");
const logger = require("../../utils/logger");
require("dotenv").config({
path: path.resolve(
@@ -72,9 +72,14 @@ const generateBill = (bill) => {
VendorRef: {
FullName: bill.vendor.name,
},
- TxnDate: moment(bill.date).format("YYYY-MM-DD"),
+ TxnDate: moment(bill.date)
+ .tz(bill.job.bodyshop.timezone)
+ .format("YYYY-MM-DD"),
DueDate:
- bill.due_date && moment(bill.due_date).format("YYYY-MM-DD"),
+ bill.due_date &&
+ moment(bill.due_date)
+ .tz(bill.job.bodyshop.timezone)
+ .format("YYYY-MM-DD"),
RefNumber: bill.invoice_number,
Memo: `RO ${bill.job.ro_number || ""}`,
ExpenseLineAdd: bill.billlines.map((il) =>
diff --git a/server/accounting/qbxml/qbxml-payments.js b/server/accounting/qbxml/qbxml-payments.js
index 7820f9a7e..2427d00fe 100644
--- a/server/accounting/qbxml/qbxml-payments.js
+++ b/server/accounting/qbxml/qbxml-payments.js
@@ -4,7 +4,7 @@ const DineroQbFormat = require("../accounting-constants").DineroQbFormat;
const queries = require("../../graphql-client/queries");
const Dinero = require("dinero.js");
var builder = require("xmlbuilder2");
-const moment = require("moment");
+const moment = require("moment-timezone");
const QbXmlUtils = require("./qbxml-utils");
const QbxmlReceivables = require("./qbxml-receivables");
const logger = require("../../utils/logger");
@@ -84,7 +84,7 @@ exports.default = async (req, res) => {
QbXmlToExecute.push({
id: i.id,
okStatusCodes: ["0"],
- qbxml: generatePayment(i, isThreeTier, twoTierPref),
+ qbxml: generatePayment(i, isThreeTier, twoTierPref, bodyshop),
});
});
@@ -101,7 +101,7 @@ exports.default = async (req, res) => {
}
};
-const generatePayment = (payment, isThreeTier, twoTierPref) => {
+const generatePayment = (payment, isThreeTier, twoTierPref, bodyshop) => {
let paymentQbxmlObj;
if (payment.amount > 0) {
paymentQbxmlObj = {
@@ -128,7 +128,9 @@ const generatePayment = (payment, isThreeTier, twoTierPref) => {
FullName:
payment.job.bodyshop.md_responsibility_centers.ar.accountname,
},
- TxnDate: moment(payment.date).format("YYYY-MM-DD"), //Trim String
+ TxnDate: moment(payment.date)
+ .tz(bodyshop.timezone)
+ .format("YYYY-MM-DD"), //Trim String
RefNumber: payment.paymentnum || payment.transactionid,
TotalAmount: Dinero({
amount: Math.round(payment.amount * 100),
@@ -172,7 +174,9 @@ const generatePayment = (payment, isThreeTier, twoTierPref) => {
FullName:
payment.job.bodyshop.md_responsibility_centers.ar.accountname,
},
- TxnDate: moment(payment.date).format("YYYY-MM-DD"), //Trim String
+ TxnDate: moment(payment.date)
+ .tz(bodyshop.timezone)
+ .format("YYYY-MM-DD"), //Trim String
RefNumber:
payment.paymentnum || payment.stripeid || payment.transactionid,
diff --git a/server/accounting/qbxml/qbxml-receivables.js b/server/accounting/qbxml/qbxml-receivables.js
index 3150a0349..978e64f2b 100644
--- a/server/accounting/qbxml/qbxml-receivables.js
+++ b/server/accounting/qbxml/qbxml-receivables.js
@@ -3,7 +3,7 @@ const path = require("path");
const DineroQbFormat = require("../accounting-constants").DineroQbFormat;
const queries = require("../../graphql-client/queries");
const Dinero = require("dinero.js");
-const moment = require("moment");
+const moment = require("moment-timezone");
var builder = require("xmlbuilder2");
const QbXmlUtils = require("./qbxml-utils");
const logger = require("../../utils/logger");
@@ -254,7 +254,9 @@ const generateInvoiceQbxml = (
? { ClassRef: { FullName: jobs_by_pk.class } }
: {}),
- TxnDate: moment(jobs_by_pk.date_invoiced).format("YYYY-MM-DD"),
+ TxnDate: moment(jobs_by_pk.date_invoiced)
+ .tz(bodyshop.timezone)
+ .format("YYYY-MM-DD"),
RefNumber: jobs_by_pk.ro_number,
BillAddress: {
Addr1: jobs_by_pk.ownr_co_nm
diff --git a/server/cdk/cdk-job-export.js b/server/cdk/cdk-job-export.js
index 2a78af167..f860a1df3 100644
--- a/server/cdk/cdk-job-export.js
+++ b/server/cdk/cdk-job-export.js
@@ -13,7 +13,7 @@ const CdkWsdl = require("./cdk-wsdl").default;
const { CDK_CREDENTIALS, CheckCdkResponseForError } = require("./cdk-wsdl");
const CalcualteAllocations = require("./cdk-calculate-allocations").default;
-const moment = require("moment");
+const moment = require("moment-timezone");
const replaceSpecialRegex = /[^a-zA-Z0-9 .,\n #]+/g;
@@ -716,6 +716,7 @@ async function InsertDmsVehicle(socket) {
dealerNumber: socket.JobData.bodyshop.cdk_dealerid,
...(socket.txEnvelope.inservicedate && {
inServiceDate: moment(socket.txEnvelope.inservicedate)
+ .tz(socket.JobData.bodyshop.timezone)
.startOf("day")
.toISOString(),
}),
@@ -723,7 +724,9 @@ async function InsertDmsVehicle(socket) {
},
manufacturer: {},
vehicle: {
- deliveryDate: moment().format("YYYYMMDD"),
+ deliveryDate: moment()
+ .tz(socket.JobData.bodyshop.timezone)
+ .format("YYYYMMDD"),
licensePlateNo: socket.JobData.plate_no,
make: socket.txEnvelope.dms_make,
modelAbrev: socket.txEnvelope.dms_model,
@@ -850,14 +853,16 @@ async function UpdateDmsVehicle(socket) {
inServiceDate: moment(
socket.DMSVeh.dealer.inServiceDate ||
socket.txEnvelope.inservicedate
- ).toISOString(),
+ )
+ .tz(socket.JobData.bodyshop.timezone)
+ .toISOString(),
}),
},
vehicle: {
...socket.DMSVeh.vehicle,
- deliveryDate: moment(
- socket.DMSVeh.vehicle.deliveryDate
- ).toISOString(),
+ deliveryDate: moment(socket.DMSVeh.vehicle.deliveryDate)
+ .tz(socket.JobData.bodyshop.timezone)
+ .toISOString(),
},
owners: ids,
},
@@ -912,10 +917,18 @@ async function InsertServiceVehicleHistory(socket) {
vehID: socket.DMSVid.vehiclesVehId,
roNumber: socket.JobData.ro_number.match(/\d+/g),
mileage: socket.txEnvelope.kmout,
- openDate: moment(socket.JobData.actual_in).format("YYYY-MM-DD"),
- openTime: moment(socket.JobData.actual_in).format("HH:mm:ss"),
- closeDate: moment(socket.JobData.invoice_date).format("YYYY-MM-DD"),
- closeTime: moment(socket.JobData.invoice_date).format("HH:mm:ss"),
+ openDate: moment(socket.JobData.actual_in)
+ .tz(socket.JobData.bodyshop.timezone)
+ .format("YYYY-MM-DD"),
+ openTime: moment(socket.JobData.actual_in)
+ .tz(socket.JobData.bodyshop.timezone)
+ .format("HH:mm:ss"),
+ closeDate: moment(socket.JobData.invoice_date)
+ .tz(socket.JobData.bodyshop.timezone)
+ .format("YYYY-MM-DD"),
+ closeTime: moment(socket.JobData.invoice_date)
+ .tz(socket.JobData.bodyshop.timezone)
+ .format("HH:mm:ss"),
comments: socket.txEnvelope.story,
cashierID: socket.JobData.bodyshop.cdk_configuration.cashierid,
},
@@ -966,7 +979,9 @@ async function InsertDmsStartWip(socket) {
arg0: CDK_CREDENTIALS,
arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid },
arg2: {
- acctgDate: moment().format("YYYY-MM-DD"),
+ acctgDate: moment()
+ .tz(socket.JobData.bodyshop.timezone)
+ .format("YYYY-MM-DD"),
//socket.JobData.invoice_date
desc:
socket.txEnvelope.story &&
diff --git a/server/data/arms.js b/server/data/arms.js
index 2d23ab8d3..0518b139c 100644
--- a/server/data/arms.js
+++ b/server/data/arms.js
@@ -1,7 +1,7 @@
const path = require("path");
const queries = require("../graphql-client/queries");
const Dinero = require("dinero.js");
-const moment = require("moment");
+const moment = require("moment-timezone");
const fs = require("fs");
const _ = require("lodash");
@@ -66,24 +66,35 @@ exports.default = async (req, res) => {
// },
RepairEvent: {
CreatedDateTime: (job.date_open
- ? moment(job.date_open)
+ ? moment(job.date_open).tz(bodyshop.timezone)
: moment()
).format(momentFormat),
ArrivalDateTime:
- job.actual_in && moment(job.actual_in).format(momentFormat),
+ job.actual_in &&
+ moment(job.actual_in)
+ .tz(bodyshop.timezone)
+ .format(momentFormat),
ArrivalOdometerReading: job.kmin,
TargetCompletionDateTime:
job.scheduled_completion &&
- moment(job.scheduled_completion).format(momentFormat),
+ moment(job.scheduled_completion)
+ .tz(bodyshop.timezone)
+ .format(momentFormat),
ActualCompletionDateTime:
job.actual_completion &&
- moment(job.actual_completion).format(momentFormat),
+ moment(job.actual_completion)
+ .tz(bodyshop.timezone)
+ .format(momentFormat),
ActualPickUpDateTime:
job.actual_delivery &&
- moment(job.actual_delivery).format(momentFormat),
+ moment(job.actual_delivery)
+ .tz(bodyshop.timezone)
+ .format(momentFormat),
CloseDateTime:
job.date_exported &&
- moment(job.date_exported).format(momentFormat),
+ moment(job.date_exported)
+ .tz(bodyshop.timezone)
+ .format(momentFormat),
},
},
RepairOrderHeader: {
@@ -254,7 +265,8 @@ exports.default = async (req, res) => {
//ProductionDate: "2009-10",
ModelYear:
parseInt(job.v_model_yr) < 1900
- ? parseInt(job.v_model_yr) < moment().format("YY")
+ ? parseInt(job.v_model_yr) <
+ moment().tz(bodyshop.timezone).format("YY")
? `20${job.v_model_yr}`
: `19${job.v_model_yr}`
: job.v_model_yr,
@@ -288,7 +300,9 @@ exports.default = async (req, res) => {
Facts: {
LossDateTime:
job.loss_date &&
- moment(job.loss_date).format(momentFormat),
+ moment(job.loss_date)
+ .tz(bodyshop.timezone)
+ .format(momentFormat),
LossDescCode: "Collision",
PrimaryPOI: {
POICode: job.area_of_damage && job.area_of_damage.impact1,
@@ -750,13 +764,17 @@ exports.default = async (req, res) => {
ProductionStatus: {
ProductionStage: {
ProductionStageCode: GetProductionStageCode(job, bodyshop),
- ProductionStageDateTime: moment().format(momentFormat),
+ ProductionStageDateTime: moment()
+ .tz(bodyshop.timezone)
+ .format(momentFormat),
// ProductionStageStatusComment:
// "Going to be painted this afternoon",
},
RepairStatus: {
RepairStatusCode: GetRepairStatusCode(job),
- RepairStatusDateTime: moment().format(momentFormat),
+ RepairStatusDateTime: moment()
+ .tz(bodyshop.timezone)
+ .format(momentFormat),
// RepairStatusMemo: "Waiting on back ordered parts",
},
},
diff --git a/server/data/autohouse.js b/server/data/autohouse.js
index 5ff98cff9..1a442485a 100644
--- a/server/data/autohouse.js
+++ b/server/data/autohouse.js
@@ -1,7 +1,7 @@
const path = require("path");
const queries = require("../graphql-client/queries");
const Dinero = require("dinero.js");
-const moment = require("moment");
+const moment = require("moment-timezone");
var builder = require("xmlbuilder2");
const _ = require("lodash");
const logger = require("../utils/logger");
@@ -267,27 +267,50 @@ const CreateRepairOrderTag = (job, errorCallback) => {
},
Dates: {
DateofLoss:
- (job.loss_date && moment(job.loss_date).format(AhDateFormat)) || "",
+ (job.loss_date &&
+ moment(job.loss_date)
+ .tz(job.bodyshop.timezone)
+ .format(AhDateFormat)) ||
+ "",
InitialCustomerContactDate: null,
FirstFollowUpDate: null,
ReferralDate: null,
EstimateAppointmentDate: null,
SecondFollowUpDate: null,
AssignedDate:
- (job.asgn_date && moment(job.asgn_date).format(AhDateFormat)) || "",
+ (job.asgn_date &&
+ moment(job.asgn_date)
+ .tz(job.bodyshop.timezone)
+ .format(AhDateFormat)) ||
+ "",
EstComplete: null,
CustomerAuthorizationDate: null,
InsuranceAuthorizationDate: null,
DateOpened:
- (job.date_open && moment(job.date_open).format(AhDateFormat)) || "",
+ (job.date_open &&
+ moment(job.date_open)
+ .tz(job.bodyshop.timezone)
+ .format(AhDateFormat)) ||
+ "",
ScheduledArrivalDate:
- (job.scheduled_in && moment(job.scheduled_in).format(AhDateFormat)) ||
+ (job.scheduled_in &&
+ moment(job.scheduled_in)
+ .tz(job.bodyshop.timezone)
+ .format(AhDateFormat)) ||
"",
CarinShop:
- (job.actual_in && moment(job.actual_in).format(AhDateFormat)) || "",
+ (job.actual_in &&
+ moment(job.actual_in)
+ .tz(job.bodyshop.timezone)
+ .format(AhDateFormat)) ||
+ "",
InsInspDate: null,
StartDate:
- (job.actual_in && moment(job.actual_in).format(AhDateFormat)) || "",
+ (job.actual_in &&
+ moment(job.actual_in)
+ .tz(job.bodyshop.timezone)
+ .format(AhDateFormat)) ||
+ "",
PartsOrder: null,
TeardownHold: null,
SupplementSubmittedDate: null,
@@ -303,24 +326,35 @@ const CreateRepairOrderTag = (job, errorCallback) => {
//InsuranceTargetOut: null,
CarComplete:
(job.actual_completion &&
- moment(job.actual_completion).format(AhDateFormat)) ||
+ moment(job.actual_completion)
+ .tz(job.bodyshop.timezone)
+ .format(AhDateFormat)) ||
"",
DeliveryAppointmentDate:
(job.scheduled_delivery &&
- moment(job.scheduled_delivery).format(AhDateFormat)) ||
+ moment(job.scheduled_delivery)
+ .tz(job.bodyshop.timezone)
+ .format(AhDateFormat)) ||
"",
DateClosed:
(job.date_invoiced &&
- moment(job.date_invoiced).format(AhDateFormat)) ||
+ moment(job.date_invoiced)
+ .tz(job.bodyshop.timezone)
+ .format(AhDateFormat)) ||
"",
CustomerPaidInFullDate: null,
InsurancePaidInFullDate: null,
CustPickup:
(job.actual_delivery &&
- moment(job.actual_delivery).format(AhDateFormat)) ||
+ moment(job.actual_delivery)
+ .tz(job.bodyshop.timezone)
+ .format(AhDateFormat)) ||
"",
AccountPostedDate:
- job.date_exported && moment(job.date_exported).format(AhDateFormat),
+ job.date_exported &&
+ moment(job.date_exported)
+ .tz(job.bodyshop.timezone)
+ .format(AhDateFormat),
CSIProcessedDate: null,
ThankYouLetterSent: null,
AdditionalFollowUpDate: null,
@@ -760,9 +794,9 @@ const GenerateDetailLines = (line, statuses) => {
MarkUp: null,
OrderedOn:
(line.parts_order_lines[0] &&
- moment(line.parts_order_lines[0].parts_order.order_date).format(
- AhDateFormat
- )) ||
+ moment(line.parts_order_lines[0].parts_order.order_date)
+ .tz(job.bodyshop.timezone)
+ .format(AhDateFormat)) ||
"",
OriginalCost: null,
OriginalInvoiceNumber: null,
@@ -786,7 +820,9 @@ const GenerateDetailLines = (line, statuses) => {
ExpectedOn: null,
ReceivedOn:
line.billlines[0] &&
- moment(line.billlines[0].bill.date).format(AhDateFormat),
+ moment(line.billlines[0].bill.date)
+ .tz(job.bodyshop.timezone)
+ .format(AhDateFormat),
OrderedBy: null,
ShipVia: null,
VendorContact: null,
diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js
index 59dc447a9..a1ad30545 100644
--- a/server/graphql-client/queries.js
+++ b/server/graphql-client/queries.js
@@ -178,6 +178,7 @@ query QUERY_JOBS_FOR_RECEIVABLES_EXPORT($ids: [uuid!]!) {
md_responsibility_centers
accountingconfig
md_ins_cos
+ timezone
}
}
`;
@@ -247,6 +248,7 @@ query QUERY_JOBS_FOR_CDK_EXPORT($id: uuid!) {
accountingconfig
cdk_dealerid
cdk_configuration
+ timezone
}
owner {
accountingid
@@ -341,6 +343,7 @@ query QUERY_JOBS_FOR_PBS_EXPORT($id: uuid!) {
accountingconfig
pbs_serialnumber
pbs_configuration
+ timezone
}
owner {
id
@@ -385,6 +388,7 @@ query QUERY_BILLS_FOR_PAYABLES_EXPORT($bills: [uuid!]!) {
class
bodyshop{
md_responsibility_centers
+ timezone
}
}
billlines{
@@ -411,6 +415,7 @@ exports.QUERY_PAYMENTS_FOR_EXPORT = `
id
md_responsibility_centers
accountingconfig
+ timezone
}
payments(where: {id: {_in: $payments}}) {
id
@@ -460,6 +465,7 @@ exports.QUERY_UPCOMING_APPOINTMENTS = `query QUERY_UPCOMING_APPOINTMENTS($now: t
ssbuckets
target_touchtime
workingdays
+ timezone
}
jobhrs: joblines_aggregate(where: {removed: {_eq: false}}) {
aggregate {
@@ -558,7 +564,7 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
autohouseid
md_responsibility_centers
jc_hourly_rates
-
+ timezone
}
jobs(where: {_and: [{converted: {_eq: true}}, {updated_at: {_gt: $start}}, {shopid: {_eq: $bodyshopid}}]}) {
id
@@ -1234,6 +1240,7 @@ exports.GET_AUTOHOUSE_SHOPS = `query GET_AUTOHOUSE_SHOPS {
md_responsibility_centers
jc_hourly_rates
imexshopid
+ timezone
}
}
`;
diff --git a/server/scheduling/scheduling-job.js b/server/scheduling/scheduling-job.js
index c5b2e3251..d461e1395 100644
--- a/server/scheduling/scheduling-job.js
+++ b/server/scheduling/scheduling-job.js
@@ -2,7 +2,7 @@ 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");
+const moment = require("moment-timezone");
const logger = require("../utils/logger");
const _ = require("lodash");
require("dotenv").config({
@@ -32,7 +32,7 @@ exports.job = async (req, res) => {
});
const { jobs_by_pk, blockedDays, prodJobs, arrJobs, compJobs } = result;
- const { ssbuckets, workingdays } = result.jobs_by_pk.bodyshop;
+ const { ssbuckets, workingdays, timezone } = result.jobs_by_pk.bodyshop;
const jobHrs = result.jobs_by_pk.jobhrs.aggregate.sum.mod_lb_hrs;
const JobBucket = ssbuckets.filter(
@@ -68,7 +68,9 @@ exports.job = async (req, res) => {
);
filteredArrJobs.forEach((item) => {
- const itemDate = moment(item.scheduled_in).format("yyyy-MM-DD");
+ const itemDate = moment(item.scheduled_in)
+ .tz(timezone)
+ .format("yyyy-MM-DD");
if (!!load[itemDate]) {
load[itemDate].hoursIn =
(load[itemDate].hoursIn || 0) +
@@ -100,18 +102,17 @@ exports.job = async (req, res) => {
//Job isn't found in production or coming in.
//is it going today or scheduled to go today?
if (
- moment(item.actual_completion || item.scheduled_completion).isSame(
- moment(),
- "day"
- )
+ moment(item.actual_completion || item.scheduled_completion)
+ .tz(timezone)
+ .isSame(moment().tz(timezone), "day")
) {
console.log("Job is going today anyways, ignore it.", item);
return;
}
if (
- moment(item.actual_completion || item.scheduled_completion).isBefore(
- moment(),
+ moment(item.actual_completion || item.scheduled_completion).tz(timezone).isBefore(
+ moment().tz(timezone),
"day"
)
) {
@@ -127,7 +128,7 @@ exports.job = async (req, res) => {
} else {
const itemDate = moment(
item.actual_completion || item.scheduled_completion
- ).format("yyyy-MM-DD");
+ ).tz(timezone).format("yyyy-MM-DD");
if (!!load[itemDate]) {
load[itemDate].hoursOut =
(load[itemDate].hoursOut || 0) +
@@ -146,20 +147,20 @@ exports.job = async (req, res) => {
});
//Propagate the expected load to each day.
- const yesterday = moment().subtract(1, "day");
- const today = moment().startOf("day");
+ const yesterday = moment().tz(timezone).subtract(1, "day");
+ const today = moment().tz(timezone).startOf("day");
- const end = moment.max([
- ...filteredArrJobs.map((a) => moment(a.scheduled_in)),
+ const end = moment.tz(timezone).max([
+ ...filteredArrJobs.map((a) => moment(a.scheduled_in).tz(timezone)),
...filteredCompJobs
- .map((p) => moment(p.actual_completion || p.scheduled_completion))
+ .map((p) => moment(p.actual_completion || p.scheduled_completion).tz(timezone))
.filter((p) => p.isValid() && p.isAfter(yesterday)),
- moment().add(5, "days"),
+ moment().tz(timezone).add(5, "days"),
]);
const range = Math.round(moment.duration(end.diff(today)).asDays());
for (var day = 0; day < range; day++) {
- const current = moment(today).add(day, "days").format("yyyy-MM-DD");
- const prev = moment(today)
+ const current = moment(today).tz(timezone).add(day, "days").format("yyyy-MM-DD");
+ const prev = moment(today).tz(timezone)
.add(day - 1, "days")
.format("yyyy-MM-DD");
if (!!!load[current]) {
@@ -187,7 +188,7 @@ exports.job = async (req, res) => {
blockedDays.forEach((b) => {
//Find it in the load, set it as blocked.
- const startIsoFormat = moment(b.start).format("YYYY-MM-DD");
+ const startIsoFormat = moment(b.start).tz(timezone).format("YYYY-MM-DD");
if (load[startIsoFormat]) load[startIsoFormat].blocked = true;
else {
load[startIsoFormat] = { blocked: true };
@@ -198,12 +199,12 @@ exports.job = async (req, res) => {
const possibleDates = [];
delete load.productionTotal;
const loadKeys = Object.keys(load).sort((a, b) =>
- moment(a).isAfter(moment(b)) ? 1 : -1
+ moment(a).tz(timezone).isAfter(moment(b).tz(timezone)) ? 1 : -1
);
loadKeys.forEach((loadKey) => {
const isShopOpen =
- (workingdays[dayOfWeekMapper(moment(loadKey).day())] || false) &&
+ (workingdays[dayOfWeekMapper(moment(loadKey).tz(timezone).day())] || false) &&
!load[loadKey].blocked;
if (
diff --git a/yarn.lock b/yarn.lock
index 783a1a0e4..b0541ac25 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2740,7 +2740,14 @@ mkdirp@^0.5.1:
dependencies:
minimist "^1.2.5"
-moment@^2.29.1:
+moment-timezone@^0.5.34:
+ version "0.5.34"
+ resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.34.tgz#a75938f7476b88f155d3504a9343f7519d9a405c"
+ integrity sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==
+ dependencies:
+ moment ">= 2.9.0"
+
+"moment@>= 2.9.0", moment@^2.29.1:
version "2.29.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==