Compare commits
111 Commits
release/20
...
release/20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12a5f17351 | ||
|
|
a2032553d9 | ||
|
|
d22979dadc | ||
|
|
c1068ec92b | ||
|
|
f8151e387e | ||
|
|
b98bfe566a | ||
|
|
c0220f0ca2 | ||
|
|
3e121a1a25 | ||
|
|
a2a8868223 | ||
|
|
80d16b4651 | ||
|
|
4c1a333514 | ||
|
|
0660b79c01 | ||
|
|
63ec578b6a | ||
|
|
dcf388ff7c | ||
|
|
0cd1b41ed9 | ||
|
|
79124daa9a | ||
|
|
76ec55d709 | ||
|
|
375b8ba050 | ||
|
|
2192cb1e7c | ||
|
|
65b505035a | ||
|
|
191f3f96a2 | ||
|
|
14d873f795 | ||
|
|
55ad75df8a | ||
|
|
5f112e797d | ||
|
|
b842cee076 | ||
|
|
2c1c11828d | ||
|
|
e7c380d780 | ||
|
|
0958ea5ba6 | ||
|
|
8031f2b2ed | ||
|
|
996863fcb7 | ||
|
|
066f395a40 | ||
|
|
51c5d163a5 | ||
|
|
d8ec6dd997 | ||
|
|
cc636bdfe2 | ||
|
|
a3e8f56728 | ||
|
|
a6f2cfba0f | ||
|
|
2e7d8df781 | ||
|
|
da51aeb135 | ||
|
|
680dd98f6d | ||
|
|
e79b9f9084 | ||
|
|
22e6d596e6 | ||
|
|
1ba904d082 | ||
|
|
62be2b0a0a | ||
|
|
dc77930950 | ||
|
|
520b61706f | ||
|
|
09a87dd2a7 | ||
|
|
f884d2e23f | ||
|
|
b84935efdc | ||
|
|
b3aeee4f45 | ||
|
|
06f266a292 | ||
|
|
a2d54d5dd5 | ||
|
|
b34694f3c4 | ||
|
|
43b8842027 | ||
|
|
0dbb3a446a | ||
|
|
75b2398421 | ||
|
|
2409042450 | ||
|
|
85ccb36b2e | ||
|
|
a616921e2b | ||
|
|
f509ea07c0 | ||
|
|
efb62b59f4 | ||
|
|
e263c32d83 | ||
|
|
30afe97fba | ||
|
|
e383b0800c | ||
|
|
e0507f2d17 | ||
|
|
cdd3841d49 | ||
|
|
4a023faf67 | ||
|
|
e9f4b48839 | ||
|
|
90f0232ff0 | ||
|
|
b94ea099b9 | ||
|
|
9f5b1c4ea5 | ||
|
|
4842605035 | ||
|
|
f9f14255a8 | ||
|
|
139dedd3e7 | ||
|
|
bd9c2cd0af | ||
|
|
e0e2183d86 | ||
|
|
f8695972a3 | ||
|
|
4c70351429 | ||
|
|
bc504d2a78 | ||
|
|
22dfcc215e | ||
|
|
415d6cca29 | ||
|
|
6c0bf67f37 | ||
|
|
351459681c | ||
|
|
e7e9ca6dfc | ||
|
|
39998a279e | ||
|
|
7c66e5cb90 | ||
|
|
b4e0dcc395 | ||
|
|
c16a2a83a5 | ||
|
|
a2dca6c1a1 | ||
|
|
ffb39bbee7 | ||
|
|
46731975e6 | ||
|
|
06f725ebb1 | ||
|
|
8745ffd08f | ||
|
|
634adb6e9a | ||
|
|
b5386be6af | ||
|
|
14e9ac2cdb | ||
|
|
76fb8f453d | ||
|
|
afc674d74c | ||
|
|
8c67a94387 | ||
|
|
a17b2b0923 | ||
|
|
91c5560fe8 | ||
|
|
356928ce77 | ||
|
|
eb05a746c4 | ||
|
|
07b5c5e93c | ||
|
|
dcb9c32336 | ||
|
|
6a59092d6a | ||
|
|
9d770a4cd5 | ||
|
|
a20e005583 | ||
|
|
b81d3369af | ||
|
|
9ab08fbdd0 | ||
|
|
1a53e7c2f7 | ||
|
|
36a7b8346e |
@@ -1,4 +1,4 @@
|
||||
<babeledit_project be_version="2.7.1" version="1.2">
|
||||
<babeledit_project version="1.2" be_version="2.7.1">
|
||||
<!--
|
||||
|
||||
BabelEdit project file
|
||||
@@ -2863,6 +2863,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>markexported</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>markforreexport</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -3120,6 +3141,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>markexported</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>reexport</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -3830,6 +3872,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>disablecontactvehiclecreation</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>dms_acctnumber</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -7794,6 +7857,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>timezone</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>tt_allow_post_to_invoiced</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -20329,6 +20413,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>comment</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>customerowing</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -25036,6 +25141,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>cost_Additional</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>cost_labor</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -25078,6 +25204,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>cost_sublet</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>costs</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -26930,6 +27077,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>sale_additional</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>sale_labor</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -26972,6 +27140,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>sale_sublet</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>sales</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -32001,6 +32190,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>part_type</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>quantity</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -35523,6 +35733,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>exported_payroll</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
@@ -36104,6 +36335,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>comment</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>compact</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -38903,6 +39155,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>dailyactual</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>dailytarget</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -38966,6 +39239,48 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>todateactual</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>weeklyactual</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>weeklytarget</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -28,6 +28,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import BillMarkExportedButton from "../bill-mark-exported-button/bill-mark-exported-button.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
@@ -107,7 +108,7 @@ export function BillDetailEditcontainer({
|
||||
);
|
||||
|
||||
billlines.forEach((billline) => {
|
||||
const { deductedfromlbr, ...il } = billline;
|
||||
const { deductedfromlbr, jobline, ...il } = billline;
|
||||
delete il.__typename;
|
||||
|
||||
if (il.id) {
|
||||
@@ -206,6 +207,8 @@ export function BillDetailEditcontainer({
|
||||
cost: i.actual_cost,
|
||||
quantity: i.quantity,
|
||||
joblineid: i.joblineid,
|
||||
oem_partno: i.jobline && i.jobline.oem_partno,
|
||||
part_type: i.jobline && i.jobline.part_type,
|
||||
};
|
||||
}),
|
||||
isReturn: true,
|
||||
@@ -234,6 +237,7 @@ export function BillDetailEditcontainer({
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
<BillReeportButtonComponent bill={data && data.bills_by_pk} />
|
||||
<BillMarkExportedButton bill={data && data.bills_by_pk} />
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -93,6 +93,7 @@ function BillEnterModalContainer({
|
||||
deductedfromlbr,
|
||||
lbr_adjustment,
|
||||
location: lineLocation,
|
||||
part_type,
|
||||
...restI
|
||||
} = i;
|
||||
|
||||
@@ -216,7 +217,11 @@ function BillEnterModalContainer({
|
||||
|
||||
if (enterAgain) {
|
||||
form.resetFields();
|
||||
form.setFieldsValue({ ...form.getFieldsValue(), billlines: [] });
|
||||
form.resetFields();
|
||||
form.setFieldsValue({
|
||||
...formValues,
|
||||
billlines: [],
|
||||
});
|
||||
} else {
|
||||
toggleModalVisible();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, notification } from "antd";
|
||||
import { gql } from "@apollo/client";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {
|
||||
selectAuthLevel,
|
||||
selectBodyshop,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
authLevel: selectAuthLevel,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(BillMarkExportedButton);
|
||||
|
||||
export function BillMarkExportedButton({ bodyshop, authLevel, bill }) {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [updateBill] = useMutation(gql`
|
||||
mutation UPDATE_BILL($billId: uuid!) {
|
||||
update_bills(where: { id: { _eq: $billId } }, _set: { exported: true }) {
|
||||
returning {
|
||||
id
|
||||
exported
|
||||
exported_at
|
||||
}
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
const handleUpdate = async () => {
|
||||
setLoading(true);
|
||||
const result = await updateBill({
|
||||
variables: { billId: bill.id },
|
||||
});
|
||||
|
||||
if (!result.errors) {
|
||||
notification["success"]({
|
||||
message: t("bills.successes.markexported"),
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("bills.errors.saving", {
|
||||
error: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
//Get the owner details, populate it all back into the job.
|
||||
};
|
||||
|
||||
const hasAccess = HasRbacAccess({
|
||||
bodyshop,
|
||||
authLevel,
|
||||
action: "bills:reexport",
|
||||
});
|
||||
|
||||
if (hasAccess)
|
||||
return (
|
||||
<Button
|
||||
loading={loading}
|
||||
disabled={bill.exported}
|
||||
onClick={handleUpdate}
|
||||
>
|
||||
{t("bills.labels.markexported")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
return <></>;
|
||||
}
|
||||
@@ -76,6 +76,7 @@ export function BillsListTableComponent({
|
||||
quantity: i.quantity,
|
||||
joblineid: i.joblineid,
|
||||
oem_partno: i.jobline && i.jobline.oem_partno,
|
||||
part_type: i.jobline && i.jobline.part_type,
|
||||
};
|
||||
}),
|
||||
isReturn: true,
|
||||
|
||||
@@ -9,7 +9,7 @@ import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
|
||||
import CourtesyCarStatus from "../courtesy-car-status-select/courtesy-car-status-select.component";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
//import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
@@ -32,7 +32,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
}
|
||||
/>
|
||||
|
||||
<FormFieldsChanged form={form} />
|
||||
{/* <FormFieldsChanged form={form} /> */}
|
||||
<LayoutFormRow header={t("courtesycars.labels.vehicle")}>
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.make")}
|
||||
|
||||
@@ -97,7 +97,9 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
||||
render: (text, record) =>
|
||||
record.cccontracts.length === 1 ? (
|
||||
<Link to={`/manage/jobs/${record.cccontracts[0].job.id}`}>
|
||||
{record.cccontracts[0].job.ro_number}
|
||||
{`${record.cccontracts[0].job.ro_number} - ${
|
||||
record.cccontracts[0].job.ownr_fn || ""
|
||||
} ${record.cccontracts[0].job.ownr_ln || ""} ${record.cccontracts[0].job.ownr_co_nm || ""}`}
|
||||
</Link>
|
||||
) : null,
|
||||
},
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function DashboardMonthlyEmployeeEfficiency({
|
||||
(dayAcc, dayVal) => {
|
||||
return {
|
||||
actual: dayAcc.actual + dayVal.actualhrs,
|
||||
productive: dayAcc.actual + dayVal.productivehrs,
|
||||
productive: dayAcc.productive + dayVal.productivehrs,
|
||||
};
|
||||
},
|
||||
{ actual: 0, productive: 0 }
|
||||
@@ -50,11 +50,13 @@ export default function DashboardMonthlyEmployeeEfficiency({
|
||||
}
|
||||
|
||||
const dailyEfficiency =
|
||||
((dailyHrs.productive - dailyHrs.actual) / dailyHrs.productive + 1) * 100;
|
||||
((dailyHrs.productive - dailyHrs.actual) / dailyHrs.actual + 1) * 100;
|
||||
|
||||
const theValue = {
|
||||
date: moment(val).format("DD"),
|
||||
...dailyHrs,
|
||||
// ...dailyHrs,
|
||||
actual: dailyHrs.actual.toFixed(1),
|
||||
productive: dailyHrs.productive.toFixed(1),
|
||||
dailyEfficiency: isNaN(dailyEfficiency) ? 0 : dailyEfficiency.toFixed(1),
|
||||
accActual:
|
||||
acc.length > 0
|
||||
@@ -67,12 +69,18 @@ export default function DashboardMonthlyEmployeeEfficiency({
|
||||
: dailyHrs.productive,
|
||||
accEfficiency: 0,
|
||||
};
|
||||
theValue.accEfficiency = (
|
||||
|
||||
theValue.accEfficiency =
|
||||
((theValue.accProductive - theValue.accActual) /
|
||||
(theValue.accProductive || 1) +
|
||||
(theValue.accActual || 0) +
|
||||
1) *
|
||||
100
|
||||
).toFixed(1);
|
||||
100;
|
||||
|
||||
if (isNaN(theValue.accEfficiency)) {
|
||||
theValue.accEfficiency = 0;
|
||||
} else {
|
||||
theValue.accEfficiency = theValue.accEfficiency.toFixed(1);
|
||||
}
|
||||
|
||||
return [...acc, theValue];
|
||||
}, []);
|
||||
@@ -131,6 +139,7 @@ export default function DashboardMonthlyEmployeeEfficiency({
|
||||
//stackId="day"
|
||||
barSize={20}
|
||||
fill="#102568"
|
||||
format={"0.0"}
|
||||
/>
|
||||
<Bar
|
||||
name="Productive Hours"
|
||||
@@ -140,6 +149,7 @@ export default function DashboardMonthlyEmployeeEfficiency({
|
||||
//stackId="day"
|
||||
barSize={20}
|
||||
fill="#017664"
|
||||
format={"0.0"}
|
||||
/>
|
||||
</ComposedChart>
|
||||
</ResponsiveContainer>
|
||||
|
||||
@@ -31,16 +31,51 @@ export default function DashboardProjectedMonthlySales({ data, ...cardProps }) {
|
||||
}
|
||||
|
||||
export const DashboardProjectedMonthlySalesGql = `
|
||||
projected_monthly_sales: jobs(where: {_or: [{_and: [{date_invoiced: {_gte: "${moment()
|
||||
projected_monthly_sales: jobs(where: {
|
||||
voided: {_eq: false},
|
||||
_or: [
|
||||
{_and: [
|
||||
{date_invoiced:{_is_null: false }},
|
||||
{date_invoiced: {_gte: "${moment()
|
||||
.startOf("month")
|
||||
.startOf("day")
|
||||
.toISOString()}"}}, {date_invoiced: {_lte: "${moment()
|
||||
.endOf("month")
|
||||
.endOf("day")
|
||||
.toISOString()}"}}]},
|
||||
{
|
||||
|
||||
_and:[
|
||||
{date_invoiced:{_is_null: true }},
|
||||
{actual_completion: {_gte: "${moment()
|
||||
.startOf("month")
|
||||
.startOf("day")
|
||||
.toISOString()}"}}, {actual_completion: {_lte: "${moment()
|
||||
.endOf("month")
|
||||
.endOf("day")
|
||||
.toISOString()}"}}
|
||||
|
||||
]
|
||||
},
|
||||
|
||||
{_and: [
|
||||
{date_invoiced: {_is_null: true}},
|
||||
{actual_completion: {_is_null: true}}
|
||||
{scheduled_completion: {_gte: "${moment()
|
||||
.startOf("month")
|
||||
.format("YYYY-MM-DD")}"}}, {date_invoiced: {_lte: "${moment()
|
||||
.startOf("day")
|
||||
.toISOString()}"}}, {scheduled_completion: {_lte: "${moment()
|
||||
.endOf("month")
|
||||
.format("YYYY-MM-DD")}"}}]}, {_and: [{scheduled_completion: {_gte: "${moment()
|
||||
.startOf("month")
|
||||
.format("YYYY-MM-DD")}"}}, {scheduled_completion: {_lte: "${moment()
|
||||
.endOf("month")
|
||||
.format("YYYY-MM-DD")}"}}]}]}) {
|
||||
.endOf("day")
|
||||
.toISOString()}"}}
|
||||
|
||||
|
||||
]}
|
||||
|
||||
]}) {
|
||||
id
|
||||
ro_number
|
||||
voided
|
||||
date_invoiced
|
||||
job_totals
|
||||
}
|
||||
|
||||
@@ -280,12 +280,13 @@ const createDashboardQuery = (state) => {
|
||||
return gql`
|
||||
query QUERY_DASHBOARD_DETAILS {
|
||||
${componentBasedAdditions || ""}
|
||||
monthly_sales: jobs(where: {_and: [{date_invoiced: {_gte: "${moment()
|
||||
.startOf("month")
|
||||
.format("YYYY-MM-DD")}"}}, {date_invoiced: {_lte: "${moment()
|
||||
.endOf("month")
|
||||
.format("YYYY-MM-DD")}"}}]}) {
|
||||
monthly_sales: jobs(where: {_and: [
|
||||
{ voided: {_eq: false}},
|
||||
{date_invoiced: {_gte: "${moment()
|
||||
.startOf("month").startOf('day').toISOString()}"}}, {date_invoiced: {_lte: "${moment()
|
||||
.endOf("month").endOf('day').toISOString()}"}}]}) {
|
||||
id
|
||||
ro_number
|
||||
date_invoiced
|
||||
job_totals
|
||||
rate_la1
|
||||
@@ -333,14 +334,14 @@ const createDashboardQuery = (state) => {
|
||||
part_qty
|
||||
part_type
|
||||
}
|
||||
labhrs: joblines_aggregate(where: { mod_lbr_ty: { _neq: "LAR" } }) {
|
||||
labhrs: joblines_aggregate(where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }) {
|
||||
aggregate {
|
||||
sum {
|
||||
mod_lb_hrs
|
||||
}
|
||||
}
|
||||
}
|
||||
larhrs: joblines_aggregate(where: { mod_lbr_ty: { _eq: "LAR" } }) {
|
||||
larhrs: joblines_aggregate(where: { mod_lbr_ty: { _eq: "LAR" }, removed: { _eq: false } }) {
|
||||
aggregate {
|
||||
sum {
|
||||
mod_lb_hrs
|
||||
|
||||
@@ -6,6 +6,7 @@ import { INSERT_NEW_DOCUMENT } from "../../graphql/documents.queries";
|
||||
import { axiosAuthInterceptorId } from "../../utils/CleanAxios";
|
||||
import client from "../../utils/GraphQLClient";
|
||||
import exifr from "exifr";
|
||||
import { store } from "../../redux/store";
|
||||
|
||||
//Context: currentUserEmail, bodyshop, jobid, invoiceid
|
||||
|
||||
@@ -112,7 +113,19 @@ export const uploadToCloudinary = async (
|
||||
);
|
||||
|
||||
if (cloudinaryUploadResponse.status !== 200) {
|
||||
if (!!onError) onError(cloudinaryUploadResponse.statusText);
|
||||
if (!!onError) {
|
||||
onError(cloudinaryUploadResponse.statusText);
|
||||
}
|
||||
|
||||
try {
|
||||
axios.post("/newlog", {
|
||||
message: "client-cloudinary-upload-error",
|
||||
type: "error",
|
||||
user: store.getState().user.email,
|
||||
object: cloudinaryUploadResponse,
|
||||
});
|
||||
} catch (error) {}
|
||||
|
||||
notification["error"]({
|
||||
message: i18n.t("documents.errors.insert", {
|
||||
message: cloudinaryUploadResponse.statusText,
|
||||
|
||||
@@ -3,27 +3,41 @@ import moment from "moment";
|
||||
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,
|
||||
onlyFuture,
|
||||
isDateOnly = true,
|
||||
...restProps
|
||||
}) {
|
||||
const ref = useRef();
|
||||
|
||||
const handleChange = (newDate) => {
|
||||
if (value !== newDate && onChange) {
|
||||
onChange(newDate);
|
||||
onChange(isDateOnly ? newDate && newDate.format("YYYY-MM-DD") : newDate);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
if (e.key.toLowerCase() === "t") {
|
||||
if (onChange) {
|
||||
onChange(new moment());
|
||||
onChange(isDateOnly ? moment().format("YYYY-MM-DD") : moment());
|
||||
// if (ref.current && ref.current.blur) ref.current.blur();
|
||||
}
|
||||
} else if (e.key.toLowerCase() === "enter") {
|
||||
@@ -51,7 +65,8 @@ export default function FormDatePicker({
|
||||
});
|
||||
}
|
||||
|
||||
if (_a.isValid() && onChange) onChange(_a);
|
||||
if (_a.isValid() && onChange)
|
||||
onChange(isDateOnly ? _a.format("YYYY-MM-DD") : _a);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -62,6 +77,7 @@ export default function FormDatePicker({
|
||||
onChange={handleChange}
|
||||
format={dateFormat}
|
||||
onBlur={onBlur || handleBlur}
|
||||
showToday={false}
|
||||
disabledTime
|
||||
{...(onlyFuture && {
|
||||
disabledDate: (d) => moment().subtract(1, "day").isAfter(d),
|
||||
|
||||
@@ -26,19 +26,20 @@ const DateTimePicker = (
|
||||
value={value}
|
||||
onBlur={onBlur}
|
||||
onChange={onChange}
|
||||
isDateOnly={false}
|
||||
/>
|
||||
|
||||
<TimePicker
|
||||
{...restProps}
|
||||
value={value ? moment(value) : null}
|
||||
{...(onlyFuture && {
|
||||
disabledDate: (d) => moment().isAfter(d),
|
||||
})}
|
||||
onChange={onChange}
|
||||
showSecond={false}
|
||||
minuteStep={15}
|
||||
onBlur={onBlur}
|
||||
format="hh:mm a"
|
||||
value={value ? moment(value) : null}
|
||||
{...(onlyFuture && {
|
||||
disabledDate: (d) => moment().isAfter(d),
|
||||
})}
|
||||
onChange={onChange}
|
||||
showSecond={false}
|
||||
minuteStep={15}
|
||||
onBlur={onBlur}
|
||||
format="hh:mm a"
|
||||
{...restProps}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -72,7 +72,7 @@ export default function JobBillsTotalComponent({
|
||||
|
||||
const discrepWithLbrAdj = discrepancy.add(lbrAdjustments);
|
||||
|
||||
const discrepWithCms = discrepWithLbrAdj.add(billCms);
|
||||
const discrepWithCms = discrepWithLbrAdj.add(totalReturns);
|
||||
const creditsNotReceived = totalReturns.subtract(billCms); //billCms is tracked as a negative number.
|
||||
|
||||
return (
|
||||
@@ -171,8 +171,8 @@ export default function JobBillsTotalComponent({
|
||||
}
|
||||
>
|
||||
<Statistic
|
||||
title={t("bills.labels.billcmtotal")}
|
||||
value={billCms.toFormat()}
|
||||
title={t("bills.labels.totalreturns")}
|
||||
value={totalReturns.toFormat()}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Typography.Title>=</Typography.Title>
|
||||
|
||||
@@ -26,11 +26,6 @@ export function JobCostingModalContainer({
|
||||
const { visible, context } = jobCostingModal;
|
||||
const { jobId } = context;
|
||||
|
||||
// const { loading, error, data } = useQuery(QUERY_JOB_COSTING_DETAILS, {
|
||||
// variables: { id: jobId },
|
||||
// skip: !jobId,
|
||||
// });
|
||||
|
||||
useEffect(() => {
|
||||
async function getData() {
|
||||
if (jobId && visible) {
|
||||
@@ -46,8 +41,14 @@ export function JobCostingModalContainer({
|
||||
<Modal
|
||||
visible={visible}
|
||||
title={t("jobs.labels.jobcosting")}
|
||||
onOk={() => toggleModalVisible()}
|
||||
onCancel={() => toggleModalVisible()}
|
||||
onOk={() => {
|
||||
toggleModalVisible();
|
||||
setCostingData(null);
|
||||
}}
|
||||
onCancel={() => {
|
||||
toggleModalVisible();
|
||||
setCostingData(null);
|
||||
}}
|
||||
cancelButtonProps={{ style: { display: "none" } }}
|
||||
width="90%"
|
||||
destroyOnClose
|
||||
|
||||
@@ -16,6 +16,14 @@ export default function JobCostingStatistics({ summaryData }) {
|
||||
value={Dinero(summaryData.totalPartsSales).toFormat()}
|
||||
title={t("jobs.labels.sale_parts")}
|
||||
/>
|
||||
<Statistic
|
||||
value={Dinero(summaryData.totalSubletSales).toFormat()}
|
||||
title={t("jobs.labels.sale_sublet")}
|
||||
/>
|
||||
<Statistic
|
||||
value={Dinero(summaryData.totalAdditionalSales).toFormat()}
|
||||
title={t("jobs.labels.sale_additional")}
|
||||
/>
|
||||
<Statistic
|
||||
value={Dinero(summaryData.totalSales).toFormat()}
|
||||
title={t("jobs.labels.total_sales")}
|
||||
@@ -28,6 +36,14 @@ export default function JobCostingStatistics({ summaryData }) {
|
||||
value={Dinero(summaryData.totalPartsCost).toFormat()}
|
||||
title={t("jobs.labels.cost_parts")}
|
||||
/>
|
||||
<Statistic
|
||||
value={Dinero(summaryData.totalSubletCost).toFormat()}
|
||||
title={t("jobs.labels.cost_sublet")}
|
||||
/>
|
||||
<Statistic
|
||||
value={Dinero(summaryData.totalAdditionalCost).toFormat()}
|
||||
title={t("jobs.labels.cost_Additional")}
|
||||
/>
|
||||
<Statistic
|
||||
value={Dinero(summaryData.totalCost).toFormat()}
|
||||
title={t("jobs.labels.total_cost")}
|
||||
|
||||
@@ -55,15 +55,17 @@ export function JobEmployeeAssignments({
|
||||
0
|
||||
}
|
||||
>
|
||||
{bodyshop.employees.map((emp) => (
|
||||
<Select.Option
|
||||
value={emp.id}
|
||||
key={emp.id}
|
||||
name={`${emp.first_name} ${emp.last_name}`}
|
||||
>
|
||||
{`${emp.first_name} ${emp.last_name}`}
|
||||
</Select.Option>
|
||||
))}
|
||||
{bodyshop.employees
|
||||
.filter((emp) => emp.active)
|
||||
.map((emp) => (
|
||||
<Select.Option
|
||||
value={emp.id}
|
||||
key={emp.id}
|
||||
name={`${emp.first_name} ${emp.last_name}`}
|
||||
>
|
||||
{`${emp.first_name} ${emp.last_name}`}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
|
||||
@@ -10,7 +10,7 @@ export default function JobLinesBillRefernece({ jobline }) {
|
||||
{subletRequired && <WarningFilled />}
|
||||
{`${(billLine.actual_price * billLine.quantity).toFixed(2)} (${
|
||||
billLine.bill.vendor.name
|
||||
})`}
|
||||
} #${billLine.bill.invoice_number})`}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -33,10 +33,10 @@ export default function JobTotalsTableOther({ job }) {
|
||||
key: t("jobs.fields.storage_payable"),
|
||||
total: job.job_totals.additional.storage,
|
||||
},
|
||||
{
|
||||
key: t("jobs.fields.ca_bc_pvrt"),
|
||||
total: job.job_totals.additional.pvrt,
|
||||
},
|
||||
// {
|
||||
// key: t("jobs.fields.ca_bc_pvrt"),
|
||||
// total: job.job_totals.additional.pvrt,
|
||||
// },
|
||||
];
|
||||
}, [job.job_totals, t]);
|
||||
|
||||
|
||||
@@ -3,7 +3,22 @@ import Dinero from "dinero.js";
|
||||
import React, { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function JobTotalsTableTotals({ job }) {
|
||||
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
|
||||
)(JobTotalsTableTotals);
|
||||
|
||||
export function JobTotalsTableTotals({ bodyshop, job }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const data = useMemo(() => {
|
||||
@@ -21,6 +36,14 @@ export default function JobTotalsTableTotals({ job }) {
|
||||
key: t("jobs.labels.state_tax_amt"),
|
||||
total: job.job_totals.totals.state_tax,
|
||||
},
|
||||
...(bodyshop.region_config === "CA_BC"
|
||||
? [
|
||||
{
|
||||
key: t("jobs.fields.ca_bc_pvrt"),
|
||||
total: job.job_totals.additional.pvrt,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
key: t("jobs.labels.federal_tax_amt"),
|
||||
total: job.job_totals.totals.federal_tax,
|
||||
@@ -58,7 +81,7 @@ export default function JobTotalsTableTotals({ job }) {
|
||||
bold: true,
|
||||
},
|
||||
];
|
||||
}, [job.job_totals, t]);
|
||||
}, [job.job_totals, t, bodyshop.region_config]);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@ import ProductionListColumnProductionNote from "../production-list-columns/produ
|
||||
import "./jobs-detail-header.styles.scss";
|
||||
import JobsRelatedRos from "../jobs-related-ros/jobs-related-ros.component";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import ProductionListColumnComment from "../production-list-columns/production-list-columns.comment.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly,
|
||||
@@ -86,6 +87,12 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
||||
) : null}
|
||||
</Space>
|
||||
</DataLabel>
|
||||
<DataLabel
|
||||
label={t("jobs.fields.comment")}
|
||||
valueStyle={{ overflow: "hidden", textOverflow: "ellipsis" }}
|
||||
>
|
||||
<ProductionListColumnComment record={job} />
|
||||
</DataLabel>
|
||||
<DataLabel label={t("jobs.fields.ins_co_nm_short")}>
|
||||
{job.ins_co_nm}
|
||||
</DataLabel>
|
||||
|
||||
@@ -30,7 +30,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "ro_number",
|
||||
key: "ro_number",
|
||||
width: "8%",
|
||||
|
||||
sorter: true, //(a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||
sortOrder: sortcolumn === "ro_number" && sortorder,
|
||||
|
||||
@@ -47,7 +47,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
key: "ownr_ln",
|
||||
ellipsis: true,
|
||||
//sorter: true, // (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||
width: "25%",
|
||||
|
||||
//sortOrder: sortcolumn === "ownr_ln" && sortorder,
|
||||
render: (text, record) => {
|
||||
return record.ownerid ? (
|
||||
@@ -67,7 +67,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
title: t("jobs.fields.ownr_ph1"),
|
||||
dataIndex: "ownr_ph1",
|
||||
key: "ownr_ph1",
|
||||
width: "12%",
|
||||
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<StartChatButton phone={record.ownr_ph1} jobid={record.id} />
|
||||
@@ -77,7 +77,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
title: t("jobs.fields.ownr_ph2"),
|
||||
dataIndex: "ownr_ph2",
|
||||
key: "ownr_ph2",
|
||||
width: "12%",
|
||||
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<StartChatButton phone={record.ownr_ph2} jobid={record.id} />
|
||||
@@ -87,7 +87,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
title: t("jobs.fields.status"),
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
width: "10%",
|
||||
|
||||
ellipsis: true,
|
||||
sorter: true, // (a, b) => alphaSort(a.status, b.status),
|
||||
sortOrder: sortcolumn === "status" && sortorder,
|
||||
@@ -104,7 +104,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
title: t("jobs.fields.vehicle"),
|
||||
dataIndex: "vehicle",
|
||||
key: "vehicle",
|
||||
width: "15%",
|
||||
|
||||
ellipsis: true,
|
||||
render: (text, record) => {
|
||||
return record.vehicleid ? (
|
||||
@@ -124,7 +124,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
title: t("vehicles.fields.plate_no"),
|
||||
dataIndex: "plate_no",
|
||||
key: "plate_no",
|
||||
width: "8%",
|
||||
|
||||
ellipsis: true,
|
||||
sorter: true, //(a, b) => alphaSort(a.plate_no, b.plate_no),
|
||||
sortOrder: sortcolumn === "plate_no" && sortorder,
|
||||
@@ -136,7 +136,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
title: t("jobs.fields.clm_no"),
|
||||
dataIndex: "clm_no",
|
||||
key: "clm_no",
|
||||
width: "12%",
|
||||
|
||||
ellipsis: true,
|
||||
sorter: true, //(a, b) => alphaSort(a.clm_no, b.clm_no),
|
||||
sortOrder: sortcolumn === "clm_no" && sortorder,
|
||||
@@ -155,7 +155,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
title: t("jobs.fields.clm_total"),
|
||||
dataIndex: "clm_total",
|
||||
key: "clm_total",
|
||||
width: "10%",
|
||||
|
||||
sorter: true, //(a, b) => a.clm_total - b.clm_total,
|
||||
sortOrder: sortcolumn === "clm_total" && sortorder,
|
||||
render: (text, record) => {
|
||||
@@ -170,11 +170,17 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
title: t("jobs.fields.owner_owing"),
|
||||
dataIndex: "owner_owing",
|
||||
key: "owner_owing",
|
||||
width: "8%",
|
||||
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatter>{record.owner_owing}</CurrencyFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.comment"),
|
||||
dataIndex: "comment",
|
||||
key: "comment",
|
||||
ellipsis: true,
|
||||
},
|
||||
];
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
@@ -224,7 +230,6 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
>
|
||||
<Table
|
||||
loading={loading}
|
||||
scroll={{ x: true }}
|
||||
pagination={{
|
||||
position: "top",
|
||||
pageSize: 25,
|
||||
|
||||
@@ -60,6 +60,9 @@ export function JobsList({ bodyshop }) {
|
||||
(j.ownr_co_nm || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.comments || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_fn || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
@@ -262,6 +265,13 @@ export function JobsList({ bodyshop }) {
|
||||
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.comment"),
|
||||
dataIndex: "comment",
|
||||
key: "comment",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
},
|
||||
// {
|
||||
// title: t("jobs.fields.owner_owing"),
|
||||
// dataIndex: "owner_owing",
|
||||
|
||||
@@ -124,7 +124,7 @@ export function JobNotesComponent({
|
||||
messageObject={{
|
||||
subject: Templates.individual_job_note.subject,
|
||||
}}
|
||||
id={record.id}
|
||||
id={jobId}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
|
||||
@@ -27,11 +27,11 @@ export function PartnerPingComponent({ bodyshop, setPartnerVersion }) {
|
||||
useEffect(() => {
|
||||
// Create an scoped async function in the hook
|
||||
async function checkPartnerStatus() {
|
||||
if (!bodyshop) return;
|
||||
try {
|
||||
//if (process.env.NODE_ENV === "development") return;
|
||||
const PartnerResponse = await axios.post("http://localhost:1337/ping/");
|
||||
const { appver, qbpath } = PartnerResponse.data;
|
||||
if (!bodyshop) return;
|
||||
setPartnerVersion(appver);
|
||||
console.log({ appver, qbpath });
|
||||
if (
|
||||
|
||||
@@ -319,6 +319,15 @@ export function PartsOrderListTableComponent({
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: t("parts_orders.fields.part_type"),
|
||||
dataIndex: "part_type",
|
||||
key: "part_type",
|
||||
render: (text, record) =>
|
||||
record.part_type
|
||||
? t(`joblines.fields.part_types.${record.part_type}`)
|
||||
: null,
|
||||
},
|
||||
{
|
||||
title: t("parts_orders.fields.oem_partno"),
|
||||
dataIndex: "oem_partno",
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import { DeleteFilled, WarningFilled } from "@ant-design/icons";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Divider, Form, Input, InputNumber, Radio, Space, Tag } from "antd";
|
||||
import {
|
||||
Divider,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
Radio,
|
||||
Space,
|
||||
Tag,
|
||||
Select,
|
||||
} from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -37,6 +46,11 @@ export function PartsOrderModalComponent({
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
const { OEConnection_PriceChange } = useTreatments(
|
||||
["OEConnection_PriceChange"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
@@ -59,6 +73,7 @@ export function PartsOrderModalComponent({
|
||||
options={vendorList}
|
||||
disabled={isReturn}
|
||||
preferredMake={preferredMake}
|
||||
showPhone
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
@@ -114,6 +129,52 @@ export function PartsOrderModalComponent({
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("parts_orders.fields.part_type")}
|
||||
key={`${index}part_type`}
|
||||
name={[field.name, "part_type"]}
|
||||
>
|
||||
<Select
|
||||
disabled={
|
||||
!(
|
||||
sendType === "oec" &&
|
||||
OEConnection_PriceChange.treatment === "on"
|
||||
)
|
||||
}
|
||||
>
|
||||
<Select.Option value="PAA">
|
||||
{t("joblines.fields.part_types.PAA")}
|
||||
</Select.Option>
|
||||
<Select.Option value="PAC">
|
||||
{t("joblines.fields.part_types.PAC")}
|
||||
</Select.Option>
|
||||
|
||||
<Select.Option value="PAL">
|
||||
{t("joblines.fields.part_types.PAL")}
|
||||
</Select.Option>
|
||||
<Select.Option value="PAG">
|
||||
{t("joblines.fields.part_types.PAG")}
|
||||
</Select.Option>
|
||||
<Select.Option value="PAM">
|
||||
{t("joblines.fields.part_types.PAM")}
|
||||
</Select.Option>
|
||||
<Select.Option value="PAP">
|
||||
{t("joblines.fields.part_types.PAP")}
|
||||
</Select.Option>
|
||||
<Select.Option value="PAN">
|
||||
{t("joblines.fields.part_types.PAN")}
|
||||
</Select.Option>
|
||||
<Select.Option value="PAO">
|
||||
{t("joblines.fields.part_types.PAO")}
|
||||
</Select.Option>
|
||||
<Select.Option value="PAR">
|
||||
{t("joblines.fields.part_types.PAR")}
|
||||
</Select.Option>
|
||||
<Select.Option value="PAS">
|
||||
{t("joblines.fields.part_types.PAS")}
|
||||
</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("parts_orders.fields.oem_partno")}
|
||||
key={`${index}oem_partno`}
|
||||
@@ -192,7 +253,7 @@ export function PartsOrderModalComponent({
|
||||
<Radio value={"none"}>{t("general.labels.none")}</Radio>
|
||||
<Radio value={"e"}>{t("parts_orders.labels.email")}</Radio>
|
||||
<Radio value={"p"}>{t("parts_orders.labels.print")}</Radio>
|
||||
{OEConnection.treatment === "on" && (
|
||||
{OEConnection.treatment === "on" && !isReturn && (
|
||||
<Radio value={"oec"}>{t("parts_orders.labels.oec")}</Radio>
|
||||
)}
|
||||
</Radio.Group>
|
||||
|
||||
@@ -77,7 +77,7 @@ export function PartsOrderModalContainer({
|
||||
|
||||
const { refetch } = actions;
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const [saving, setSaving] = useState(false);
|
||||
const sendTypeState = useState("e");
|
||||
const sendType = sendTypeState[0];
|
||||
|
||||
@@ -93,7 +93,7 @@ export function PartsOrderModalContainer({
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
logImEXEvent("parts_order_insert");
|
||||
|
||||
setSaving(true);
|
||||
const insertResult = await insertPartOrder({
|
||||
variables: {
|
||||
po: [
|
||||
@@ -196,6 +196,11 @@ export function PartsOrderModalContainer({
|
||||
(item) => item.id === values.vendorid
|
||||
)[0];
|
||||
|
||||
let vendorEmails =
|
||||
matchingVendor &&
|
||||
matchingVendor.email &&
|
||||
matchingVendor.email.split(RegExp("[;,]"));
|
||||
|
||||
GenerateDocument(
|
||||
{
|
||||
name: isReturn
|
||||
@@ -206,7 +211,7 @@ export function PartsOrderModalContainer({
|
||||
},
|
||||
},
|
||||
{
|
||||
to: matchingVendor ? [matchingVendor.email] : null,
|
||||
to: matchingVendor ? vendorEmails : null,
|
||||
replyTo: bodyshop.email,
|
||||
subject: isReturn
|
||||
? Templates.parts_return_slip.subject
|
||||
@@ -247,8 +252,6 @@ export function PartsOrderModalContainer({
|
||||
});
|
||||
}
|
||||
|
||||
console.log(partsOrder.data.parts_orders_by_pk);
|
||||
|
||||
const oecResponse = await axios.post(
|
||||
"http://localhost:1337/oec/",
|
||||
|
||||
@@ -275,11 +278,11 @@ export function PartsOrderModalContainer({
|
||||
error: JSON.stringify(error.message),
|
||||
}),
|
||||
});
|
||||
|
||||
setSaving(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setSaving(false);
|
||||
toggleModalVisible();
|
||||
};
|
||||
|
||||
@@ -301,6 +304,7 @@ export function PartsOrderModalContainer({
|
||||
cost: value.cost,
|
||||
quantity: value.part_qty,
|
||||
job_line_id: isReturn ? value.joblineid : value.id,
|
||||
part_type: value.part_type,
|
||||
});
|
||||
return acc;
|
||||
}, [])
|
||||
@@ -324,6 +328,8 @@ export function PartsOrderModalContainer({
|
||||
}
|
||||
onCancel={() => toggleModalVisible()}
|
||||
onOk={() => form.submit()}
|
||||
okButtonProps={{ loading: saving }}
|
||||
cancelButtonProps={{ loading: saving }}
|
||||
destroyOnClose
|
||||
width="75%"
|
||||
forceRender
|
||||
|
||||
@@ -22,7 +22,7 @@ export default function ProductionBoardCard(
|
||||
) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
let employee_body, employee_prep, employee_refinish; //employee_csr;
|
||||
let employee_body, employee_prep, employee_refinish, employee_csr;
|
||||
if (card.employee_body) {
|
||||
employee_body = bodyshop.employees.find((e) => e.id === card.employee_body);
|
||||
}
|
||||
@@ -34,6 +34,9 @@ export default function ProductionBoardCard(
|
||||
(e) => e.id === card.employee_refinish
|
||||
);
|
||||
}
|
||||
if (card.employee_csr) {
|
||||
employee_csr = bodyshop.employees.find((e) => e.id === card.employee_csr);
|
||||
}
|
||||
// if (card.employee_csr) {
|
||||
// employee_csr = bodyshop.employees.find((e) => e.id === card.employee_csr);
|
||||
// }
|
||||
@@ -131,11 +134,11 @@ export default function ProductionBoardCard(
|
||||
)} ${employee_refinish.last_name.charAt(0)}`
|
||||
: ""
|
||||
} ${card.larhrs.aggregate.sum.mod_lb_hrs || "?"}h`}</Col>
|
||||
{/* <Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`C: ${
|
||||
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`C: ${
|
||||
employee_csr
|
||||
? `${employee_csr.first_name} ${employee_csr.last_name}`
|
||||
: ""
|
||||
}`}</Col> */}
|
||||
}`}</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
|
||||
@@ -66,6 +66,7 @@ export const createBoardData = (AllStatuses, Jobs, filter) => {
|
||||
include ||
|
||||
j.employee_body === employeeId ||
|
||||
j.employee_prep === employeeId ||
|
||||
j.employee_csr === employeeId ||
|
||||
j.employee_refinish === employeeId;
|
||||
}
|
||||
|
||||
@@ -76,9 +77,8 @@ export const createBoardData = (AllStatuses, Jobs, filter) => {
|
||||
|
||||
Object.keys(DataGroupedByStatus).map((statusGroupKey) => {
|
||||
try {
|
||||
boardLanes.columns.find(
|
||||
(l) => l.id === statusGroupKey
|
||||
).cards = sortByParentId(DataGroupedByStatus[statusGroupKey]);
|
||||
boardLanes.columns.find((l) => l.id === statusGroupKey).cards =
|
||||
sortByParentId(DataGroupedByStatus[statusGroupKey]);
|
||||
} catch (error) {
|
||||
console.log("Error while creating board card", error);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import Icon from "@ant-design/icons";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Input, Popover } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FaRegStickyNote } from "react-icons/fa";
|
||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
export default function ProductionListColumnComment({ record }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [note, setNote] = useState(record.comment || "");
|
||||
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
const [updateAlert] = useMutation(UPDATE_JOB);
|
||||
|
||||
const handleSaveNote = (e) => {
|
||||
e.stopPropagation();
|
||||
setVisible(false);
|
||||
updateAlert({
|
||||
variables: {
|
||||
jobId: record.id,
|
||||
job: {
|
||||
comment: note,
|
||||
},
|
||||
},
|
||||
}).then(() => {
|
||||
if (record.refetch) record.refetch();
|
||||
});
|
||||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
e.stopPropagation();
|
||||
setNote(e.target.value);
|
||||
};
|
||||
|
||||
const handleVisibleChange = (flag) => {
|
||||
setVisible(flag);
|
||||
if (flag) setNote(record.comment || "");
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover
|
||||
onVisibleChange={handleVisibleChange}
|
||||
visible={visible}
|
||||
content={
|
||||
<div style={{ width: "30em" }}>
|
||||
<Input.TextArea
|
||||
rows={5}
|
||||
value={note}
|
||||
onChange={handleChange}
|
||||
// onPressEnter={handleSaveNote}
|
||||
autoFocus
|
||||
allowClear
|
||||
/>
|
||||
<div>
|
||||
<Button onClick={handleSaveNote}>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
trigger={["click"]}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "19px",
|
||||
cursor: "pointer",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
<Icon component={FaRegStickyNote} style={{ marginRight: ".2rem" }} />
|
||||
{record.comment || " "}
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import ProductionListColumnNote from "./production-list-columns.productionnote.c
|
||||
import ProductionListColumnStatus from "./production-list-columns.status.component";
|
||||
import ProductionListColumnCategory from "./production-list-columns.status.category";
|
||||
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
|
||||
import ProductionListColumnComment from "./production-list-columns.comment.component";
|
||||
|
||||
const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
||||
return [
|
||||
@@ -95,7 +96,7 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "actual_in" && state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<ProductionListDate record={record} field="actual_in" />
|
||||
<ProductionListDate record={record} field="actual_in" time/>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -109,7 +110,12 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
||||
state.sortedInfo.columnKey === "scheduled_completion" &&
|
||||
state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<ProductionListDate record={record} field="scheduled_completion" pastIndicator />
|
||||
<ProductionListDate
|
||||
record={record}
|
||||
field="scheduled_completion"
|
||||
pastIndicator
|
||||
time
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -156,7 +162,12 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
||||
state.sortedInfo.columnKey === "scheduled_delivery" &&
|
||||
state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<ProductionListDate record={record} field="scheduled_delivery" pastIndicator/>
|
||||
<ProductionListDate
|
||||
record={record}
|
||||
field="scheduled_delivery"
|
||||
pastIndicator
|
||||
time
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -325,6 +336,13 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
||||
ellipsis: true,
|
||||
render: (text, record) => <ProductionListColumnNote record={record} />,
|
||||
},
|
||||
{
|
||||
title: i18n.t("production.labels.comment"),
|
||||
dataIndex: "comment",
|
||||
key: "comment",
|
||||
ellipsis: true,
|
||||
render: (text, record) => <ProductionListColumnComment record={record} />,
|
||||
},
|
||||
{
|
||||
title: i18n.t("production.labels.touchtime"),
|
||||
dataIndex: "tt",
|
||||
|
||||
@@ -20,9 +20,9 @@ export default function ProductionListDate({
|
||||
|
||||
const handleChange = (date) => {
|
||||
logImEXEvent("product_toggle_date", { field });
|
||||
if (date.isSame(record[field] && moment(record[field]))) {
|
||||
return;
|
||||
}
|
||||
// if (date.isSame(record[field] && moment(record[field]))) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
//e.stopPropagation();
|
||||
updateAlert({
|
||||
@@ -67,12 +67,14 @@ export default function ProductionListDate({
|
||||
value={(record[field] && moment(record[field])) || null}
|
||||
onChange={handleChange}
|
||||
format="MM/DD/YYYY"
|
||||
isDateOnly={!time}
|
||||
/>
|
||||
{time && (
|
||||
<TimePicker
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
value={(record[field] && moment(record[field])) || null}
|
||||
onChange={handleChange}
|
||||
minuteStep={15}
|
||||
format="hh:mm a"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -116,15 +116,17 @@ export function ProductionListEmpAssignment({
|
||||
0
|
||||
}
|
||||
>
|
||||
{bodyshop.employees.map((emp) => (
|
||||
<Select.Option
|
||||
value={emp.id}
|
||||
key={emp.id}
|
||||
name={`${emp.first_name} ${emp.last_name}`}
|
||||
>
|
||||
{`${emp.first_name} ${emp.last_name}`}
|
||||
</Select.Option>
|
||||
))}
|
||||
{bodyshop.employees
|
||||
.filter((emp) => emp.active)
|
||||
.map((emp) => (
|
||||
<Select.Option
|
||||
value={emp.id}
|
||||
key={emp.id}
|
||||
name={`${emp.first_name} ${emp.last_name}`}
|
||||
>
|
||||
{`${emp.first_name} ${emp.last_name}`}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Descriptions, Drawer, Space } from "antd";
|
||||
import { Descriptions, Drawer, Space, PageHeader, Button } from "antd";
|
||||
import queryString from "query-string";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -16,8 +16,25 @@ import JobEmployeeAssignments from "../job-employee-assignments/job-employee-ass
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
import ProductionRemoveButton from "../production-remove-button/production-remove-button.component";
|
||||
import JobAtChange from "../job-at-change/job-at-change.component";
|
||||
import { PrinterFilled } from "@ant-design/icons";
|
||||
|
||||
export default function ProductionListDetail({ jobs }) {
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setPrintCenterContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "printCenter" })),
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ProductionListDetail);
|
||||
|
||||
export function ProductionListDetail({ jobs, setPrintCenterContext }) {
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const history = useHistory();
|
||||
const { selected } = search;
|
||||
@@ -39,11 +56,29 @@ export default function ProductionListDetail({ jobs }) {
|
||||
return (
|
||||
<Drawer
|
||||
title={
|
||||
<Space>
|
||||
<span>{t("production.labels.jobdetail")}</span>
|
||||
<span>{theJob.ro_number}</span>
|
||||
<ProductionRemoveButton jobId={theJob.id} />
|
||||
</Space>
|
||||
<PageHeader
|
||||
title={theJob.ro_number}
|
||||
extra={
|
||||
<Space>
|
||||
<ProductionRemoveButton jobId={theJob.id} />{" "}
|
||||
<Button
|
||||
onClick={() => {
|
||||
setPrintCenterContext({
|
||||
actions: { refetch: refetch },
|
||||
context: {
|
||||
id: theJob.id,
|
||||
job: theJob,
|
||||
type: "job",
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<PrinterFilled />
|
||||
{t("jobs.actions.printCenter")}
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
}
|
||||
placement="right"
|
||||
width={"33%"}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { CalendarOutlined } from "@ant-design/icons";
|
||||
import { Card, Col, Row, Statistic } from "antd";
|
||||
import React from "react";
|
||||
import _ from "lodash";
|
||||
import moment from "moment";
|
||||
import React, { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
@@ -16,25 +18,78 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
});
|
||||
|
||||
const rowGutter = [16, 16];
|
||||
const statSpans = { xs: 24, sm: 6 };
|
||||
const statSpans = { xs: 24, sm: 3 };
|
||||
|
||||
export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const values = useMemo(() => {
|
||||
const dateHash = _.groupBy(scoreBoardlist, "date");
|
||||
console.log(
|
||||
"🚀 ~ file: scoreboard-targets-table.component.jsx ~ line 31 ~ values ~ dateHash",
|
||||
dateHash
|
||||
);
|
||||
|
||||
let ret = {
|
||||
todayBody: 0,
|
||||
todayPaint: 0,
|
||||
weeklyPaint: 0,
|
||||
weeklyBody: 0,
|
||||
toDateBody: 0,
|
||||
toDatePaint: 0,
|
||||
};
|
||||
|
||||
const today = moment();
|
||||
if (dateHash[today.format("YYYY-MM-DD")]) {
|
||||
dateHash[today.format("YYYY-MM-DD")].forEach((d) => {
|
||||
ret.todayBody = ret.todayBody + d.bodyhrs;
|
||||
ret.todayPaint = ret.todayPaint + d.painthrs;
|
||||
});
|
||||
}
|
||||
|
||||
let StartOfWeek = moment().startOf("week");
|
||||
while (StartOfWeek.isSameOrBefore(today)) {
|
||||
if (dateHash[StartOfWeek.format("YYYY-MM-DD")]) {
|
||||
dateHash[StartOfWeek.format("YYYY-MM-DD")].forEach((d) => {
|
||||
ret.weeklyBody = ret.weeklyBody + d.bodyhrs;
|
||||
ret.weeklyPaint = ret.weeklyPaint + d.painthrs;
|
||||
});
|
||||
}
|
||||
StartOfWeek = StartOfWeek.add(1, "day");
|
||||
}
|
||||
|
||||
let startOfMonth = moment().startOf("month");
|
||||
while (startOfMonth.isSameOrBefore(today)) {
|
||||
if (dateHash[startOfMonth.format("YYYY-MM-DD")]) {
|
||||
dateHash[startOfMonth.format("YYYY-MM-DD")].forEach((d) => {
|
||||
ret.toDateBody = ret.toDateBody + d.bodyhrs;
|
||||
ret.toDatePaint = ret.toDatePaint + d.painthrs;
|
||||
});
|
||||
}
|
||||
startOfMonth = startOfMonth.add(1, "day");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}, [scoreBoardlist]);
|
||||
console.log(
|
||||
"🚀 ~ file: scoreboard-targets-table.component.jsx ~ line 51 ~ values ~ values",
|
||||
values
|
||||
);
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t("scoreboard.labels.targets")}
|
||||
extra={<ScoreboardJobsList scoreBoardlist={scoreBoardlist} />}
|
||||
>
|
||||
<Row gutter={rowGutter}>
|
||||
<Col xs={24} sm={{ offset: 0, span: 4 }} lg={{ offset: 5, span: 4 }}>
|
||||
<Col xs={24} sm={{ offset: 0, span: 4 }} lg={{ span: 4 }}>
|
||||
<Statistic
|
||||
title={t("scoreboard.labels.workingdays")}
|
||||
value={Util.CalculateWorkingDaysThisMonth()}
|
||||
prefix={<CalendarOutlined />}
|
||||
/>
|
||||
</Col>
|
||||
<Col xs={24} sm={{ offset: 0, span: 20 }} lg={{ offset: 0, span: 13 }}>
|
||||
<Col xs={24} sm={{ offset: 0, span: 20 }} lg={{ offset: 0, span: 20 }}>
|
||||
<Row>
|
||||
<Col {...statSpans}>
|
||||
<Statistic
|
||||
@@ -43,6 +98,12 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
||||
prefix="B"
|
||||
/>
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic
|
||||
title={t("scoreboard.labels.dailyactual")}
|
||||
value={values.todayBody.toFixed(1)}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic
|
||||
title={t("scoreboard.labels.weeklytarget")}
|
||||
@@ -52,6 +113,12 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
||||
)}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic
|
||||
title={t("scoreboard.labels.weeklyactual")}
|
||||
value={values.weeklyBody.toFixed(1)}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic
|
||||
title={t("scoreboard.labels.monthlytarget")}
|
||||
@@ -70,6 +137,12 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
||||
)}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic
|
||||
title={t("scoreboard.labels.todateactual")}
|
||||
value={values.toDateBody.toFixed(1)}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col {...statSpans}>
|
||||
@@ -78,6 +151,9 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
||||
prefix="P"
|
||||
/>
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic value={values.todayPaint.toFixed(1)} />
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic
|
||||
value={Util.WeeklyTargetHrs(
|
||||
@@ -86,6 +162,9 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
||||
)}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic value={values.weeklyPaint.toFixed(1)} />
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic
|
||||
value={Util.MonthlyTargetHrs(
|
||||
@@ -102,6 +181,9 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
||||
)}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic value={values.toDatePaint.toFixed(1)} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@@ -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 }) {
|
||||
<Form.Item label={t("bodyshop.fields.email")} name="email">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.phone")}
|
||||
name="phone"
|
||||
@@ -97,6 +101,23 @@ export default function ShopInfoGeneral({ form }) {
|
||||
<Form.Item label={t("bodyshop.fields.website")} name="website">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.timezone")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
name="timezone"
|
||||
>
|
||||
<Select
|
||||
showSearch
|
||||
options={timeZonesList.map((z) => {
|
||||
return { label: z, value: z };
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.insurance_vendor_id")}
|
||||
name="insurance_vendor_id"
|
||||
|
||||
@@ -129,6 +129,15 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
{bodyshop.pbs_serialnumber && (
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.dms.disablecontactvehiclecreation")}
|
||||
valuePropName="checked"
|
||||
name={["pbs_configuration", "disablecontactvehicle"]}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
)}
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("bodyshop.labels.dms.cdk.payers")}>
|
||||
<Form.List name={["cdk_configuration", "payers"]}>
|
||||
|
||||
@@ -76,6 +76,21 @@ export function TimeTicketList({
|
||||
state.sortedInfo.columnKey === "employee" && state.sortedInfo.order,
|
||||
render: (text, record) =>
|
||||
`${record.employee.first_name} ${record.employee.last_name}`,
|
||||
filters:
|
||||
timetickets
|
||||
.map((l) => l.employeeid)
|
||||
.filter(onlyUnique)
|
||||
.map((s) => {
|
||||
return {
|
||||
text: (() => {
|
||||
const emp = bodyshop.employees.find((e) => e.id === s);
|
||||
|
||||
return `${emp.first_name} ${emp.last_name}`;
|
||||
})(), //
|
||||
value: [s],
|
||||
};
|
||||
}) || [],
|
||||
onFilter: (value, record) => value.includes(record.employeeid),
|
||||
},
|
||||
{
|
||||
title: t("timetickets.fields.cost_center"),
|
||||
|
||||
@@ -159,8 +159,10 @@ export function TimeTicketModalComponent({
|
||||
name="flat_rate"
|
||||
label={t("timetickets.fields.flat_rate")}
|
||||
valuePropName="checked"
|
||||
noStyle
|
||||
style={{ display: "none" }}
|
||||
>
|
||||
<Switch />
|
||||
<Switch style={{ display: "none" }} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
|
||||
@@ -212,6 +214,7 @@ export function TimeTicketModalComponent({
|
||||
<>
|
||||
<Form.Item label={t("timetickets.fields.clockon")} name="clockon">
|
||||
<FormDateTimePicker
|
||||
minuteStep={5}
|
||||
disabled={
|
||||
!HasRbacAccess({
|
||||
bodyshop,
|
||||
@@ -245,6 +248,7 @@ export function TimeTicketModalComponent({
|
||||
]}
|
||||
>
|
||||
<FormDateTimePicker
|
||||
minuteStep={5}
|
||||
disabled={
|
||||
!HasRbacAccess({
|
||||
bodyshop,
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Button } from "antd";
|
||||
import queryString from "query-string";
|
||||
import React, { useState } from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import moment from "moment";
|
||||
const PayrollTemplate = TemplateList("special").exported_payroll;
|
||||
export default function TimeTicketsPayrollTable() {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { start, end } = searchParams;
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleClick = async () => {
|
||||
setLoading(true);
|
||||
|
||||
await GenerateDocument(
|
||||
{
|
||||
name: PayrollTemplate.key,
|
||||
variables: {
|
||||
start: start
|
||||
? start
|
||||
: moment().startOf("week").subtract(7, "days").format("YYYY-MM-DD"),
|
||||
end: end ? end : moment().endOf("week").format("YYYY-MM-DD"),
|
||||
},
|
||||
},
|
||||
{},
|
||||
"x"
|
||||
);
|
||||
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Button loading={loading} onClick={handleClick}>
|
||||
{t("printcenter.payments.exported_payroll")}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
import { HeartOutlined } from "@ant-design/icons";
|
||||
import { Select, Tag } from "antd";
|
||||
import { Select, Space, Tag } from "antd";
|
||||
import React, { forwardRef, useEffect, useState } from "react";
|
||||
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||
const { Option } = Select;
|
||||
|
||||
//To be used as a form element only.
|
||||
|
||||
const VendorSearchSelect = (
|
||||
{ value, onChange, options, onSelect, disabled, preferredMake },
|
||||
{ value, onChange, options, onSelect, disabled, preferredMake, showPhone },
|
||||
ref
|
||||
) => {
|
||||
const [option, setOption] = useState(value);
|
||||
@@ -35,6 +36,7 @@ const VendorSearchSelect = (
|
||||
style={{
|
||||
width: "100%",
|
||||
}}
|
||||
dropdownMatchSelectWidth={false}
|
||||
onChange={setOption}
|
||||
optionFilterProp="name"
|
||||
onSelect={onSelect}
|
||||
@@ -50,10 +52,15 @@ const VendorSearchSelect = (
|
||||
>
|
||||
<div className="imex-flex-row">
|
||||
<div style={{ flex: 1 }}>{o.name}</div>
|
||||
<HeartOutlined />
|
||||
{o.discount && o.discount !== 0 ? (
|
||||
<Tag color="green">{`${o.discount * 100}%`}</Tag>
|
||||
) : null}
|
||||
<Space style={{ marginLeft: "1rem" }}>
|
||||
<HeartOutlined style={{ color: "red" }} />
|
||||
{o.phone && showPhone && (
|
||||
<PhoneNumberFormatter>{o.phone}</PhoneNumberFormatter>
|
||||
)}
|
||||
{o.discount && o.discount !== 0 ? (
|
||||
<Tag color="green">{`${o.discount * 100}%`}</Tag>
|
||||
) : null}
|
||||
</Space>
|
||||
</div>
|
||||
</Option>
|
||||
))
|
||||
@@ -64,9 +71,14 @@ const VendorSearchSelect = (
|
||||
<div className="imex-flex-row" style={{ width: "100%" }}>
|
||||
<div style={{ flex: 1 }}>{o.name}</div>
|
||||
|
||||
{o.discount && o.discount !== 0 ? (
|
||||
<Tag color="green">{`${o.discount * 100}%`}</Tag>
|
||||
) : null}
|
||||
<Space style={{ marginLeft: "1rem" }}>
|
||||
{o.phone && showPhone && (
|
||||
<PhoneNumberFormatter>{o.phone}</PhoneNumberFormatter>
|
||||
)}
|
||||
{o.discount && o.discount !== 0 ? (
|
||||
<Tag color="green">{`${o.discount * 100}%`}</Tag>
|
||||
) : null}
|
||||
</Space>
|
||||
</div>
|
||||
</Option>
|
||||
))
|
||||
|
||||
@@ -109,12 +109,14 @@ export default function VendorsFormComponent({
|
||||
|
||||
<Form.Item
|
||||
label={t("vendors.fields.email")}
|
||||
rules={[
|
||||
{
|
||||
type: "email",
|
||||
message: t("general.validation.invalidemail"),
|
||||
},
|
||||
]}
|
||||
rules={
|
||||
[
|
||||
// {
|
||||
// type: "email",
|
||||
// message: t("general.validation.invalidemail"),
|
||||
// },
|
||||
]
|
||||
}
|
||||
name="email"
|
||||
>
|
||||
<FormItemEmail email={getFieldValue("email")} />
|
||||
@@ -158,7 +160,7 @@ export default function VendorsFormComponent({
|
||||
<InputNumber min={0} max={1} precision={2} step={0.01} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("vendors.fields.due_date")} name="due_date">
|
||||
<InputNumber />
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>
|
||||
{
|
||||
// <Form.Item
|
||||
|
||||
@@ -84,6 +84,7 @@ export const QUERY_BILLS_BY_JOBID = gql`
|
||||
line_remarks
|
||||
quantity
|
||||
job_line_id
|
||||
part_type
|
||||
cost
|
||||
jobline {
|
||||
id
|
||||
@@ -123,6 +124,10 @@ export const QUERY_BILLS_BY_JOBID = gql`
|
||||
applicable_taxes
|
||||
deductedfromlbr
|
||||
lbr_adjustment
|
||||
jobline{
|
||||
oem_partno
|
||||
part_type
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,6 +164,10 @@ export const QUERY_BILL_BY_PK = gql`
|
||||
cost_center
|
||||
quantity
|
||||
joblineid
|
||||
jobline{
|
||||
oem_partno
|
||||
part_type
|
||||
}
|
||||
applicable_taxes
|
||||
deductedfromlbr
|
||||
lbr_adjustment
|
||||
|
||||
@@ -100,6 +100,7 @@ export const QUERY_BODYSHOP = gql`
|
||||
pbs_serialnumber
|
||||
md_filehandlers
|
||||
md_email_cc
|
||||
timezone
|
||||
employees {
|
||||
user_email
|
||||
id
|
||||
@@ -197,6 +198,7 @@ export const UPDATE_SHOP = gql`
|
||||
pbs_serialnumber
|
||||
md_filehandlers
|
||||
md_email_cc
|
||||
timezone
|
||||
employees {
|
||||
id
|
||||
first_name
|
||||
|
||||
@@ -89,6 +89,9 @@ export const QUERY_ALL_CC = gql`
|
||||
job {
|
||||
id
|
||||
ro_number
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_co_nm
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,8 @@ export const GET_LINE_TICKET_BY_PK = gql`
|
||||
employeeid
|
||||
memo
|
||||
flat_rate
|
||||
clockon
|
||||
clockoff
|
||||
employee {
|
||||
id
|
||||
first_name
|
||||
|
||||
@@ -12,11 +12,7 @@ export const QUERY_ALL_ACTIVE_JOBS = gql`
|
||||
ownr_ph1
|
||||
ownr_ph2
|
||||
ownr_ea
|
||||
owner {
|
||||
id
|
||||
allow_text_message
|
||||
preferred_contact
|
||||
}
|
||||
comment
|
||||
plate_no
|
||||
plate_st
|
||||
v_vin
|
||||
@@ -121,6 +117,7 @@ export const QUERY_EXACT_JOB_IN_PRODUCTION = gql`
|
||||
id
|
||||
status
|
||||
ro_number
|
||||
comment
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
category
|
||||
@@ -193,6 +190,7 @@ export const QUERY_EXACT_JOBS_IN_PRODUCTION = gql`
|
||||
id
|
||||
status
|
||||
ro_number
|
||||
comment
|
||||
ownr_fn
|
||||
category
|
||||
ownr_ln
|
||||
@@ -264,6 +262,7 @@ export const QUERY_JOBS_IN_PRODUCTION = gql`
|
||||
jobs(where: { inproduction: { _eq: true } }) {
|
||||
id
|
||||
updated_at
|
||||
comment
|
||||
status
|
||||
category
|
||||
ro_number
|
||||
@@ -489,6 +488,7 @@ export const GET_JOB_BY_PK = gql`
|
||||
alt_transport
|
||||
intakechecklist
|
||||
invoice_final_note
|
||||
comment
|
||||
loss_desc
|
||||
kmin
|
||||
kmout
|
||||
@@ -696,6 +696,7 @@ export const GET_JOB_BY_PK = gql`
|
||||
joblineid
|
||||
bill {
|
||||
id
|
||||
invoice_number
|
||||
vendor {
|
||||
id
|
||||
name
|
||||
@@ -830,6 +831,7 @@ export const QUERY_JOB_CARD_DETAILS = gql`
|
||||
ownr_co_nm
|
||||
ownr_ph1
|
||||
ownr_ph2
|
||||
comment
|
||||
ownr_ea
|
||||
ca_gst_registrant
|
||||
owner_owing
|
||||
@@ -1034,7 +1036,7 @@ export const UPDATE_JOB = gql`
|
||||
update_jobs(where: { id: { _eq: $jobId } }, _set: $job) {
|
||||
returning {
|
||||
id
|
||||
|
||||
comment
|
||||
date_exported
|
||||
status
|
||||
alt_transport
|
||||
@@ -1766,13 +1768,12 @@ export const QUERY_ALL_JOBS_PAGINATED_STATUS_FILTERED = gql`
|
||||
order_by: $order
|
||||
where: { status: { _in: $statusList } }
|
||||
) {
|
||||
comment
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_co_nm
|
||||
ownerid
|
||||
ownr_ph1
|
||||
ownr_ph2
|
||||
ownr_ea
|
||||
plate_no
|
||||
plate_st
|
||||
v_vin
|
||||
@@ -1781,32 +1782,16 @@ export const QUERY_ALL_JOBS_PAGINATED_STATUS_FILTERED = gql`
|
||||
v_make_desc
|
||||
v_color
|
||||
vehicleid
|
||||
actual_completion
|
||||
actual_delivery
|
||||
actual_in
|
||||
id
|
||||
ins_co_nm
|
||||
ins_ct_fn
|
||||
ins_ct_ln
|
||||
ins_ph1
|
||||
ins_ea
|
||||
est_co_nm
|
||||
est_ph1
|
||||
est_ea
|
||||
est_ct_fn
|
||||
est_ct_ln
|
||||
clm_no
|
||||
clm_total
|
||||
owner_owing
|
||||
ro_number
|
||||
po_number
|
||||
scheduled_completion
|
||||
scheduled_in
|
||||
scheduled_delivery
|
||||
status
|
||||
updated_at
|
||||
ded_amt
|
||||
vehicleid
|
||||
}
|
||||
search_jobs_aggregate(
|
||||
args: { search: $search }
|
||||
|
||||
@@ -69,6 +69,7 @@ export const QUERY_PARTS_ORDER_OEC = gql`
|
||||
db_price
|
||||
line_desc
|
||||
quantity
|
||||
part_type
|
||||
}
|
||||
job {
|
||||
bodyshop{
|
||||
|
||||
@@ -87,6 +87,7 @@ export const QUERY_ALL_VENDORS_FOR_ORDER = gql`
|
||||
discount
|
||||
email
|
||||
active
|
||||
phone
|
||||
}
|
||||
jobs(where: { id: { _eq: $jobId } }) {
|
||||
v_make_desc
|
||||
|
||||
@@ -70,7 +70,12 @@ export function CourtesyCarCreateContainer({
|
||||
|
||||
return (
|
||||
<RbacWrapper action="courtesycar:create">
|
||||
<Form form={form} autoComplete="new-password" onFinish={handleFinish}>
|
||||
<Form
|
||||
form={form}
|
||||
autoComplete="new-password"
|
||||
onFinish={handleFinish}
|
||||
layout="vertical"
|
||||
>
|
||||
<CourtesyCarFormComponent form={form} saveLoading={loading} />
|
||||
</Form>
|
||||
</RbacWrapper>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Col, Row } from "antd";
|
||||
import { Col, Row, Space } from "antd";
|
||||
import moment from "moment";
|
||||
import queryString from "query-string";
|
||||
import React, { useEffect } from "react";
|
||||
@@ -11,6 +11,7 @@ import AlertComponent from "../../components/alert/alert.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import TimeTicketsDatesSelector from "../../components/ticket-tickets-dates-selector/time-tickets-dates-selector.component";
|
||||
import TimeTicketList from "../../components/time-ticket-list/time-ticket-list.component";
|
||||
import TimeTicketsPayrollTable from "../../components/time-tickets-payroll-table/time-tickets-payroll-table.component";
|
||||
import TimeTicketsSummaryEmployees from "../../components/time-tickets-summary-employees/time-tickets-summary-employees.component";
|
||||
import { QUERY_TIME_TICKETS_IN_RANGE } from "../../graphql/timetickets.queries";
|
||||
import {
|
||||
@@ -68,7 +69,12 @@ export function TimeTicketsContainer({
|
||||
<TimeTicketList
|
||||
loading={loading}
|
||||
timetickets={data ? data.timetickets : []}
|
||||
extra={<TimeTicketsDatesSelector />}
|
||||
extra={
|
||||
<Space wrap>
|
||||
<TimeTicketsPayrollTable />
|
||||
<TimeTicketsDatesSelector />
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
|
||||
@@ -41,6 +41,7 @@ import UserActionTypes from "./user.types";
|
||||
import axios from "axios";
|
||||
import { messaging } from "../../firebase/firebase.utils";
|
||||
import { getToken } from "firebase/messaging";
|
||||
|
||||
export function* onEmailSignInStart() {
|
||||
yield takeLatest(UserActionTypes.EMAIL_SIGN_IN_START, signInWithEmail);
|
||||
}
|
||||
@@ -266,6 +267,12 @@ export function* onSetShopDetails() {
|
||||
export function* SetAuthLevelFromShopDetails({ payload }) {
|
||||
try {
|
||||
const userEmail = yield select((state) => state.user.currentUser.email);
|
||||
try {
|
||||
console.log("Setting shop timezone.");
|
||||
// moment.tz.setDefault(payload.timezone);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
factory.client(payload.imexshopid);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"actions": {
|
||||
"block": "Block Day",
|
||||
"calculate": "Calculate SMART Dates",
|
||||
"cancel": "Cancel",
|
||||
"cancel": "Cancel Appointment",
|
||||
"intake": "Intake",
|
||||
"new": "New Appointment",
|
||||
"preview": "Preview",
|
||||
@@ -183,6 +183,7 @@
|
||||
"federal_tax": "Federal Tax",
|
||||
"iouexists": "An IOU exists that is associated to this RO.",
|
||||
"local_tax": "Local Tax",
|
||||
"markexported": "Mark Exported",
|
||||
"markforreexport": "Mark for Re-export",
|
||||
"new": "New Bill",
|
||||
"noneselected": "No bill selected.",
|
||||
@@ -197,6 +198,7 @@
|
||||
"created": "Invoice added successfully.",
|
||||
"deleted": "Bill deleted successfully.",
|
||||
"exported": "Bill(s) exported successfully.",
|
||||
"markexported": "Bill marked as exported.",
|
||||
"reexport": "Bill marked for re-export."
|
||||
},
|
||||
"validation": {
|
||||
@@ -244,6 +246,7 @@
|
||||
"dms": {
|
||||
"cashierid": "Cashier ID",
|
||||
"default_journal": "Default Journal",
|
||||
"disablecontactvehiclecreation": "Disable Contact & Vehicle Updates/Creation",
|
||||
"dms_acctnumber": "DMS Account #",
|
||||
"dms_wip_acctnumber": "DMS W.I.P. Account #",
|
||||
"generic_customer_number": "Generic Customer Number",
|
||||
@@ -483,6 +486,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",
|
||||
@@ -1242,6 +1246,7 @@
|
||||
"class": "Class",
|
||||
"clm_no": "Claim #",
|
||||
"clm_total": "Claim Total",
|
||||
"comment": "Comment",
|
||||
"customerowing": "Customer Owing",
|
||||
"date_estimated": "Date Estimated",
|
||||
"date_exported": "Exported",
|
||||
@@ -1482,8 +1487,10 @@
|
||||
"closejob": "Close Job {{ro_number}}",
|
||||
"contracts": "CC Contracts",
|
||||
"cost": "Cost",
|
||||
"cost_Additional": "Cost - Additional",
|
||||
"cost_labor": "Cost - Labor",
|
||||
"cost_parts": "Cost - Parts",
|
||||
"cost_sublet": "Cost - Sublet",
|
||||
"costs": "Costs",
|
||||
"create": {
|
||||
"jobinfo": "Job Info",
|
||||
@@ -1553,7 +1560,7 @@
|
||||
"partstotal": "Parts Total (ex. Taxes)",
|
||||
"plitooltips": {
|
||||
"billtotal": "The total amount of all bill lines that have been posted against this RO (not including credits, taxes, or labor adjustments).",
|
||||
"creditmemos": "The total amount of all credit memos entered. This amount does not reflect any parts returns created.",
|
||||
"creditmemos": "The total amount of all returns created. This amount does not reflect credit memos that have been posted.",
|
||||
"creditsnotreceived": "The total amount of returns created for this job that do not have a corresponding credit memo posted. An amount greater than $0 indicates that vendors have not provided requested credit memos.",
|
||||
"discrep1": "If the discrepancy is not $0, you may have one of the following: <br/><br/>\n\n<ul>\n<li>Too many bills/bill lines that have been posted against this RO. Check to make sure every bill posted on this RO is correctly posted and assigned.</li>\n<li>You do not have the latest supplement imported, or, a supplement must be submitted and then imported.</li>\n<li>You have posted a bill line to labor.</li>\n</ul>\n<br/>\n<i>There may be additional issues not listed above that prevent this job from reconciling.</i>",
|
||||
"discrep2": "If the discrepancy is not $0, you may have one of the following: <br/><br/>\n\n<ul>\n<li>Used an incorrect rate when deducting from labor.</li>\n<li>An outstanding imbalance higher in the reconciliation process.</li>\n</ul>\n<br/>\n<i>There may be additional issues not listed above that prevent this job from reconciling.</i>",
|
||||
@@ -1581,8 +1588,10 @@
|
||||
"relatedros": "Related ROs",
|
||||
"returntotals": "Return Totals",
|
||||
"rosaletotal": "RO Parts Total",
|
||||
"sale_additional": "Sales - Additional",
|
||||
"sale_labor": "Sales - Labor",
|
||||
"sale_parts": "Sales - Parts & Sublet",
|
||||
"sale_parts": "Sales - Parts",
|
||||
"sale_sublet": "Sales - Sublet",
|
||||
"sales": "Sales",
|
||||
"savebeforeconversion": "You have unsaved changes on the job. Please save them before converting it. ",
|
||||
"scheduledinchange": "The scheduled in is based off the latest appointment. To change this date, please schedule or reschedule the job. ",
|
||||
@@ -1900,6 +1909,7 @@
|
||||
"order_date": "Order Date",
|
||||
"order_number": "Order Number",
|
||||
"orderedby": "Ordered By",
|
||||
"part_type": "Type",
|
||||
"quantity": "Qty.",
|
||||
"return": "Return",
|
||||
"status": "Status"
|
||||
@@ -2109,7 +2119,8 @@
|
||||
"title": "Print Center"
|
||||
},
|
||||
"payments": {
|
||||
"ca_bc_etf_table": "ICBC ETF Table"
|
||||
"ca_bc_etf_table": "ICBC ETF Table",
|
||||
"exported_payroll": "Payroll Table"
|
||||
},
|
||||
"subjects": {
|
||||
"jobs": {
|
||||
@@ -2150,6 +2161,7 @@
|
||||
"bodypriority": "B/P",
|
||||
"cardsettings": "Card Settings",
|
||||
"clm_no": "Claim Number",
|
||||
"comment": "Comment",
|
||||
"compact": "Compact Cards",
|
||||
"detailpriority": "D/P",
|
||||
"employeeassignments": "Employee Assignments",
|
||||
@@ -2230,7 +2242,7 @@
|
||||
"gsr_by_csr": "Gross Sales by CSR",
|
||||
"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_exported_date": "Exported Gross Sales",
|
||||
"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",
|
||||
@@ -2315,9 +2327,12 @@
|
||||
},
|
||||
"labels": {
|
||||
"asoftodaytarget": "As of Today",
|
||||
"dailyactual": "Actual (D)",
|
||||
"dailytarget": "Daily",
|
||||
"monthlytarget": "Monthly",
|
||||
"targets": "Targets",
|
||||
"todateactual": "Actual (MTD)",
|
||||
"weeklyactual": "Actual (W)",
|
||||
"weeklytarget": "Weekly",
|
||||
"workingdays": "Working Days / Month"
|
||||
},
|
||||
@@ -2588,7 +2603,7 @@
|
||||
"country": "Country",
|
||||
"discount": "Discount % (as decimal)",
|
||||
"display_name": "Display Name",
|
||||
"due_date": "Payment Due Date",
|
||||
"due_date": "Payment Due Date (# of days)",
|
||||
"email": "Contact Email",
|
||||
"favorite": "Favorite?",
|
||||
"lkq": "LKQ",
|
||||
|
||||
@@ -183,6 +183,7 @@
|
||||
"federal_tax": "",
|
||||
"iouexists": "",
|
||||
"local_tax": "",
|
||||
"markexported": "",
|
||||
"markforreexport": "",
|
||||
"new": "",
|
||||
"noneselected": "",
|
||||
@@ -197,6 +198,7 @@
|
||||
"created": "",
|
||||
"deleted": "",
|
||||
"exported": "",
|
||||
"markexported": "",
|
||||
"reexport": ""
|
||||
},
|
||||
"validation": {
|
||||
@@ -244,6 +246,7 @@
|
||||
"dms": {
|
||||
"cashierid": "",
|
||||
"default_journal": "",
|
||||
"disablecontactvehiclecreation": "",
|
||||
"dms_acctnumber": "",
|
||||
"dms_wip_acctnumber": "",
|
||||
"generic_customer_number": "",
|
||||
@@ -483,6 +486,7 @@
|
||||
"production_statuses": ""
|
||||
},
|
||||
"target_touchtime": "",
|
||||
"timezone": "",
|
||||
"tt_allow_post_to_invoiced": "",
|
||||
"use_fippa": "",
|
||||
"website": "",
|
||||
@@ -1242,6 +1246,7 @@
|
||||
"class": "",
|
||||
"clm_no": "Reclamación #",
|
||||
"clm_total": "Reclamar total",
|
||||
"comment": "",
|
||||
"customerowing": "Cliente debido",
|
||||
"date_estimated": "Fecha estimada",
|
||||
"date_exported": "Exportado",
|
||||
@@ -1482,8 +1487,10 @@
|
||||
"closejob": "",
|
||||
"contracts": "",
|
||||
"cost": "",
|
||||
"cost_Additional": "",
|
||||
"cost_labor": "",
|
||||
"cost_parts": "",
|
||||
"cost_sublet": "",
|
||||
"costs": "",
|
||||
"create": {
|
||||
"jobinfo": "",
|
||||
@@ -1581,8 +1588,10 @@
|
||||
"relatedros": "",
|
||||
"returntotals": "",
|
||||
"rosaletotal": "",
|
||||
"sale_additional": "",
|
||||
"sale_labor": "",
|
||||
"sale_parts": "",
|
||||
"sale_sublet": "",
|
||||
"sales": "",
|
||||
"savebeforeconversion": "",
|
||||
"scheduledinchange": "",
|
||||
@@ -1900,6 +1909,7 @@
|
||||
"order_date": "",
|
||||
"order_number": "",
|
||||
"orderedby": "",
|
||||
"part_type": "",
|
||||
"quantity": "",
|
||||
"return": "",
|
||||
"status": ""
|
||||
@@ -2109,7 +2119,8 @@
|
||||
"title": ""
|
||||
},
|
||||
"payments": {
|
||||
"ca_bc_etf_table": ""
|
||||
"ca_bc_etf_table": "",
|
||||
"exported_payroll": ""
|
||||
},
|
||||
"subjects": {
|
||||
"jobs": {
|
||||
@@ -2150,6 +2161,7 @@
|
||||
"bodypriority": "",
|
||||
"cardsettings": "",
|
||||
"clm_no": "",
|
||||
"comment": "",
|
||||
"compact": "",
|
||||
"detailpriority": "",
|
||||
"employeeassignments": "",
|
||||
@@ -2315,9 +2327,12 @@
|
||||
},
|
||||
"labels": {
|
||||
"asoftodaytarget": "",
|
||||
"dailyactual": "",
|
||||
"dailytarget": "",
|
||||
"monthlytarget": "",
|
||||
"targets": "",
|
||||
"todateactual": "",
|
||||
"weeklyactual": "",
|
||||
"weeklytarget": "",
|
||||
"workingdays": ""
|
||||
},
|
||||
|
||||
@@ -183,6 +183,7 @@
|
||||
"federal_tax": "",
|
||||
"iouexists": "",
|
||||
"local_tax": "",
|
||||
"markexported": "",
|
||||
"markforreexport": "",
|
||||
"new": "",
|
||||
"noneselected": "",
|
||||
@@ -197,6 +198,7 @@
|
||||
"created": "",
|
||||
"deleted": "",
|
||||
"exported": "",
|
||||
"markexported": "",
|
||||
"reexport": ""
|
||||
},
|
||||
"validation": {
|
||||
@@ -244,6 +246,7 @@
|
||||
"dms": {
|
||||
"cashierid": "",
|
||||
"default_journal": "",
|
||||
"disablecontactvehiclecreation": "",
|
||||
"dms_acctnumber": "",
|
||||
"dms_wip_acctnumber": "",
|
||||
"generic_customer_number": "",
|
||||
@@ -483,6 +486,7 @@
|
||||
"production_statuses": ""
|
||||
},
|
||||
"target_touchtime": "",
|
||||
"timezone": "",
|
||||
"tt_allow_post_to_invoiced": "",
|
||||
"use_fippa": "",
|
||||
"website": "",
|
||||
@@ -1242,6 +1246,7 @@
|
||||
"class": "",
|
||||
"clm_no": "Prétendre #",
|
||||
"clm_total": "Total réclamation",
|
||||
"comment": "",
|
||||
"customerowing": "Client propriétaire",
|
||||
"date_estimated": "Date estimée",
|
||||
"date_exported": "Exportés",
|
||||
@@ -1482,8 +1487,10 @@
|
||||
"closejob": "",
|
||||
"contracts": "",
|
||||
"cost": "",
|
||||
"cost_Additional": "",
|
||||
"cost_labor": "",
|
||||
"cost_parts": "",
|
||||
"cost_sublet": "",
|
||||
"costs": "",
|
||||
"create": {
|
||||
"jobinfo": "",
|
||||
@@ -1581,8 +1588,10 @@
|
||||
"relatedros": "",
|
||||
"returntotals": "",
|
||||
"rosaletotal": "",
|
||||
"sale_additional": "",
|
||||
"sale_labor": "",
|
||||
"sale_parts": "",
|
||||
"sale_sublet": "",
|
||||
"sales": "",
|
||||
"savebeforeconversion": "",
|
||||
"scheduledinchange": "",
|
||||
@@ -1900,6 +1909,7 @@
|
||||
"order_date": "",
|
||||
"order_number": "",
|
||||
"orderedby": "",
|
||||
"part_type": "",
|
||||
"quantity": "",
|
||||
"return": "",
|
||||
"status": ""
|
||||
@@ -2109,7 +2119,8 @@
|
||||
"title": ""
|
||||
},
|
||||
"payments": {
|
||||
"ca_bc_etf_table": ""
|
||||
"ca_bc_etf_table": "",
|
||||
"exported_payroll": ""
|
||||
},
|
||||
"subjects": {
|
||||
"jobs": {
|
||||
@@ -2150,6 +2161,7 @@
|
||||
"bodypriority": "",
|
||||
"cardsettings": "",
|
||||
"clm_no": "",
|
||||
"comment": "",
|
||||
"compact": "",
|
||||
"detailpriority": "",
|
||||
"employeeassignments": "",
|
||||
@@ -2315,9 +2327,12 @@
|
||||
},
|
||||
"labels": {
|
||||
"asoftodaytarget": "",
|
||||
"dailyactual": "",
|
||||
"dailytarget": "",
|
||||
"monthlytarget": "",
|
||||
"targets": "",
|
||||
"todateactual": "",
|
||||
"weeklyactual": "",
|
||||
"weeklytarget": "",
|
||||
"workingdays": ""
|
||||
},
|
||||
|
||||
@@ -17,7 +17,8 @@ const Templates = TemplateList();
|
||||
export default async function RenderTemplate(
|
||||
templateObject,
|
||||
bodyshop,
|
||||
renderAsHtml = false
|
||||
renderAsHtml = false,
|
||||
renderAsExcel = false
|
||||
) {
|
||||
//Query assets that match the template name. Must be in format <<templateName>>.query
|
||||
let { contextData, useShopSpecificTemplate } = await fetchContextData(
|
||||
@@ -30,6 +31,7 @@ export default async function RenderTemplate(
|
||||
? `/${bodyshop.imexshopid}/${templateObject.name}`
|
||||
: `/${templateObject.name}`,
|
||||
...(renderAsHtml ? {} : { recipe: "chrome-pdf" }),
|
||||
...(renderAsExcel ? { recipe: "html-to-xlsx" } : {}),
|
||||
},
|
||||
data: {
|
||||
...contextData,
|
||||
@@ -37,7 +39,7 @@ export default async function RenderTemplate(
|
||||
...templateObject.context,
|
||||
headerpath: `/${bodyshop.imexshopid}/header.html`,
|
||||
bodyshop: bodyshop,
|
||||
offset: moment().utcOffset(),
|
||||
offset: bodyshop.timezone, //moment().utcOffset(),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -182,6 +184,9 @@ export const GenerateDocument = async (
|
||||
template,
|
||||
})
|
||||
);
|
||||
} else if (sendType === "x") {
|
||||
console.log("excel");
|
||||
await RenderTemplate(template, bodyshop, false, true);
|
||||
} else {
|
||||
await RenderTemplate(template, bodyshop);
|
||||
}
|
||||
|
||||
@@ -1635,6 +1635,13 @@ export const TemplateList = (type, context) => {
|
||||
key: "ca_bc_etf_table",
|
||||
disabled: false,
|
||||
},
|
||||
exported_payroll: {
|
||||
title: i18n.t("printcenter.payments.exported_payroll"),
|
||||
description: "Est Detail",
|
||||
subject: i18n.t("printcenter.payments.exported_payroll"),
|
||||
key: "exported_payroll",
|
||||
disabled: false,
|
||||
},
|
||||
production_by_technician_one: {
|
||||
title: i18n.t(
|
||||
"reportcenter.templates.production_by_technician_one"
|
||||
|
||||
@@ -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==
|
||||
|
||||
@@ -815,6 +815,7 @@
|
||||
- email
|
||||
- enforce_class
|
||||
- enforce_referral
|
||||
- entegral_configuration
|
||||
- entegral_id
|
||||
- features
|
||||
- federal_tax_id
|
||||
@@ -867,6 +868,7 @@
|
||||
- target_touchtime
|
||||
- template_header
|
||||
- textid
|
||||
- timezone
|
||||
- tt_allow_post_to_invoiced
|
||||
- updated_at
|
||||
- use_fippa
|
||||
@@ -926,6 +928,7 @@
|
||||
- md_referral_sources
|
||||
- md_responsibility_centers
|
||||
- md_ro_statuses
|
||||
- pbs_configuration
|
||||
- phone
|
||||
- prodtargethrs
|
||||
- production_config
|
||||
@@ -939,6 +942,7 @@
|
||||
- state
|
||||
- state_tax_id
|
||||
- target_touchtime
|
||||
- timezone
|
||||
- tt_allow_post_to_invoiced
|
||||
- updated_at
|
||||
- use_fippa
|
||||
@@ -2707,6 +2711,7 @@
|
||||
- clm_title
|
||||
- clm_total
|
||||
- clm_zip
|
||||
- comment
|
||||
- converted
|
||||
- created_at
|
||||
- cust_pr
|
||||
@@ -2963,6 +2968,7 @@
|
||||
- clm_title
|
||||
- clm_total
|
||||
- clm_zip
|
||||
- comment
|
||||
- converted
|
||||
- created_at
|
||||
- cust_pr
|
||||
@@ -3229,6 +3235,7 @@
|
||||
- clm_title
|
||||
- clm_total
|
||||
- clm_zip
|
||||
- comment
|
||||
- converted
|
||||
- created_at
|
||||
- cust_pr
|
||||
|
||||
@@ -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;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."bodyshops" add column "timezone" text
|
||||
null;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."jobs" add column "comment" text
|
||||
-- null;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."jobs" add column "comment" text
|
||||
null;
|
||||
@@ -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 "entegral_configuration" jsonb
|
||||
-- null default jsonb_build_array();
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."bodyshops" add column "entegral_configuration" jsonb
|
||||
null default jsonb_build_array();
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."bodyshops" alter column "entegral_configuration" set default jsonb_build_array();
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."bodyshops" alter column "entegral_configuration" set default jsonb_build_object();
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "public"."bodyshops" ALTER COLUMN "timezone" drop default;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."bodyshops" alter column "timezone" set default 'America/Vancouver';
|
||||
5325
package-lock.json
generated
Normal file
5325
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -35,8 +35,10 @@
|
||||
"graylog2": "^0.2.1",
|
||||
"inline-css": "^3.0.0",
|
||||
"intuit-oauth": "^4.0.0",
|
||||
"json-2-csv": "^3.17.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",
|
||||
|
||||
@@ -149,6 +149,7 @@ app.post(
|
||||
fb.unsubscribe
|
||||
);
|
||||
app.post("/adm/updateuser", fb.validateFirebaseIdToken, fb.updateUser);
|
||||
app.post("/adm/createuser", fb.validateFirebaseIdToken, fb.createUser);
|
||||
|
||||
//Stripe Processing
|
||||
var stripe = require("./server/stripe/payment");
|
||||
@@ -177,8 +178,15 @@ var data = require("./server/data/data");
|
||||
app.post("/data/ah", data.autohouse);
|
||||
app.post("/data/arms", data.arms);
|
||||
|
||||
var taskHandler = require("./server/tasks/tasks");
|
||||
app.post("/taskHandler", taskHandler.taskHandler);
|
||||
|
||||
var ioevent = require("./server/ioevent/ioevent");
|
||||
app.post("/ioevent", ioevent.default);
|
||||
app.post("/newlog", (req, res) => {
|
||||
const { message, type, user, record, object } = req.body;
|
||||
logger.log(message, type, user, record, object);
|
||||
});
|
||||
|
||||
var cdkGetMake = require("./server/cdk/cdk-get-makes");
|
||||
app.post("/cdk/getvehicles", fb.validateFirebaseIdToken, cdkGetMake.default);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -102,40 +103,54 @@ exports.PbsSelectedCustomer = async function PbsSelectedCustomer(
|
||||
selectedCustomerId
|
||||
) {
|
||||
try {
|
||||
CdkBase.createLogEvent(
|
||||
socket,
|
||||
"DEBUG",
|
||||
`User selected customer ${selectedCustomerId || "NEW"}`
|
||||
);
|
||||
if (
|
||||
socket.JobData.bodyshop.pbs_configuration.disablecontactvehicle === false
|
||||
) {
|
||||
CdkBase.createLogEvent(
|
||||
socket,
|
||||
"DEBUG",
|
||||
`User selected customer ${selectedCustomerId || "NEW"}`
|
||||
);
|
||||
|
||||
//Upsert the contact information as per Wafaa's Email.
|
||||
CdkBase.createLogEvent(
|
||||
socket,
|
||||
"DEBUG",
|
||||
`Upserting contact information to DMS for ${
|
||||
socket.JobData.ownr_fn || ""
|
||||
} ${socket.JobData.ownr_ln || ""} ${socket.JobData.ownr_co_nm || ""}`
|
||||
);
|
||||
const ownerRef = await UpsertContactData(socket, selectedCustomerId);
|
||||
//Upsert the contact information as per Wafaa's Email.
|
||||
CdkBase.createLogEvent(
|
||||
socket,
|
||||
"DEBUG",
|
||||
`Upserting contact information to DMS for ${
|
||||
socket.JobData.ownr_fn || ""
|
||||
} ${socket.JobData.ownr_ln || ""} ${socket.JobData.ownr_co_nm || ""}`
|
||||
);
|
||||
const ownerRef = await UpsertContactData(socket, selectedCustomerId);
|
||||
|
||||
CdkBase.createLogEvent(
|
||||
socket,
|
||||
"DEBUG",
|
||||
`Upserting vehicle information to DMS for ${socket.JobData.v_vin}`
|
||||
);
|
||||
await UpsertVehicleData(socket, ownerRef.ReferenceId);
|
||||
CdkBase.createLogEvent(
|
||||
socket,
|
||||
"DEBUG",
|
||||
`Upserting vehicle information to DMS for ${socket.JobData.v_vin}`
|
||||
);
|
||||
await UpsertVehicleData(socket, ownerRef.ReferenceId);
|
||||
} else {
|
||||
CdkBase.createLogEvent(
|
||||
socket,
|
||||
"DEBUG",
|
||||
`Contact and Vehicle updates disabled. Skipping to accounting data insert.`
|
||||
);
|
||||
}
|
||||
CdkBase.createLogEvent(socket, "DEBUG", `Inserting account data.`);
|
||||
CdkBase.createLogEvent(
|
||||
socket,
|
||||
"DEBUG",
|
||||
`Inserting accounting posting data..`
|
||||
);
|
||||
await InsertAccountPostingData(socket);
|
||||
const insertResponse = await InsertAccountPostingData(socket);
|
||||
|
||||
CdkBase.createLogEvent(socket, "DEBUG", `Marking job as exported.`);
|
||||
await MarkJobExported(socket, socket.JobData.id);
|
||||
if (insertResponse.WasSuccessful) {
|
||||
CdkBase.createLogEvent(socket, "DEBUG", `Marking job as exported.`);
|
||||
await MarkJobExported(socket, socket.JobData.id);
|
||||
|
||||
socket.emit("export-success", socket.JobData.id);
|
||||
socket.emit("export-success", socket.JobData.id);
|
||||
} else {
|
||||
CdkBase.createLogEvent(socket, "ERROR", `Export was not succesful.`);
|
||||
}
|
||||
} catch (error) {
|
||||
CdkBase.createLogEvent(
|
||||
socket,
|
||||
@@ -553,7 +568,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 +584,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 +597,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 +614,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 +632,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 +647,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",
|
||||
|
||||
@@ -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,8 +178,16 @@ async function InsertBill(oauthClient, qbo_realmId, req, bill, vendor) {
|
||||
VendorRef: {
|
||||
value: vendor.Id,
|
||||
},
|
||||
TxnDate: moment(bill.date).format("YYYY-MM-DD"),
|
||||
//DueDate: bill.due_date && moment(bill.due_date).format("YYYY-MM-DD"),
|
||||
TxnDate: moment(bill.date)
|
||||
//.tz(bill.job.bodyshop.timezone)
|
||||
.format("YYYY-MM-DD"),
|
||||
...(!bill.is_credit_memo &&
|
||||
bill.vendor.due_date && {
|
||||
DueDate: moment(bill.date)
|
||||
//.tz(bill.job.bodyshop.timezone)
|
||||
.add(bill.vendor.due_date, "days")
|
||||
.format("YYYY-MM-DD"),
|
||||
}),
|
||||
DocNumber: bill.invoice_number,
|
||||
//...(bill.job.class ? { ClassRef: { Id: classes[bill.job.class] } } : {}),
|
||||
|
||||
|
||||
@@ -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,8 @@ 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 +372,8 @@ async function InsertCreditMemo(
|
||||
qbo_realmId,
|
||||
req,
|
||||
payment,
|
||||
parentRef
|
||||
parentRef,
|
||||
bodyshop
|
||||
) {
|
||||
const { paymentMethods, invoices, items, taxCodes } = await QueryMetaData(
|
||||
oauthClient,
|
||||
@@ -382,7 +393,9 @@ 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 } }
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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,16 @@ const generateBill = (bill) => {
|
||||
VendorRef: {
|
||||
FullName: bill.vendor.name,
|
||||
},
|
||||
TxnDate: moment(bill.date).format("YYYY-MM-DD"),
|
||||
DueDate:
|
||||
bill.due_date && moment(bill.due_date).format("YYYY-MM-DD"),
|
||||
TxnDate: moment(bill.date)
|
||||
//.tz(bill.job.bodyshop.timezone)
|
||||
.format("YYYY-MM-DD"),
|
||||
...(!bill.is_credit_memo &&
|
||||
bill.vendor.due_date && {
|
||||
DueDate: moment(bill.date)
|
||||
// .tz(bill.job.bodyshop.timezone)
|
||||
.add(bill.vendor.due_date, "days")
|
||||
.format("YYYY-MM-DD"),
|
||||
}),
|
||||
RefNumber: bill.invoice_number,
|
||||
Memo: `RO ${bill.job.ro_number || ""}`,
|
||||
ExpenseLineAdd: bill.billlines.map((il) =>
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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");
|
||||
@@ -93,6 +93,7 @@ exports.default = async (req, res) => {
|
||||
.end({ allowEmptyTags: true });
|
||||
|
||||
allxmlsToUpload.push({
|
||||
count: autoHouseObject.AutoHouseExport.RepairOrder.length,
|
||||
xml: ret,
|
||||
filename: `IM_${bodyshop.autohouseid}_${moment().format(
|
||||
"DDMMYYYY_HHMMSS"
|
||||
@@ -213,7 +214,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
Street: job.ownr_addr1 || "",
|
||||
City: job.ownr_city || "",
|
||||
State: job.ownr_st || "",
|
||||
Zip: job.ownr_zip || "",
|
||||
Zip: (job.ownr_zip && job.ownr_zip.substring(0, 3)) || "",
|
||||
Phone1: job.ownr_ph1 || "",
|
||||
Phone2: null,
|
||||
Phone2Extension: null,
|
||||
@@ -227,7 +228,11 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
InsuredorClaimantFlag: null,
|
||||
},
|
||||
VehicleInformation: {
|
||||
Year: job.v_model_yr || "",
|
||||
Year: job.v_model_yr
|
||||
? parseInt(job.v_model_yr.match(/\d/g))
|
||||
? parseInt(job.v_model_yr.match(/\d/g).join(""), 10)
|
||||
: ""
|
||||
: "",
|
||||
Make: job.v_make_desc || "",
|
||||
Model: job.v_model_desc || "",
|
||||
VIN: job.v_vin || "",
|
||||
@@ -279,15 +284,30 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
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 +323,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,
|
||||
@@ -457,8 +488,8 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
PartsReconditionedCost:
|
||||
repairCosts.PartsReconditionedCost.toFormat(AHDineroFormat),
|
||||
PartsRecycled: Dinero(
|
||||
job.job_totals.parts.parts.list.PAR &&
|
||||
job.job_totals.parts.parts.list.PAR.total
|
||||
job.job_totals.parts.parts.list.PAL &&
|
||||
job.job_totals.parts.parts.list.PAL.total
|
||||
).toFormat(AHDineroFormat),
|
||||
PartsRecycledCost:
|
||||
repairCosts.PartsRecycledCost.toFormat(AHDineroFormat),
|
||||
@@ -524,7 +555,9 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
AHDineroFormat
|
||||
),
|
||||
BMTotalCost: repairCosts.BMTotalCost.toFormat(AHDineroFormat),
|
||||
MiscTotal: 0,
|
||||
MiscTotal: Dinero(job.job_totals.additional.additionalCosts).toFormat(
|
||||
AHDineroFormat
|
||||
),
|
||||
MiscTotalCost: 0,
|
||||
TowingTotal: Dinero(job.job_totals.additional.towing).toFormat(
|
||||
AHDineroFormat
|
||||
@@ -541,10 +574,12 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
.add(Dinero(job.job_totals.totals.federal_tax))
|
||||
.toFormat(AHDineroFormat),
|
||||
SalesTaxTotalCost: 0,
|
||||
GrossTotal: Dinero(job.job_totals.totals.net_repairs).toFormat(
|
||||
GrossTotal: Dinero(job.job_totals.totals.total_repairs).toFormat(
|
||||
AHDineroFormat
|
||||
),
|
||||
DeductibleTotal: job.ded_amt || 0,
|
||||
DeductibleTotal: Dinero({
|
||||
amount: Math.round((job.ded_amt || 0) * 100),
|
||||
}).toFormat(AHDineroFormat),
|
||||
DepreciationTotal: Dinero(
|
||||
job.job_totals.totals.custPayable.dep_taxes
|
||||
).toFormat(AHDineroFormat),
|
||||
@@ -554,7 +589,9 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
CustomerPay: Dinero(job.job_totals.totals.custPayable.total).toFormat(
|
||||
AHDineroFormat
|
||||
),
|
||||
InsurancePay: 0,
|
||||
InsurancePay: Dinero(job.job_totals.totals.total_repairs)
|
||||
.subtract(Dinero(job.job_totals.totals.custPayable))
|
||||
.toFormat(AHDineroFormat),
|
||||
Deposit: 0,
|
||||
AmountDue: 0,
|
||||
},
|
||||
@@ -600,7 +637,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
DetailLine:
|
||||
job.joblines.length > 0
|
||||
? job.joblines.map((jl) =>
|
||||
GenerateDetailLines(jl, job.bodyshop.md_order_statuses)
|
||||
GenerateDetailLines(job, jl, job.bodyshop.md_order_statuses)
|
||||
)
|
||||
: [generateNullDetailLine()],
|
||||
},
|
||||
@@ -638,7 +675,7 @@ const CreateCosts = (job) => {
|
||||
});
|
||||
return bill_acc;
|
||||
}, {});
|
||||
const materialsHours = { mapaHrs: 0, mashHrs: 0 };
|
||||
|
||||
//If the hourly rates for job costing are set, add them in.
|
||||
if (job.bodyshop.jc_hourly_rates && job.bodyshop.jc_hourly_rates.mapa) {
|
||||
if (
|
||||
@@ -659,9 +696,32 @@ const CreateCosts = (job) => {
|
||||
(job.bodyshop.jc_hourly_rates &&
|
||||
job.bodyshop.jc_hourly_rates.mapa * 100) ||
|
||||
0,
|
||||
}).multiply(materialsHours.mapaHrs)
|
||||
}).multiply(job.job_totals.rates.mapa.hours)
|
||||
);
|
||||
}
|
||||
if (job.bodyshop.jc_hourly_rates && job.bodyshop.jc_hourly_rates.mash) {
|
||||
if (
|
||||
!billTotalsByCostCenters[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MASH
|
||||
]
|
||||
)
|
||||
billTotalsByCostCenters[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MASH
|
||||
] = Dinero();
|
||||
billTotalsByCostCenters[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MASH
|
||||
] = billTotalsByCostCenters[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MASH
|
||||
].add(
|
||||
Dinero({
|
||||
amount:
|
||||
(job.bodyshop.jc_hourly_rates &&
|
||||
job.bodyshop.jc_hourly_rates.mash * 100) ||
|
||||
0,
|
||||
}).multiply(job.job_totals.rates.mash.hours)
|
||||
);
|
||||
}
|
||||
|
||||
const ticketTotalsByCostCenter = job.timetickets.reduce(
|
||||
(ticket_acc, ticket_val) => {
|
||||
//At the invoice level.
|
||||
@@ -684,7 +744,10 @@ const CreateCosts = (job) => {
|
||||
|
||||
return {
|
||||
PartsTotalCost: Object.keys(billTotalsByCostCenters).reduce((acc, key) => {
|
||||
return acc.add(billTotalsByCostCenters[key]);
|
||||
if (key !== defaultCosts.PAS && key !== defaultCosts.PASL)
|
||||
return acc.add(billTotalsByCostCenters[key]);
|
||||
|
||||
return acc;
|
||||
}, Dinero()),
|
||||
PartsOemCost: (billTotalsByCostCenters[defaultCosts.PAN] || Dinero()).add(
|
||||
billTotalsByCostCenters[defaultCosts.PAP] || Dinero()
|
||||
@@ -694,7 +757,9 @@ const CreateCosts = (job) => {
|
||||
billTotalsByCostCenters[defaultCosts.PAM] || Dinero(),
|
||||
PartsRecycledCost: billTotalsByCostCenters[defaultCosts.PAL] || Dinero(),
|
||||
PartsOtherCost: billTotalsByCostCenters[defaultCosts.PAO] || Dinero(),
|
||||
SubletTotalCost: billTotalsByCostCenters[defaultCosts.PAS] || Dinero(),
|
||||
SubletTotalCost:
|
||||
billTotalsByCostCenters[defaultCosts.PAS] ||
|
||||
Dinero(billTotalsByCostCenters[defaultCosts.PASL] || Dinero()),
|
||||
BodyLaborTotalCost: ticketTotalsByCostCenter[defaultCosts.LAB] || Dinero(),
|
||||
RefinishLaborTotalCost:
|
||||
ticketTotalsByCostCenter[defaultCosts.LAR] || Dinero(),
|
||||
@@ -747,10 +812,15 @@ const StatusMapping = (status, md_ro_statuses) => {
|
||||
// default: return "UNDEFINED"
|
||||
};
|
||||
|
||||
const GenerateDetailLines = (line, statuses) => {
|
||||
const GenerateDetailLines = (job, line, statuses) => {
|
||||
const ret = {
|
||||
BackOrdered: line.status === statuses.default_bo ? "1" : "0",
|
||||
Cost: (line.billlines[0] && line.billlines[0].actual_cost.toFixed(2)) || 0,
|
||||
Cost:
|
||||
(line.billlines[0] &&
|
||||
(line.billlines[0].actual_cost * line.billlines[0].quantity).toFixed(
|
||||
2
|
||||
)) ||
|
||||
0,
|
||||
//Critical: null,
|
||||
Description: line.line_desc || "",
|
||||
DiscountMarkup: line.prt_dsmk_m || 0,
|
||||
@@ -777,11 +847,7 @@ const GenerateDetailLines = (line, statuses) => {
|
||||
Vendor: (line.billlines[0] && line.billlines[0].bill.vendor.name) || "",
|
||||
VendorPaid: null,
|
||||
VendorPrice:
|
||||
(line.billlines[0] &&
|
||||
(line.billlines[0].actual_price * line.billlines[0].quantity).toFixed(
|
||||
2
|
||||
)) ||
|
||||
0,
|
||||
(line.billlines[0] && line.billlines[0].actual_price.toFixed(2)) || 0,
|
||||
Deleted: null,
|
||||
ExpectedOn: null,
|
||||
ReceivedOn:
|
||||
|
||||
@@ -45,6 +45,25 @@ exports.sendServerEmail = async function ({ subject, text }) {
|
||||
logger.log("server-email-failure", "error", null, null, error);
|
||||
}
|
||||
};
|
||||
exports.sendTaskEmail = async function ({ to, subject, text, attachments }) {
|
||||
try {
|
||||
transporter.sendMail(
|
||||
{
|
||||
from: `ImEX Online <noreply@imex.online>`,
|
||||
to: to,
|
||||
subject: subject,
|
||||
text: text,
|
||||
attachments: attachments || null,
|
||||
},
|
||||
(err, info) => {
|
||||
console.log(err || info);
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
logger.log("server-email-failure", "error", null, null, error);
|
||||
}
|
||||
};
|
||||
|
||||
exports.sendEmail = async (req, res) => {
|
||||
logger.log("send-email", "DEBUG", req.user.email, null, {
|
||||
|
||||
@@ -25,6 +25,43 @@ const adminEmail = [
|
||||
"patrick@thinkimex.com",
|
||||
];
|
||||
|
||||
exports.createUser = (req, res) => {
|
||||
logger.log("admin-create-user", "WARN", req.user.email, null, {
|
||||
request: req.body,
|
||||
});
|
||||
if (!adminEmail.includes(req.user.email)) {
|
||||
logger.log(
|
||||
"admin-create-user-unauthorized",
|
||||
"ERROR",
|
||||
req.user.email,
|
||||
null,
|
||||
{
|
||||
request: req.body,
|
||||
user: req.user,
|
||||
}
|
||||
);
|
||||
res.sendStatus(404);
|
||||
}
|
||||
const { email, displayName, password } = req.body;
|
||||
admin
|
||||
.auth()
|
||||
.createUser({ email, displayName, password })
|
||||
.then((userRecord) => {
|
||||
// See the UserRecord reference doc for the contents of userRecord.
|
||||
|
||||
logger.log("admin-update-user-success", "DEBUG", req.user.email, null, {
|
||||
userRecord,
|
||||
});
|
||||
res.json(userRecord);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.log("admin-update-user-error", "ERROR", req.user.email, null, {
|
||||
error,
|
||||
});
|
||||
res.status(500).json(error);
|
||||
});
|
||||
};
|
||||
|
||||
exports.updateUser = (req, res) => {
|
||||
logger.log("admin-update-user", "WARN", req.user.email, null, {
|
||||
request: req.body,
|
||||
|
||||
@@ -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{
|
||||
@@ -400,6 +404,7 @@ query QUERY_BILLS_FOR_PAYABLES_EXPORT($bills: [uuid!]!) {
|
||||
vendor{
|
||||
id
|
||||
name
|
||||
due_date
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -411,6 +416,7 @@ exports.QUERY_PAYMENTS_FOR_EXPORT = `
|
||||
id
|
||||
md_responsibility_centers
|
||||
accountingconfig
|
||||
timezone
|
||||
}
|
||||
payments(where: {id: {_in: $payments}}) {
|
||||
id
|
||||
@@ -460,6 +466,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 +565,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
|
||||
@@ -574,7 +581,6 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
|
||||
ownr_st
|
||||
ownr_ph1
|
||||
ownr_zip
|
||||
referral_source
|
||||
loss_type
|
||||
v_model_yr
|
||||
v_model_desc
|
||||
@@ -632,7 +638,7 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
|
||||
job_totals
|
||||
driveable
|
||||
parts_tax_rates
|
||||
|
||||
ded_amt
|
||||
joblines(where: {removed: {_eq: false}}) {
|
||||
id
|
||||
line_no
|
||||
@@ -828,6 +834,7 @@ exports.GET_JOB_BY_PK = ` query GET_JOB_BY_PK($id: uuid!) {
|
||||
loss_desc
|
||||
kmin
|
||||
kmout
|
||||
comment
|
||||
referral_source
|
||||
referral_source_extra
|
||||
unit_number
|
||||
@@ -1234,6 +1241,7 @@ exports.GET_AUTOHOUSE_SHOPS = `query GET_AUTOHOUSE_SHOPS {
|
||||
md_responsibility_centers
|
||||
jc_hourly_rates
|
||||
imexshopid
|
||||
timezone
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -31,8 +31,8 @@ async function JobCosting(req, res) {
|
||||
res.status(200).json(ret);
|
||||
} catch (error) {
|
||||
logger.log("job-costing-error", "ERROR", req.user.email, jobid, {
|
||||
jobid,
|
||||
error,
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
|
||||
res.status(400).send(JSON.stringify(error));
|
||||
@@ -62,19 +62,29 @@ async function JobCostingMulti(req, res) {
|
||||
summaryData: {
|
||||
totalLaborSales: Dinero({ amount: 0 }),
|
||||
totalPartsSales: Dinero({ amount: 0 }),
|
||||
totalAdditionalSales: Dinero({ amount: 0 }),
|
||||
totalSubletSales: Dinero({ amount: 0 }),
|
||||
totalSales: Dinero({ amount: 0 }),
|
||||
totalLaborCost: Dinero({ amount: 0 }),
|
||||
totalPartsCost: Dinero({ amount: 0 }),
|
||||
totalAdditionalCost: Dinero({ amount: 0 }),
|
||||
totalSubletCost: Dinero({ amount: 0 }),
|
||||
totalCost: Dinero({ amount: 0 }),
|
||||
gpdollars: Dinero({ amount: 0 }),
|
||||
gppercent: null,
|
||||
gppercentFormatted: null,
|
||||
totalLaborGp: Dinero({ amount: 0 }),
|
||||
totalPartsGp: Dinero({ amount: 0 }),
|
||||
totalAdditionalGp: Dinero({ amount: 0 }),
|
||||
totalSubletGp: Dinero({ amount: 0 }),
|
||||
totalLaborGppercent: null,
|
||||
totalLaborGppercentFormatted: null,
|
||||
totalPartsGppercent: null,
|
||||
totalPartsGppercentFormatted: null,
|
||||
totalAdditionalGppercent: null,
|
||||
totalAdditionalGppercentFormatted: null,
|
||||
totalSubletGppercent: null,
|
||||
totalSubletGppercentFormatted: null,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -102,12 +112,24 @@ async function JobCostingMulti(req, res) {
|
||||
sale_parts_dinero: multiSummary.costCenterData[
|
||||
CostCenterIndex
|
||||
].sale_parts_dinero.add(c.sale_parts_dinero),
|
||||
sale_additional_dinero: multiSummary.costCenterData[
|
||||
CostCenterIndex
|
||||
].sale_additional_dinero.add(c.sale_additional_dinero),
|
||||
sale_sublet_dinero: multiSummary.costCenterData[
|
||||
CostCenterIndex
|
||||
].sale_sublet_dinero.add(c.sale_sublet_dinero),
|
||||
cost_labor_dinero: multiSummary.costCenterData[
|
||||
CostCenterIndex
|
||||
].cost_labor_dinero.add(c.cost_labor_dinero),
|
||||
cost_parts_dinero: multiSummary.costCenterData[
|
||||
CostCenterIndex
|
||||
].cost_parts_dinero.add(c.cost_parts_dinero),
|
||||
cost_additional_dinero: multiSummary.costCenterData[
|
||||
CostCenterIndex
|
||||
].cost_additional_dinero.add(c.cost_additional_dinero),
|
||||
cost_sublet_dinero: multiSummary.costCenterData[
|
||||
CostCenterIndex
|
||||
].cost_sublet_dinero.add(c.cost_sublet_dinero),
|
||||
gpdollars_dinero: multiSummary.costCenterData[
|
||||
CostCenterIndex
|
||||
].gpdollars_dinero.add(c.gpdollars_dinero),
|
||||
@@ -129,6 +151,14 @@ async function JobCostingMulti(req, res) {
|
||||
multiSummary.summaryData.totalPartsSales.add(
|
||||
costingData.summaryData.totalPartsSales
|
||||
);
|
||||
multiSummary.summaryData.totalAdditionalSales =
|
||||
multiSummary.summaryData.totalAdditionalSales.add(
|
||||
costingData.summaryData.totalAdditionalSales
|
||||
);
|
||||
multiSummary.summaryData.totalSubletSales =
|
||||
multiSummary.summaryData.totalSubletSales.add(
|
||||
costingData.summaryData.totalSubletSales
|
||||
);
|
||||
multiSummary.summaryData.totalSales =
|
||||
multiSummary.summaryData.totalSales.add(
|
||||
costingData.summaryData.totalSales
|
||||
@@ -145,6 +175,14 @@ async function JobCostingMulti(req, res) {
|
||||
multiSummary.summaryData.totalPartsCost.add(
|
||||
costingData.summaryData.totalPartsCost
|
||||
);
|
||||
multiSummary.summaryData.totalAdditionalCost =
|
||||
multiSummary.summaryData.totalAdditionalCost.add(
|
||||
costingData.summaryData.totalAdditionalCost
|
||||
);
|
||||
multiSummary.summaryData.totalSubletCost =
|
||||
multiSummary.summaryData.totalSubletCost.add(
|
||||
costingData.summaryData.totalSubletCost
|
||||
);
|
||||
multiSummary.summaryData.totalCost =
|
||||
multiSummary.summaryData.totalCost.add(
|
||||
costingData.summaryData.totalCost
|
||||
@@ -162,6 +200,14 @@ async function JobCostingMulti(req, res) {
|
||||
multiSummary.summaryData.totalPartsGp.add(
|
||||
costingData.summaryData.totalPartsGp
|
||||
);
|
||||
multiSummary.summaryData.totalAdditionalGp =
|
||||
multiSummary.summaryData.totalAdditionalGp.add(
|
||||
costingData.summaryData.totalAdditionalGp
|
||||
);
|
||||
multiSummary.summaryData.totalSubletGp =
|
||||
multiSummary.summaryData.totalSubletGp.add(
|
||||
costingData.summaryData.totalSubletGp
|
||||
);
|
||||
|
||||
//Take the summary data & add it to total summary data.
|
||||
});
|
||||
@@ -182,10 +228,30 @@ async function JobCostingMulti(req, res) {
|
||||
multiSummary.summaryData.totalPartsSales.getAmount()) *
|
||||
100
|
||||
).toFixed(2);
|
||||
|
||||
multiSummary.summaryData.totalPartsGppercentFormatted = formatGpPercent(
|
||||
multiSummary.summaryData.totalPartsGppercent
|
||||
);
|
||||
|
||||
multiSummary.summaryData.totalAdditionalGppercent = (
|
||||
(multiSummary.summaryData.totalAdditionalGp.getAmount() /
|
||||
multiSummary.summaryData.totalAdditionalSales.getAmount()) *
|
||||
100
|
||||
).toFixed(2);
|
||||
|
||||
multiSummary.summaryData.totalAdditionalGppercentFormatted =
|
||||
formatGpPercent(multiSummary.summaryData.totalAdditionalGppercent);
|
||||
|
||||
multiSummary.summaryData.totalSubletGppercent = (
|
||||
(multiSummary.summaryData.totalSubletGp.getAmount() /
|
||||
multiSummary.summaryData.totalSubletSales.getAmount()) *
|
||||
100
|
||||
).toFixed(2);
|
||||
|
||||
multiSummary.summaryData.totalSubletGppercentFormatted = formatGpPercent(
|
||||
multiSummary.summaryData.totalSubletGppercent
|
||||
);
|
||||
|
||||
multiSummary.summaryData.gppercent = (
|
||||
(multiSummary.summaryData.gpdollars.getAmount() /
|
||||
multiSummary.summaryData.totalSales.getAmount()) *
|
||||
@@ -201,10 +267,16 @@ async function JobCostingMulti(req, res) {
|
||||
...c,
|
||||
sale_labor: c.sale_labor_dinero && c.sale_labor_dinero.toFormat(),
|
||||
sale_parts: c.sale_parts_dinero && c.sale_parts_dinero.toFormat(),
|
||||
sales: c.sale_labor_dinero.add(c.sale_parts_dinero).toFormat(),
|
||||
sale_additional:
|
||||
c.sale_additional_dinero && c.sale_additional_dinero.toFormat(),
|
||||
sale_sublet: c.sale_sublet_dinero && c.sale_sublet_dinero.toFormat(),
|
||||
sales: c.sales_dinero.toFormat(),
|
||||
cost_parts: c.cost_parts_dinero && c.cost_parts_dinero.toFormat(),
|
||||
cost_labor: c.cost_labor_dinero && c.cost_labor_dinero.toFormat(),
|
||||
costs: c.cost_parts_dinero.add(c.cost_labor_dinero).toFormat(),
|
||||
cost_additional:
|
||||
c.cost_additional_dinero && c.cost_additional_dinero.toFormat(),
|
||||
cost_sublet: c.cost_sublet_dinero && c.cost_sublet_dinero.toFormat(),
|
||||
costs: c.costs_dinero.toFormat(),
|
||||
gpdollars: c.gpdollars_dinero.toFormat(),
|
||||
gppercent: formatGpPercent(
|
||||
(
|
||||
@@ -224,10 +296,10 @@ async function JobCostingMulti(req, res) {
|
||||
});
|
||||
} catch (error) {
|
||||
logger.log("job-costing-multi-error", "ERROR", req.user.email, [jobids], {
|
||||
jobids,
|
||||
error,
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
res.status(400).send(JSON.stringify(error));
|
||||
res.status(400).send(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,7 +352,12 @@ function GenerateCostingData(job) {
|
||||
}
|
||||
}
|
||||
|
||||
if (val.part_type && val.part_type !== "PAE") {
|
||||
if (
|
||||
val.part_type &&
|
||||
val.part_type !== "PAE" &&
|
||||
val.part_type !== "PAS" &&
|
||||
val.part_type !== "PASL"
|
||||
) {
|
||||
const partsProfitCenter =
|
||||
val.profitcenter_part || defaultProfits[val.part_type] || "?";
|
||||
|
||||
@@ -312,6 +389,42 @@ function GenerateCostingData(job) {
|
||||
acc.parts[partsProfitCenter] =
|
||||
acc.parts[partsProfitCenter].add(partsAmount);
|
||||
}
|
||||
if (
|
||||
val.part_type &&
|
||||
val.part_type !== "PAE" &&
|
||||
(val.part_type === "PAS" || val.part_type === "PASL")
|
||||
) {
|
||||
const partsProfitCenter =
|
||||
val.profitcenter_part || defaultProfits[val.part_type] || "?";
|
||||
|
||||
if (partsProfitCenter === "?")
|
||||
console.log("Unknown type", val.line_desc, val.part_type);
|
||||
|
||||
if (!partsProfitCenter)
|
||||
console.log(
|
||||
"Unknown cost/profit center mapping for sublet.",
|
||||
val.line_desc,
|
||||
val.part_type
|
||||
);
|
||||
const partsAmount = Dinero({
|
||||
amount: Math.round((val.act_price || 0) * 100),
|
||||
})
|
||||
.multiply(val.part_qty || 1)
|
||||
.add(
|
||||
val.prt_dsmk_m && val.prt_dsmk_m !== 0
|
||||
? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) })
|
||||
: Dinero({
|
||||
amount: Math.round(val.act_price * 100),
|
||||
})
|
||||
.multiply(val.part_qty || 0)
|
||||
.percentage(Math.abs(val.prt_dsmk_p || 0))
|
||||
.multiply(val.prt_dsmk_p > 0 ? 1 : -1)
|
||||
);
|
||||
if (!acc.sublet[partsProfitCenter])
|
||||
acc.sublet[partsProfitCenter] = Dinero();
|
||||
acc.sublet[partsProfitCenter] =
|
||||
acc.sublet[partsProfitCenter].add(partsAmount);
|
||||
}
|
||||
|
||||
//To deal with additional costs.
|
||||
if (!val.part_type && !val.mod_lbr_ty) {
|
||||
@@ -340,33 +453,33 @@ function GenerateCostingData(job) {
|
||||
.multiply(val.prt_dsmk_p > 0 ? 1 : -1)
|
||||
);
|
||||
|
||||
if (!acc.parts[partsProfitCenter])
|
||||
acc.parts[partsProfitCenter] = Dinero();
|
||||
acc.parts[partsProfitCenter] =
|
||||
acc.parts[partsProfitCenter].add(partsAmount);
|
||||
if (!acc.additional[partsProfitCenter])
|
||||
acc.additional[partsProfitCenter] = Dinero();
|
||||
acc.additional[partsProfitCenter] =
|
||||
acc.additional[partsProfitCenter].add(partsAmount);
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{ parts: {}, labor: {} }
|
||||
{ parts: {}, labor: {}, additional: {}, sublet: {} }
|
||||
);
|
||||
|
||||
if (!hasMapaLine) {
|
||||
if (!jobLineTotalsByProfitCenter.parts[defaultProfits["MAPA"]])
|
||||
jobLineTotalsByProfitCenter.parts[defaultProfits["MAPA"]] = Dinero();
|
||||
jobLineTotalsByProfitCenter.parts[defaultProfits["MAPA"]] =
|
||||
jobLineTotalsByProfitCenter.parts[defaultProfits["MAPA"]].add(
|
||||
if (!jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]])
|
||||
jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]] = Dinero();
|
||||
jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]] =
|
||||
jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]].add(
|
||||
Dinero({
|
||||
amount: Math.round((job.rate_mapa || 0) * 100),
|
||||
}).multiply(materialsHours.mapaHrs || 0)
|
||||
);
|
||||
}
|
||||
if (!hasMashLine) {
|
||||
if (!jobLineTotalsByProfitCenter.parts[defaultProfits["MASH"]])
|
||||
jobLineTotalsByProfitCenter.parts[defaultProfits["MASH"]] = Dinero();
|
||||
jobLineTotalsByProfitCenter.parts[defaultProfits["MASH"]] =
|
||||
jobLineTotalsByProfitCenter.parts[defaultProfits["MASH"]].add(
|
||||
if (!jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]])
|
||||
jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]] = Dinero();
|
||||
jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]] =
|
||||
jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]].add(
|
||||
Dinero({
|
||||
amount: Math.round((job.rate_mash || 0) * 100),
|
||||
}).multiply(materialsHours.mashHrs || 0)
|
||||
@@ -381,54 +494,107 @@ function GenerateCostingData(job) {
|
||||
)) ||
|
||||
job.bodyshop.md_responsibility_centers.defaults;
|
||||
|
||||
const billTotalsByCostCenters = job.bills.reduce((bill_acc, bill_val) => {
|
||||
//At the bill level.
|
||||
bill_val.billlines.map((line_val) => {
|
||||
//At the bill line level.
|
||||
if (job.bodyshop.pbs_serialnumber || job.bodyshop.cdk_dealerid) {
|
||||
if (!bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]])
|
||||
const billTotalsByCostCenters = job.bills.reduce(
|
||||
(bill_acc, bill_val) => {
|
||||
//At the bill level.
|
||||
bill_val.billlines.map((line_val) => {
|
||||
//At the bill line level.
|
||||
if (job.bodyshop.pbs_serialnumber || job.bodyshop.cdk_dealerid) {
|
||||
if (
|
||||
!bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]]
|
||||
)
|
||||
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] =
|
||||
Dinero();
|
||||
|
||||
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] =
|
||||
Dinero();
|
||||
bill_acc[
|
||||
selectedDmsAllocationConfig.costs[line_val.cost_center]
|
||||
].add(
|
||||
Dinero({
|
||||
amount: Math.round((line_val.actual_cost || 0) * 100),
|
||||
})
|
||||
.multiply(line_val.quantity)
|
||||
.multiply(bill_val.is_credit_memo ? -1 : 1)
|
||||
);
|
||||
} else {
|
||||
const isSubletCostCenter =
|
||||
line_val.cost_center ===
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.PAS ||
|
||||
line_val.cost_center ===
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.PASL;
|
||||
|
||||
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] =
|
||||
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]].add(
|
||||
Dinero({
|
||||
amount: Math.round((line_val.actual_cost || 0) * 100),
|
||||
})
|
||||
.multiply(line_val.quantity)
|
||||
.multiply(bill_val.is_credit_memo ? -1 : 1)
|
||||
);
|
||||
} else {
|
||||
if (!bill_acc[line_val.cost_center])
|
||||
bill_acc[line_val.cost_center] = Dinero();
|
||||
const isAdditionalCostCenter =
|
||||
// line_val.cost_center ===
|
||||
// job.bodyshop.md_responsibility_centers.defaults.costs.PAS ||
|
||||
// line_val.cost_center ===
|
||||
// job.bodyshop.md_responsibility_centers.defaults.costs.PASL ||
|
||||
line_val.cost_center ===
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.TOW ||
|
||||
line_val.cost_center ===
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA ||
|
||||
line_val.cost_center ===
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MASH;
|
||||
|
||||
bill_acc[line_val.cost_center] = bill_acc[line_val.cost_center].add(
|
||||
Dinero({
|
||||
amount: Math.round((line_val.actual_cost || 0) * 100),
|
||||
})
|
||||
.multiply(line_val.quantity)
|
||||
.multiply(bill_val.is_credit_memo ? -1 : 1)
|
||||
);
|
||||
}
|
||||
if (isAdditionalCostCenter) {
|
||||
if (!bill_acc.additionalCosts[line_val.cost_center])
|
||||
bill_acc.additionalCosts[line_val.cost_center] = Dinero();
|
||||
|
||||
return null;
|
||||
});
|
||||
return bill_acc;
|
||||
}, {});
|
||||
bill_acc.additionalCosts[line_val.cost_center] =
|
||||
bill_acc.additionalCosts[line_val.cost_center].add(
|
||||
Dinero({
|
||||
amount: Math.round((line_val.actual_cost || 0) * 100),
|
||||
})
|
||||
.multiply(line_val.quantity)
|
||||
.multiply(bill_val.is_credit_memo ? -1 : 1)
|
||||
);
|
||||
} else if (isSubletCostCenter) {
|
||||
if (!bill_acc.subletCosts[line_val.cost_center])
|
||||
bill_acc.subletCosts[line_val.cost_center] = Dinero();
|
||||
|
||||
bill_acc.subletCosts[line_val.cost_center] = bill_acc.subletCosts[
|
||||
line_val.cost_center
|
||||
].add(
|
||||
Dinero({
|
||||
amount: Math.round((line_val.actual_cost || 0) * 100),
|
||||
})
|
||||
.multiply(line_val.quantity)
|
||||
.multiply(bill_val.is_credit_memo ? -1 : 1)
|
||||
);
|
||||
} else {
|
||||
if (!bill_acc[line_val.cost_center])
|
||||
bill_acc[line_val.cost_center] = Dinero();
|
||||
|
||||
bill_acc[line_val.cost_center] = bill_acc[line_val.cost_center].add(
|
||||
Dinero({
|
||||
amount: Math.round((line_val.actual_cost || 0) * 100),
|
||||
})
|
||||
.multiply(line_val.quantity)
|
||||
.multiply(bill_val.is_credit_memo ? -1 : 1)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
return bill_acc;
|
||||
},
|
||||
{ additionalCosts: {}, subletCosts: {} }
|
||||
);
|
||||
|
||||
//If the hourly rates for job costing are set, add them in.
|
||||
|
||||
if (job.bodyshop.jc_hourly_rates && job.bodyshop.jc_hourly_rates.mapa) {
|
||||
if (
|
||||
!billTotalsByCostCenters[
|
||||
!billTotalsByCostCenters.additionalCosts[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||
]
|
||||
)
|
||||
billTotalsByCostCenters[
|
||||
billTotalsByCostCenters.additionalCosts[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||
] = Dinero();
|
||||
billTotalsByCostCenters[
|
||||
billTotalsByCostCenters.additionalCosts[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||
] = billTotalsByCostCenters[
|
||||
] = billTotalsByCostCenters.additionalCosts[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||
].add(
|
||||
Dinero({
|
||||
@@ -442,16 +608,16 @@ function GenerateCostingData(job) {
|
||||
|
||||
if (job.bodyshop.jc_hourly_rates && job.bodyshop.jc_hourly_rates.mash) {
|
||||
if (
|
||||
!billTotalsByCostCenters[
|
||||
!billTotalsByCostCenters.additionalCosts[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MASH
|
||||
]
|
||||
)
|
||||
billTotalsByCostCenters[
|
||||
billTotalsByCostCenters.additionalCosts[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MASH
|
||||
] = Dinero();
|
||||
billTotalsByCostCenters[
|
||||
billTotalsByCostCenters.additionalCosts[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MASH
|
||||
] = billTotalsByCostCenters[
|
||||
] = billTotalsByCostCenters.additionalCosts[
|
||||
job.bodyshop.md_responsibility_centers.defaults.costs.MASH
|
||||
].add(
|
||||
Dinero({
|
||||
@@ -511,18 +677,27 @@ function GenerateCostingData(job) {
|
||||
const summaryData = {
|
||||
totalLaborSales: Dinero({ amount: 0 }),
|
||||
totalPartsSales: Dinero({ amount: 0 }),
|
||||
totalAdditionalSales: Dinero({ amount: 0 }),
|
||||
totalSubletSales: Dinero({ amount: 0 }),
|
||||
totalSales: Dinero({ amount: 0 }),
|
||||
totalLaborCost: Dinero({ amount: 0 }),
|
||||
totalPartsCost: Dinero({ amount: 0 }),
|
||||
totalAdditionalCost: Dinero({ amount: 0 }),
|
||||
totalSubletCost: Dinero({ amount: 0 }),
|
||||
totalCost: Dinero({ amount: 0 }),
|
||||
totalLaborGp: Dinero({ amount: 0 }),
|
||||
totalPartsGp: Dinero({ amount: 0 }),
|
||||
totalAdditionalGp: Dinero({ amount: 0 }),
|
||||
totalSubletGp: Dinero({ amount: 0 }),
|
||||
gpdollars: Dinero({ amount: 0 }),
|
||||
|
||||
totalLaborGppercent: null,
|
||||
totalLaborGppercentFormatted: null,
|
||||
totalPartsGppercent: null,
|
||||
totalPartsGppercentFormatted: null,
|
||||
totalAdditionalGppercent: null,
|
||||
totalAdditionalGppercentFormatted: null,
|
||||
totalSubletGppercent: null,
|
||||
totalSubletGppercentFormatted: null,
|
||||
gppercent: null,
|
||||
gppercentFormatted: null,
|
||||
};
|
||||
@@ -533,14 +708,26 @@ function GenerateCostingData(job) {
|
||||
jobLineTotalsByProfitCenter.labor[ccVal] || Dinero({ amount: 0 });
|
||||
const sale_parts =
|
||||
jobLineTotalsByProfitCenter.parts[ccVal] || Dinero({ amount: 0 });
|
||||
const sale_additional =
|
||||
jobLineTotalsByProfitCenter.additional[ccVal] || Dinero({ amount: 0 });
|
||||
const sale_sublet =
|
||||
jobLineTotalsByProfitCenter.sublet[ccVal] || Dinero({ amount: 0 });
|
||||
|
||||
const cost_labor = ticketTotalsByCostCenter[ccVal] || Dinero({ amount: 0 });
|
||||
const cost_parts = billTotalsByCostCenters[ccVal] || Dinero({ amount: 0 });
|
||||
const cost_additional =
|
||||
billTotalsByCostCenters.additionalCosts[ccVal] || Dinero({ amount: 0 });
|
||||
const cost_sublet =
|
||||
billTotalsByCostCenters.subletCosts[ccVal] || Dinero({ amount: 0 });
|
||||
|
||||
const costs = (billTotalsByCostCenters[ccVal] || Dinero({ amount: 0 })).add(
|
||||
ticketTotalsByCostCenter[ccVal] || Dinero({ amount: 0 })
|
||||
);
|
||||
const totalSales = sale_labor.add(sale_parts);
|
||||
const costs = cost_labor
|
||||
.add(cost_parts)
|
||||
.add(cost_additional)
|
||||
.add(cost_sublet);
|
||||
const totalSales = sale_labor
|
||||
.add(sale_parts)
|
||||
.add(sale_additional)
|
||||
.add(sale_sublet);
|
||||
const gpdollars = totalSales.subtract(costs);
|
||||
const gppercent = (
|
||||
(gpdollars.getAmount() / totalSales.getAmount()) *
|
||||
@@ -550,11 +737,16 @@ function GenerateCostingData(job) {
|
||||
//Push summary data to avoid extra loop.
|
||||
summaryData.totalLaborSales = summaryData.totalLaborSales.add(sale_labor);
|
||||
summaryData.totalPartsSales = summaryData.totalPartsSales.add(sale_parts);
|
||||
summaryData.totalSales = summaryData.totalSales
|
||||
.add(sale_labor)
|
||||
.add(sale_parts);
|
||||
summaryData.totalAdditionalSales =
|
||||
summaryData.totalAdditionalSales.add(sale_additional);
|
||||
summaryData.totalSubletSales =
|
||||
summaryData.totalSubletSales.add(sale_sublet);
|
||||
summaryData.totalSales = summaryData.totalSales.add(totalSales);
|
||||
summaryData.totalLaborCost = summaryData.totalLaborCost.add(cost_labor);
|
||||
summaryData.totalPartsCost = summaryData.totalPartsCost.add(cost_parts);
|
||||
summaryData.totalAdditionalCost =
|
||||
summaryData.totalAdditionalCost.add(cost_additional);
|
||||
summaryData.totalSubletCost = summaryData.totalSubletCost.add(cost_sublet);
|
||||
summaryData.totalCost = summaryData.totalCost.add(costs);
|
||||
|
||||
return {
|
||||
@@ -564,14 +756,22 @@ function GenerateCostingData(job) {
|
||||
sale_labor_dinero: sale_labor,
|
||||
sale_parts: sale_parts && sale_parts.toFormat(),
|
||||
sale_parts_dinero: sale_parts,
|
||||
sales: sale_labor.add(sale_parts).toFormat(),
|
||||
sales_dinero: sale_labor.add(sale_parts),
|
||||
sale_additional: sale_additional && sale_additional.toFormat(),
|
||||
sale_additional_dinero: sale_additional,
|
||||
sale_sublet: sale_sublet && sale_sublet.toFormat(),
|
||||
sale_sublet_dinero: sale_sublet,
|
||||
sales: totalSales.toFormat(),
|
||||
sales_dinero: totalSales,
|
||||
cost_parts: cost_parts && cost_parts.toFormat(),
|
||||
cost_parts_dinero: cost_parts,
|
||||
cost_labor: cost_labor && cost_labor.toFormat(),
|
||||
cost_labor_dinero: cost_labor,
|
||||
costs: cost_parts.add(cost_labor).toFormat(),
|
||||
costs_dinero: cost_parts.add(cost_labor),
|
||||
cost_additional: cost_additional && cost_additional.toFormat(),
|
||||
cost_additional_dinero: cost_additional,
|
||||
cost_sublet: cost_sublet && cost_sublet.toFormat(),
|
||||
cost_sublet_dinero: cost_sublet,
|
||||
costs: costs.toFormat(),
|
||||
costs_dinero: costs,
|
||||
gpdollars_dinero: gpdollars,
|
||||
gpdollars: gpdollars.toFormat(),
|
||||
gppercent: formatGpPercent(gppercent),
|
||||
@@ -592,12 +792,20 @@ function GenerateCostingData(job) {
|
||||
sale_labor_dinero: Dinero(),
|
||||
sale_parts: Dinero().toFormat(),
|
||||
sale_parts_dinero: Dinero(),
|
||||
sale_additional: Dinero(),
|
||||
sale_additional_dinero: Dinero(),
|
||||
sale_sublet: Dinero(),
|
||||
sale_sublet_dinero: Dinero(),
|
||||
sales: Dinero().toFormat(),
|
||||
sales_dinero: Dinero(),
|
||||
cost_parts: Dinero().toFormat(),
|
||||
cost_parts_dinero: Dinero(),
|
||||
cost_labor: Adjustment.toFormat(),
|
||||
cost_labor_dinero: Adjustment,
|
||||
cost_additional: Dinero(),
|
||||
cost_additional_dinero: Dinero(),
|
||||
cost_sublet: Dinero(),
|
||||
cost_sublet_dinero: Dinero(),
|
||||
costs: Adjustment.toFormat(),
|
||||
costs_dinero: Adjustment,
|
||||
gpdollars_dinero: Dinero(),
|
||||
@@ -631,6 +839,28 @@ function GenerateCostingData(job) {
|
||||
summaryData.totalPartsGppercentFormatted = formatGpPercent(
|
||||
summaryData.totalPartsGppercent
|
||||
);
|
||||
summaryData.totalAdditionalGp = summaryData.totalAdditionalSales.subtract(
|
||||
summaryData.totalAdditionalCost
|
||||
);
|
||||
summaryData.totalAdditionalGppercent = (
|
||||
(summaryData.totalAdditionalGp.getAmount() /
|
||||
summaryData.totalAdditionalSales.getAmount()) *
|
||||
100
|
||||
).toFixed(2);
|
||||
summaryData.totalAdditionalGppercentFormatted = formatGpPercent(
|
||||
summaryData.totalAdditionalGppercent
|
||||
);
|
||||
summaryData.totalSubletGp = summaryData.totalSubletSales.subtract(
|
||||
summaryData.totalSubletCost
|
||||
);
|
||||
summaryData.totalSubletGppercent = (
|
||||
(summaryData.totalSubletGp.getAmount() /
|
||||
summaryData.totalSubletSales.getAmount()) *
|
||||
100
|
||||
).toFixed(2);
|
||||
summaryData.totalSubletGppercentFormatted = formatGpPercent(
|
||||
summaryData.totalSubletGppercent
|
||||
);
|
||||
|
||||
summaryData.gpdollars = summaryData.totalSales.subtract(
|
||||
summaryData.totalCost
|
||||
@@ -667,7 +897,7 @@ const formatGpPercent = (gppercent) => {
|
||||
//Verify that this stays in line with jobs-close-auto-allocate logic from the application.
|
||||
const getAdditionalCostCenter = (jl, profitCenters) => {
|
||||
if (!jl.part_type && !jl.mod_lbr_ty) {
|
||||
const lineDesc = jl.line_desc.toLowerCase();
|
||||
const lineDesc = jl.line_desc ? jl.line_desc.toLowerCase() : "";
|
||||
|
||||
if (lineDesc.includes("shop mat")) {
|
||||
return profitCenters["MASH"];
|
||||
@@ -675,6 +905,8 @@ const getAdditionalCostCenter = (jl, profitCenters) => {
|
||||
return profitCenters["MAPA"];
|
||||
} else if (lineDesc.includes("ats amount")) {
|
||||
return profitCenters["ATS"];
|
||||
} else if (lineDesc.includes("towing")) {
|
||||
return profitCenters["TOW"];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -442,8 +442,8 @@ function CalculateAdditional(job) {
|
||||
ret.total = ret.additionalCosts
|
||||
.add(ret.adjustments) //IO-813 Adjustment takes care of GST & PST at labor rate.
|
||||
.add(ret.towing)
|
||||
.add(ret.storage)
|
||||
.add(ret.pvrt);
|
||||
.add(ret.storage);
|
||||
//.add(ret.pvrt);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -453,6 +453,7 @@ function CalculateTaxesTotals(job, otherTotals) {
|
||||
.add(otherTotals.parts.sublets.subtotal)
|
||||
.add(otherTotals.rates.subtotal) //No longer using just rates subtotal to include mapa/mash.
|
||||
.add(otherTotals.additional.total);
|
||||
|
||||
// .add(Dinero({ amount: (job.towing_payable || 0) * 100 }))
|
||||
// .add(Dinero({ amount: (job.storage_payable || 0) * 100 }));
|
||||
|
||||
@@ -522,7 +523,13 @@ function CalculateTaxesTotals(job, otherTotals) {
|
||||
|
||||
let ret = {
|
||||
subtotal: subtotal,
|
||||
federal_tax: subtotal.percentage((job.federal_tax_rate || 0) * 100),
|
||||
federal_tax: subtotal
|
||||
.percentage((job.federal_tax_rate || 0) * 100)
|
||||
.add(
|
||||
otherTotals.additional.pvrt.percentage(
|
||||
(job.federal_tax_rate || 0) * 100
|
||||
)
|
||||
),
|
||||
statePartsTax,
|
||||
state_tax: statePartsTax
|
||||
.add(
|
||||
@@ -540,12 +547,14 @@ function CalculateTaxesTotals(job, otherTotals) {
|
||||
otherTotals.additional.storage.percentage((job.tax_str_rt || 0) * 100)
|
||||
)
|
||||
.add(additionalItemsTax),
|
||||
// .add(otherTotals.additional.pvrt),
|
||||
local_tax: subtotal.percentage((job.local_tax_rate || 0) * 100),
|
||||
};
|
||||
ret.total_repairs = ret.subtotal
|
||||
.add(ret.federal_tax)
|
||||
.add(ret.state_tax)
|
||||
.add(ret.local_tax);
|
||||
.add(ret.local_tax)
|
||||
.add(otherTotals.additional.pvrt);
|
||||
|
||||
ret.custPayable = {
|
||||
deductible: Dinero({ amount: Math.round((job.ded_amt || 0) * 100) }) || 0,
|
||||
|
||||
@@ -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,20 +102,18 @@ 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(),
|
||||
"day"
|
||||
)
|
||||
moment(item.actual_completion || item.scheduled_completion)
|
||||
.tz(timezone)
|
||||
.isBefore(moment().tz(timezone), "day")
|
||||
) {
|
||||
console.log("Job should have already gone. Ignoring it.", item);
|
||||
return;
|
||||
@@ -127,7 +127,9 @@ 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 +148,26 @@ 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);
|
||||
|
||||
const end = moment.max([
|
||||
...filteredArrJobs.map((a) => moment(a.scheduled_in)),
|
||||
...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(15, "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 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,13 +195,13 @@ 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 };
|
||||
}
|
||||
});
|
||||
// //Propose the first 5 dates where we are below target.
|
||||
// //Propose the first 10 dates where we are below target.
|
||||
|
||||
const possibleDates = [];
|
||||
delete load.productionTotal;
|
||||
@@ -215,10 +223,10 @@ exports.job = async (req, res) => {
|
||||
possibleDates.push(new Date(loadKey).toISOString().substr(0, 10));
|
||||
});
|
||||
|
||||
if (possibleDates.length < 6) {
|
||||
if (possibleDates.length < 11) {
|
||||
res.json(possibleDates);
|
||||
} else {
|
||||
res.json(possibleDates.slice(0, 5));
|
||||
res.json(possibleDates.slice(0, 10));
|
||||
}
|
||||
} catch (error) {
|
||||
logger.log("smart-scheduling-error", "ERROR", req.user.email, jobId, {
|
||||
|
||||
92
server/tasks/tasks.js
Normal file
92
server/tasks/tasks.js
Normal file
@@ -0,0 +1,92 @@
|
||||
const path = require("path");
|
||||
require("dotenv").config({
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
});
|
||||
const axios = require("axios");
|
||||
const client = require("../graphql-client/graphql-client").client;
|
||||
const emailer = require("../email/sendemail");
|
||||
const logger = require("../utils/logger");
|
||||
const moment = require("moment-timezone");
|
||||
exports.taskHandler = async (req, res) => {
|
||||
try {
|
||||
const { bodyshopid, query, variables, text, to, subject, timezone } =
|
||||
req.body;
|
||||
//Run the query
|
||||
|
||||
//Check the variables to see if they are an object.
|
||||
Object.keys(variables).forEach((key) => {
|
||||
if (typeof variables[key] === "object") {
|
||||
if (variables[key].function) {
|
||||
variables[key] = functionMapper(variables[key].function, timezone);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const response = await client.request(query, variables);
|
||||
//Massage the data
|
||||
//Send the email
|
||||
const rootElement = response[Object.keys(response)[0]]; //This element shoudl always be an array.
|
||||
let converter = require("json-2-csv");
|
||||
converter.json2csv(
|
||||
rootElement,
|
||||
(err, csv) => {
|
||||
if (err) {
|
||||
res.status(500).json(err);
|
||||
}
|
||||
|
||||
emailer.sendTaskEmail({
|
||||
to,
|
||||
subject,
|
||||
text,
|
||||
attachments: [{ filename: "query.csv", content: csv }],
|
||||
});
|
||||
res.status(200).send(csv);
|
||||
},
|
||||
{ emptyFieldValue: "" }
|
||||
);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message, stack: error.stackTrace });
|
||||
}
|
||||
};
|
||||
|
||||
const isoformat = "YYYY-MM-DD";
|
||||
function functionMapper(f, timezone) {
|
||||
switch (f) {
|
||||
case "date.today":
|
||||
return moment().tz(timezone).format(isoformat);
|
||||
case "date.now":
|
||||
return moment().tz(timezone);
|
||||
case "date.yesterday":
|
||||
return moment().tz(timezone).subtract(1, "day").format(isoformat);
|
||||
case "date.3daysago":
|
||||
return moment().tz(timezone).subtract(3, "days").format(isoformat);
|
||||
case "date.7daysago":
|
||||
return moment().tz(timezone).subtract(7, "days").format(isoformat);
|
||||
case "date.tomorrow":
|
||||
return moment().tz(timezone).add(1, "day").format(isoformat);
|
||||
case "date.3daysfromnow":
|
||||
return moment().tz(timezone).add(3, "days").format(isoformat);
|
||||
case "date.7daysfromnow":
|
||||
return moment().tz(timezone).add(7, "days").format(isoformat);
|
||||
case "date.yesterdaytz":
|
||||
return moment().tz(timezone).subtract(1, "day");
|
||||
case "date.3daysagotz":
|
||||
return moment().tz(timezone).subtract(3, "days");
|
||||
case "date.7daysagotz":
|
||||
return moment().tz(timezone).subtract(7, "days");
|
||||
case "date.tomorrowtz":
|
||||
return moment().tz(timezone).add(1, "day");
|
||||
case "date.3daysfromnowtz":
|
||||
return moment().tz(timezone).add(3, "days");
|
||||
case "date.7daysfromnowtz":
|
||||
return moment().tz(timezone).add(7, "days");
|
||||
|
||||
case "date.now":
|
||||
return moment().tz(timezone);
|
||||
default:
|
||||
return f;
|
||||
}
|
||||
}
|
||||
27
yarn.lock
27
yarn.lock
@@ -1172,6 +1172,11 @@ decode-uri-component@^0.2.0:
|
||||
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
|
||||
integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
|
||||
|
||||
deeks@2.5.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/deeks/-/deeks-2.5.1.tgz#c4e18a00cac3f8ff95a306463db8f533c879f436"
|
||||
integrity sha512-fqrBeUz7f1UqaXDRzVB5RG2EfPk15HJRrb2pMZj8mLlSTtz4tRPsK5leFOskoHFPuyZ6+7aRM9j657fvXLkJ7Q==
|
||||
|
||||
deep-is@^0.1.3, deep-is@~0.1.3:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
|
||||
@@ -1213,6 +1218,11 @@ dinero.js@^1.9.1:
|
||||
resolved "https://registry.yarnpkg.com/dinero.js/-/dinero.js-1.9.1.tgz#64b10ce7277a07805dac9c8cd6500e9b7d0aee96"
|
||||
integrity sha512-1HXiF2vv3ZeRQ23yr+9lFxj/PbZqutuYWJnE0qfCB9xYBPnuaJ8lXtli1cJM0TvUXW1JTOaePldmqN5JVNxKSA==
|
||||
|
||||
doc-path@3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/doc-path/-/doc-path-3.0.2.tgz#a7df9d58eadce28a2ddd4eda5aa392628fd75b6d"
|
||||
integrity sha512-VRlA2OKSjTbHWj6wmSanxJ338fE/YN8pqmZ0FIWK5JWkIJMFRc4KmD35JtOrnjvVG0WrzOtDDNHx1lN1tkb+lA==
|
||||
|
||||
doctrine@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
|
||||
@@ -2346,6 +2356,14 @@ jsbn@~0.1.0:
|
||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
|
||||
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
|
||||
|
||||
json-2-csv@^3.17.0:
|
||||
version "3.17.0"
|
||||
resolved "https://registry.yarnpkg.com/json-2-csv/-/json-2-csv-3.17.0.tgz#d886e4ec382bdb96dd31cbf87e0206173176d3c7"
|
||||
integrity sha512-mLnrYK3TX/mDSiBduA6vuSuVlvkYgDMv+Ef8HXCriyz73BBc6I7pWljdRfR+FKPzYKcnz/rUvzbqJQx/tbEoMQ==
|
||||
dependencies:
|
||||
deeks "2.5.1"
|
||||
doc-path "3.0.2"
|
||||
|
||||
json-bigint@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1"
|
||||
@@ -2740,7 +2758,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==
|
||||
|
||||
Reference in New Issue
Block a user