Compare commits
25 Commits
release/20
...
hotfix/202
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
634adb6e9a | ||
|
|
14e9ac2cdb | ||
|
|
76fb8f453d | ||
|
|
356928ce77 | ||
|
|
eb05a746c4 | ||
|
|
7ef8ef5f2f | ||
|
|
f41277c081 | ||
|
|
7f15e9ef7a | ||
|
|
6d3fc783d6 | ||
|
|
0052a54915 | ||
|
|
375856bd68 | ||
|
|
cf4f6f6e55 | ||
|
|
173e9a278c | ||
|
|
35ab86c5fd | ||
|
|
932c89f8f9 | ||
|
|
ddd5ce4bf0 | ||
|
|
1d0e526466 | ||
|
|
73e2b2d65d | ||
|
|
88bc8d4d05 | ||
|
|
53cecd68f0 | ||
|
|
4bce6d996e | ||
|
|
83b51384c7 | ||
|
|
09a0309108 | ||
|
|
b19120af3d | ||
|
|
f71a4b7c83 |
@@ -1,4 +1,4 @@
|
||||
<babeledit_project version="1.2" be_version="2.7.1">
|
||||
<babeledit_project be_version="2.7.1" version="1.2">
|
||||
<!--
|
||||
|
||||
BabelEdit project file
|
||||
@@ -40261,6 +40261,53 @@
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>validation</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>clockoffmustbeafterclockon</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>clockoffwithoutclockon</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>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
Space,
|
||||
Switch,
|
||||
Table,
|
||||
Tooltip,
|
||||
Tooltip
|
||||
} from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -334,6 +334,19 @@ export function BillEnterModalLinesComponent({
|
||||
additional: (record, index) => (
|
||||
<Form.Item shouldUpdate style={{ display: "inline-block" }}>
|
||||
{() => {
|
||||
const price = getFieldValue([
|
||||
"billlines",
|
||||
record.name,
|
||||
"actual_price",
|
||||
]);
|
||||
|
||||
const adjustmentRate = getFieldValue([
|
||||
"billlines",
|
||||
record.name,
|
||||
"lbr_adjustment",
|
||||
"rate",
|
||||
]);
|
||||
|
||||
if (getFieldValue(["billlines", record.name, "deductedfromlbr"]))
|
||||
return (
|
||||
<div>
|
||||
@@ -406,6 +419,9 @@ export function BillEnterModalLinesComponent({
|
||||
>
|
||||
<InputNumber precision={2} min={0.01} />
|
||||
</Form.Item>
|
||||
{price &&
|
||||
adjustmentRate &&
|
||||
`${(price / adjustmentRate).toFixed(1)} hrs`}
|
||||
</div>
|
||||
);
|
||||
return <></>;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -38,6 +38,7 @@ export default function ScheduleEventContainer({ bodyshop, event, refetch }) {
|
||||
job: {
|
||||
date_scheduled: null,
|
||||
scheduled_in: null,
|
||||
scheduled_completion:null,
|
||||
status: bodyshop.md_ro_statuses.default_imported,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -6,9 +6,11 @@ import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { setPartnerVersion } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
@@ -19,26 +21,35 @@ export default connect(
|
||||
mapDispatchToProps
|
||||
)(PartnerPingComponent);
|
||||
|
||||
export function PartnerPingComponent({ setPartnerVersion }) {
|
||||
export function PartnerPingComponent({ bodyshop, setPartnerVersion }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
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;
|
||||
|
||||
setPartnerVersion(appver);
|
||||
console.log({ appver, qbpath });
|
||||
if (!qbpath) {
|
||||
if (
|
||||
!qbpath &&
|
||||
!(
|
||||
bodyshop &&
|
||||
(bodyshop.cdk_dealerid ||
|
||||
bodyshop.pbs_serialnumber ||
|
||||
bodyshop.accountingconfig.qbo)
|
||||
)
|
||||
) {
|
||||
notification["error"]({
|
||||
title: "",
|
||||
message: t("general.messages.noacctfilepath"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
notification["error"]({
|
||||
title: "",
|
||||
message: t("general.messages.partnernotrunning"),
|
||||
@@ -48,7 +59,7 @@ export function PartnerPingComponent({ setPartnerVersion }) {
|
||||
// Execute the created function directly
|
||||
checkPartnerStatus();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
}, [bodyshop]);
|
||||
|
||||
return <></>;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import AlertComponent from "../alert/alert.component";
|
||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||
import PartsOrderModalComponent from "./parts-order-modal.component";
|
||||
import axios from "axios";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import _ from "lodash";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
@@ -57,6 +59,11 @@ export function PartsOrderModalContainer({
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const client = useApolloClient();
|
||||
const { OEConnection_PriceChange } = useTreatments(
|
||||
["OEConnection_PriceChange"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
const { visible, context, actions } = partsOrderModal;
|
||||
const {
|
||||
jobId,
|
||||
@@ -230,11 +237,22 @@ export function PartsOrderModalContainer({
|
||||
id: insertResult.data.insert_parts_orders.returning[0].id,
|
||||
},
|
||||
});
|
||||
let po;
|
||||
//Massage the data based on the split. Should they be able to overwrite OEC pricing?
|
||||
if (OEConnection_PriceChange.treatment === "on") {
|
||||
//Set the flag to include the override.
|
||||
po = _.cloneDeep(partsOrder.data.parts_orders_by_pk);
|
||||
po.parts_order_lines.forEach((pol) => {
|
||||
pol.priceChange = true;
|
||||
});
|
||||
}
|
||||
|
||||
console.log(partsOrder.data.parts_orders_by_pk);
|
||||
|
||||
const oecResponse = await axios.post(
|
||||
"http://localhost:1337/oec/",
|
||||
|
||||
partsOrder.data.parts_orders_by_pk,
|
||||
po || partsOrder.data.parts_orders_by_pk,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
||||
|
||||
@@ -208,7 +208,7 @@ export function TimeTicketModalComponent({
|
||||
>
|
||||
<InputNumber min={0} precision={1} />
|
||||
</Form.Item>
|
||||
{isEdit && (
|
||||
{
|
||||
<>
|
||||
<Form.Item label={t("timetickets.fields.clockon")} name="clockon">
|
||||
<FormDateTimePicker
|
||||
@@ -221,7 +221,29 @@ export function TimeTicketModalComponent({
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label={t("timetickets.fields.clockoff")} name="clockoff">
|
||||
<Form.Item
|
||||
label={t("timetickets.fields.clockoff")}
|
||||
name="clockoff"
|
||||
rules={[
|
||||
({ getFieldValue }) => ({
|
||||
validator(rule, value) {
|
||||
const clockon = getFieldValue("clockon");
|
||||
|
||||
if (!value) return Promise.resolve();
|
||||
if (!clockon && value)
|
||||
return Promise.reject(
|
||||
t("timetickets.validation.clockoffwithoutclockon")
|
||||
);
|
||||
if (!value.isSameOrAfter(clockon))
|
||||
return Promise.reject(
|
||||
t("timetickets.validation.clockoffmustbeafterclockon")
|
||||
);
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<FormDateTimePicker
|
||||
disabled={
|
||||
!HasRbacAccess({
|
||||
@@ -233,7 +255,7 @@ export function TimeTicketModalComponent({
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
}
|
||||
|
||||
<Form.Item label={t("timetickets.fields.memo")} name="memo">
|
||||
<MemoInput />
|
||||
|
||||
@@ -2230,7 +2230,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",
|
||||
@@ -2406,6 +2406,10 @@
|
||||
"clockedout": "Clocked out successfully.",
|
||||
"created": "Time ticket entered successfully.",
|
||||
"deleted": "Time ticket deleted successfully."
|
||||
},
|
||||
"validation": {
|
||||
"clockoffmustbeafterclockon": "Clock off time must be the same or after clock in time.",
|
||||
"clockoffwithoutclockon": "Clock off time cannot be set without a clock in time."
|
||||
}
|
||||
},
|
||||
"titles": {
|
||||
|
||||
@@ -2406,6 +2406,10 @@
|
||||
"clockedout": "",
|
||||
"created": "",
|
||||
"deleted": ""
|
||||
},
|
||||
"validation": {
|
||||
"clockoffmustbeafterclockon": "",
|
||||
"clockoffwithoutclockon": ""
|
||||
}
|
||||
},
|
||||
"titles": {
|
||||
|
||||
@@ -2406,6 +2406,10 @@
|
||||
"clockedout": "",
|
||||
"created": "",
|
||||
"deleted": ""
|
||||
},
|
||||
"validation": {
|
||||
"clockoffmustbeafterclockon": "",
|
||||
"clockoffwithoutclockon": ""
|
||||
}
|
||||
},
|
||||
"titles": {
|
||||
|
||||
@@ -815,6 +815,7 @@
|
||||
- email
|
||||
- enforce_class
|
||||
- enforce_referral
|
||||
- entegral_id
|
||||
- features
|
||||
- federal_tax_id
|
||||
- id
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "appointments_bodyshopid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "appointments_bodyshopid" on
|
||||
"public"."appointments" using btree ("bodyshopid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "appointments_jobid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "appointments_jobid" on
|
||||
"public"."appointments" using btree ("jobid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "appointments_start";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "appointments_start" on
|
||||
"public"."appointments" using btree ("start");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "appointments_end";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "appointments_end" on
|
||||
"public"."appointments" using btree ("end");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "associations_bodyshopid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "associations_bodyshopid" on
|
||||
"public"."associations" using btree ("shopid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "associations_useremail";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "associations_useremail" on
|
||||
"public"."associations" using btree ("useremail");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "invoicelines_billid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "invoicelines_billid" on
|
||||
"public"."billlines" using btree ("billid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "invoicelines_jobid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "invoicelines_jobid" on
|
||||
"public"."billlines" using btree ("joblineid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "bills_jobid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "bills_jobid" on
|
||||
"public"."bills" using btree ("jobid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "conversations_shopid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "conversations_shopid" on
|
||||
"public"."conversations" using btree ("bodyshopid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "documents_shopid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "documents_shopid" on
|
||||
"public"."documents" using btree ("bodyshopid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "documents_jobid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "documents_jobid" on
|
||||
"public"."documents" using btree ("jobid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "documents_billid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "documents_billid" on
|
||||
"public"."documents" using btree ("billid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "jobline_jobid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "jobline_jobid" on
|
||||
"public"."joblines" using btree ("jobid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "jobs_updatedat";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "jobs_updatedat" on
|
||||
"public"."jobs" using btree ("updated_at");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "jobs_bodyshopid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "jobs_bodyshopid" on
|
||||
"public"."jobs" using btree ("shopid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "messages_conversationid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "messages_conversationid" on
|
||||
"public"."messages" using btree ("conversationid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "timetickets_jobid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "timetickets_jobid" on
|
||||
"public"."timetickets" using btree ("jobid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "parts_orders_jobid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "parts_orders_jobid" on
|
||||
"public"."parts_orders" using btree ("jobid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "parts_order_lines_poid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "parts_order_lines_poid" on
|
||||
"public"."parts_order_lines" using btree ("orderid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "parts_order_lines_joblineid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "parts_order_lines_joblineid" on
|
||||
"public"."parts_order_lines" using btree ("job_line_id");
|
||||
@@ -9,6 +9,7 @@ exports.default = function ({
|
||||
qbo = false,
|
||||
items,
|
||||
taxCodes,
|
||||
classes,
|
||||
}) {
|
||||
const InvoiceLineAdd = [];
|
||||
const responsibilityCenters = bodyshop.md_responsibility_centers;
|
||||
@@ -95,6 +96,9 @@ exports.default = function ({
|
||||
DetailType: "SalesItemLineDetail",
|
||||
Amount: DineroAmount,
|
||||
SalesItemLineDetail: {
|
||||
...(jobs_by_pk.class
|
||||
? { ClassRef: { value: classes[jobs_by_pk.class] } }
|
||||
: {}),
|
||||
ItemRef: {
|
||||
value: items[account.accountitem],
|
||||
},
|
||||
@@ -161,6 +165,9 @@ exports.default = function ({
|
||||
DetailType: "SalesItemLineDetail",
|
||||
Amount: DineroAmount,
|
||||
SalesItemLineDetail: {
|
||||
...(jobs_by_pk.class
|
||||
? { ClassRef: { value: classes[jobs_by_pk.class] } }
|
||||
: {}),
|
||||
ItemRef: {
|
||||
value: items[account.accountitem],
|
||||
},
|
||||
@@ -227,6 +234,9 @@ exports.default = function ({
|
||||
ItemRef: {
|
||||
value: items[mapaAccount.accountitem],
|
||||
},
|
||||
...(jobs_by_pk.class
|
||||
? { ClassRef: { value: classes[jobs_by_pk.class] } }
|
||||
: {}),
|
||||
TaxCodeRef: {
|
||||
value: QboTaxId,
|
||||
},
|
||||
@@ -290,6 +300,9 @@ exports.default = function ({
|
||||
ItemRef: {
|
||||
value: items[mashAccount.accountitem],
|
||||
},
|
||||
...(jobs_by_pk.class
|
||||
? { ClassRef: { value: classes[jobs_by_pk.class] } }
|
||||
: {}),
|
||||
TaxCodeRef: {
|
||||
value: QboTaxId,
|
||||
},
|
||||
@@ -367,6 +380,9 @@ exports.default = function ({
|
||||
amount: Math.round((jobs_by_pk.towing_payable || 0) * 100),
|
||||
}).toFormat(DineroQbFormat),
|
||||
SalesItemLineDetail: {
|
||||
...(jobs_by_pk.class
|
||||
? { ClassRef: { value: classes[jobs_by_pk.class] } }
|
||||
: {}),
|
||||
ItemRef: {
|
||||
value: items[account.accountitem],
|
||||
},
|
||||
@@ -415,6 +431,9 @@ exports.default = function ({
|
||||
amount: Math.round((jobs_by_pk.storage_payable || 0) * 100),
|
||||
}).toFormat(DineroQbFormat),
|
||||
SalesItemLineDetail: {
|
||||
...(jobs_by_pk.class
|
||||
? { ClassRef: { value: classes[jobs_by_pk.class] } }
|
||||
: {}),
|
||||
ItemRef: {
|
||||
value: items[account.accountitem],
|
||||
},
|
||||
@@ -466,6 +485,9 @@ exports.default = function ({
|
||||
amount: Math.round((jobs_by_pk.adjustment_bottom_line || 0) * 100),
|
||||
}).toFormat(DineroQbFormat),
|
||||
SalesItemLineDetail: {
|
||||
...(jobs_by_pk.class
|
||||
? { ClassRef: { value: classes[jobs_by_pk.class] } }
|
||||
: {}),
|
||||
ItemRef: {
|
||||
value: items[account.accountitem],
|
||||
},
|
||||
@@ -554,6 +576,9 @@ exports.default = function ({
|
||||
DineroQbFormat
|
||||
),
|
||||
SalesItemLineDetail: {
|
||||
...(jobs_by_pk.class
|
||||
? { ClassRef: { value: classes[jobs_by_pk.class] } }
|
||||
: {}),
|
||||
ItemRef: {
|
||||
value: items["PVRT"],
|
||||
},
|
||||
|
||||
@@ -181,11 +181,9 @@ async function InsertBill(oauthClient, qbo_realmId, req, bill, vendor) {
|
||||
TxnDate: moment(bill.date).format("YYYY-MM-DD"),
|
||||
//DueDate: bill.due_date && moment(bill.due_date).format("YYYY-MM-DD"),
|
||||
DocNumber: bill.invoice_number,
|
||||
...(bill.job.class ? { ClassRef: { Id: classes[bill.job.class] } } : {}),
|
||||
//...(bill.job.class ? { ClassRef: { Id: classes[bill.job.class] } } : {}),
|
||||
|
||||
PrivateNote: `RO ${bill.job.ro_number || ""} OWNER ${
|
||||
bill.job.ownr_fn || ""
|
||||
} ${bill.job.ownr_ln || ""} ${bill.job.ownr_co_nm || ""}`,
|
||||
PrivateNote: `RO ${bill.job.ro_number || ""}`,
|
||||
Line: bill.billlines.map((il) =>
|
||||
generateBillLine(
|
||||
il,
|
||||
@@ -249,12 +247,11 @@ const generateBillLine = (
|
||||
) => {
|
||||
const account = costCenters.find((c) => c.name === billLine.cost_center);
|
||||
|
||||
console.log(account.accountname, accounts[account.accountname]);
|
||||
return {
|
||||
DetailType: "AccountBasedExpenseLineDetail",
|
||||
|
||||
AccountBasedExpenseLineDetail: {
|
||||
...(jobClass ? { ClassRef: { Id: classes[jobClass] } } : {}),
|
||||
...(jobClass ? { ClassRef: { value: classes[jobClass] } } : {}),
|
||||
TaxCodeRef: {
|
||||
value:
|
||||
taxCodes[findTaxCode(billLine.applicable_taxes, ioSalesTaxCodes)],
|
||||
@@ -324,7 +321,7 @@ async function QueryMetaData(oauthClient, qbo_realmId, req) {
|
||||
classes.json.QueryResponse &&
|
||||
classes.json.QueryResponse.Class &&
|
||||
classes.json.QueryResponse.Class.forEach((t) => {
|
||||
accountMapping[t.Name] = t.Id;
|
||||
classMapping[t.Name] = t.Id;
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -356,7 +356,11 @@ async function InsertJob(oauthClient, qbo_realmId, req, job, parentTierRef) {
|
||||
exports.InsertJob = InsertJob;
|
||||
async function QueryMetaData(oauthClient, qbo_realmId, req) {
|
||||
const items = await oauthClient.makeApiCall({
|
||||
url: urlBuilder(qbo_realmId, "query", `select * From Item`),
|
||||
url: urlBuilder(
|
||||
qbo_realmId,
|
||||
"query",
|
||||
`select * From Item where active=true maxresults 1000`
|
||||
),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -402,7 +406,7 @@ async function QueryMetaData(oauthClient, qbo_realmId, req) {
|
||||
classes.json.QueryResponse &&
|
||||
classes.json.QueryResponse.Class &&
|
||||
classes.json.QueryResponse.Class.forEach((t) => {
|
||||
itemMapping[t.Name] = t.Id;
|
||||
classMapping[t.Name] = t.Id;
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -431,13 +435,21 @@ async function InsertInvoice(
|
||||
qbo: true,
|
||||
items,
|
||||
taxCodes,
|
||||
classes,
|
||||
});
|
||||
|
||||
const invoiceObj = {
|
||||
Line: InvoiceLineAdd,
|
||||
TxnDate: moment(job.date_invoiced).format("YYYY-MM-DD"),
|
||||
DocNumber: job.ro_number,
|
||||
...(job.class ? { ClassRef: { Id: classes[job.class] } } : {}),
|
||||
...(job.class ? { ClassRef: { value: classes[job.class] } } : {}),
|
||||
CustomerMemo: {
|
||||
value: `${job.clm_no ? `Claim No: ${job.clm_no}` : ``}${
|
||||
job.po_number ? `PO No: ${job.po_number}` : ``
|
||||
} Vehicle:${job.v_model_yr || ""} ${job.v_make_desc || ""} ${
|
||||
job.v_model_desc || ""
|
||||
} ${job.v_vin || ""} ${job.plate_no || ""} `.trim(),
|
||||
},
|
||||
CustomerRef: {
|
||||
value: parentTierRef.Id,
|
||||
},
|
||||
@@ -447,6 +459,15 @@ async function InsertInvoice(
|
||||
...(bodyshop.accountingconfig.emaillater && job.ownr_ea
|
||||
? { EmailStatus: "NeedToSend" }
|
||||
: {}),
|
||||
BillAddr: {
|
||||
Line3: `${job.ownr_city || ""}, ${job.ownr_st || ""} ${
|
||||
job.ownr_zip || ""
|
||||
}`.trim(),
|
||||
Line2: job.ownr_addr1 || "",
|
||||
Line1: `${job.ownr_fn || ""} ${job.ownr_ln || ""} ${
|
||||
job.ownr_co_nm || ""
|
||||
}`,
|
||||
},
|
||||
};
|
||||
|
||||
logger.log("qbo-receivable-objectlog", "DEBUG", req.user.email, job.id, {
|
||||
|
||||
@@ -566,6 +566,13 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
|
||||
status
|
||||
est_ct_fn
|
||||
est_ct_ln
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_co_nm
|
||||
ownr_addr1
|
||||
ownr_city
|
||||
ownr_st
|
||||
ownr_ph1
|
||||
ownr_zip
|
||||
referral_source
|
||||
loss_type
|
||||
|
||||
@@ -667,7 +667,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"];
|
||||
|
||||
Reference in New Issue
Block a user