Merged in hotfix/2020-05-28 (pull request #72)
Hotfix/2020 05 28 deployment to Test.
This commit is contained in:
@@ -3339,6 +3339,53 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>jc_hourly_rates</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>mapa</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>mash</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>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>lastnumberworkingdays</name>
|
<name>lastnumberworkingdays</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -10757,6 +10804,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>updating</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>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
@@ -12947,6 +13015,48 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>nointernet</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>nointernet_sub</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>
|
<concept_node>
|
||||||
<name>none</name>
|
<name>none</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -22179,6 +22289,27 @@
|
|||||||
</concept_node>
|
</concept_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>prt_dsmk_total</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>
|
<concept_node>
|
||||||
<name>rates</name>
|
<name>rates</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -27859,6 +27990,48 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>labels</name>
|
<name>labels</name>
|
||||||
<children>
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>noneselected</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>onenamerequired</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>
|
<concept_node>
|
||||||
<name>vendorcategory</name>
|
<name>vendorcategory</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -30292,6 +30465,32 @@
|
|||||||
</concept_node>
|
</concept_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>successes</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>updated</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>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
|
|||||||
|
|
||||||
export default function AppContainer() {
|
export default function AppContainer() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ApolloProvider client={client}>
|
<ApolloProvider client={client}>
|
||||||
<ConfigProvider
|
<ConfigProvider
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Button, Result } from "antd";
|
||||||
import React, { lazy, Suspense, useEffect } from "react";
|
import React, { lazy, Suspense, useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -8,6 +9,8 @@ import ErrorBoundary from "../components/error-boundary/error-boundary.component
|
|||||||
import LoadingSpinner from "../components/loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../components/loading-spinner/loading-spinner.component";
|
||||||
import AboutPage from "../pages/about/about.page";
|
import AboutPage from "../pages/about/about.page";
|
||||||
import TechPageContainer from "../pages/tech/tech.page.container";
|
import TechPageContainer from "../pages/tech/tech.page.container";
|
||||||
|
import { setOnline } from "../redux/application/application.actions";
|
||||||
|
import { selectOnline } from "../redux/application/application.selectors";
|
||||||
import { checkUserSession } from "../redux/user/user.actions";
|
import { checkUserSession } from "../redux/user/user.actions";
|
||||||
import { selectCurrentUser } from "../redux/user/user.selectors";
|
import { selectCurrentUser } from "../redux/user/user.selectors";
|
||||||
import PrivateRoute from "../utils/private-route";
|
import PrivateRoute from "../utils/private-route";
|
||||||
@@ -27,12 +30,14 @@ const MobilePaymentContainer = lazy(() =>
|
|||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
|
online: selectOnline,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
checkUserSession: () => dispatch(checkUserSession()),
|
checkUserSession: () => dispatch(checkUserSession()),
|
||||||
|
setOnline: (isOnline) => dispatch(setOnline(isOnline)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function App({ checkUserSession, currentUser }) {
|
export function App({ checkUserSession, currentUser, online, setOnline }) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
checkUserSession();
|
checkUserSession();
|
||||||
}, [checkUserSession]);
|
}, [checkUserSession]);
|
||||||
@@ -42,10 +47,38 @@ export function App({ checkUserSession, currentUser }) {
|
|||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
window.addEventListener("offline", function (e) {
|
||||||
|
console.log("Internet connection lost.");
|
||||||
|
setOnline(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("online", function (e) {
|
||||||
|
setOnline(true);
|
||||||
|
});
|
||||||
|
|
||||||
if (currentUser.authorized === null) {
|
if (currentUser.authorized === null) {
|
||||||
return <LoadingSpinner message={t("general.labels.loggingin")} />;
|
return <LoadingSpinner message={t("general.labels.loggingin")} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!online)
|
||||||
|
return (
|
||||||
|
<Result
|
||||||
|
status="warning"
|
||||||
|
title={t("general.labels.nointernet")}
|
||||||
|
subTitle={t("general.labels.nointernet_sub")}
|
||||||
|
extra={
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
window.location.reload();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("general.actions.refresh")}
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Suspense fallback={<LoadingSpinner message="ImEX Online" />}>
|
<Suspense fallback={<LoadingSpinner message="ImEX Online" />}>
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ export const uploadToCloudinary = async (
|
|||||||
let takenat;
|
let takenat;
|
||||||
if (fileType.includes("image")) {
|
if (fileType.includes("image")) {
|
||||||
const exif = await exifr.parse(file);
|
const exif = await exifr.parse(file);
|
||||||
console.log(`exif`, exif);
|
|
||||||
takenat = exif && exif.DateTimeOriginal;
|
takenat = exif && exif.DateTimeOriginal;
|
||||||
}
|
}
|
||||||
const documentInsert = await client.mutate({
|
const documentInsert = await client.mutate({
|
||||||
@@ -141,7 +141,7 @@ export const uploadToCloudinary = async (
|
|||||||
uploaded_by: uploaded_by,
|
uploaded_by: uploaded_by,
|
||||||
key: key,
|
key: key,
|
||||||
type: fileType,
|
type: fileType,
|
||||||
extension: extension,
|
extension: cloudinaryUploadResponse.data.format || extension,
|
||||||
bodyshopid: bodyshop.id,
|
bodyshopid: bodyshop.id,
|
||||||
size: cloudinaryUploadResponse.data.bytes || file.size,
|
size: cloudinaryUploadResponse.data.bytes || file.size,
|
||||||
takenat,
|
takenat,
|
||||||
@@ -176,6 +176,7 @@ export const uploadToCloudinary = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Also needs to be updated in media JS and mobile app.
|
||||||
export function DetermineFileType(filetype) {
|
export function DetermineFileType(filetype) {
|
||||||
if (!filetype) return "auto";
|
if (!filetype) return "auto";
|
||||||
else if (filetype.startsWith("image")) return "image";
|
else if (filetype.startsWith("image")) return "image";
|
||||||
|
|||||||
@@ -155,7 +155,14 @@ export function JobLinesComponent({
|
|||||||
state.sortedInfo.columnKey === "act_price" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "act_price" && state.sortedInfo.order,
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<CurrencyFormatter>{record.act_price}</CurrencyFormatter>
|
<>
|
||||||
|
<CurrencyFormatter>{record.act_price}</CurrencyFormatter>
|
||||||
|
{record.prt_dsmk_p !== 0 && (
|
||||||
|
<span
|
||||||
|
style={{ marginLeft: ".2rem" }}
|
||||||
|
>{`(${record.prt_dsmk_p}%)`}</span>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -69,17 +69,28 @@ export default function JobTotalsTableParts({ job }) {
|
|||||||
x: true,
|
x: true,
|
||||||
}}
|
}}
|
||||||
summary={() => (
|
summary={() => (
|
||||||
<Table.Summary.Row>
|
<>
|
||||||
<Table.Summary.Cell>
|
<Table.Summary.Row>
|
||||||
<strong>{t("jobs.labels.partstotal")}</strong>
|
<Table.Summary.Cell>
|
||||||
</Table.Summary.Cell>
|
{t("jobs.labels.prt_dsmk_total")}
|
||||||
|
</Table.Summary.Cell>
|
||||||
|
|
||||||
<Table.Summary.Cell align="right">
|
<Table.Summary.Cell align="right">
|
||||||
<strong>
|
{Dinero(job.job_totals.parts.parts.prt_dsmk_total).toFormat()}
|
||||||
{Dinero(job.job_totals.parts.parts.total).toFormat()}
|
</Table.Summary.Cell>
|
||||||
</strong>
|
</Table.Summary.Row>
|
||||||
</Table.Summary.Cell>
|
<Table.Summary.Row>
|
||||||
</Table.Summary.Row>
|
<Table.Summary.Cell>
|
||||||
|
<strong>{t("jobs.labels.partstotal")}</strong>
|
||||||
|
</Table.Summary.Cell>
|
||||||
|
|
||||||
|
<Table.Summary.Cell align="right">
|
||||||
|
<strong>
|
||||||
|
{Dinero(job.job_totals.parts.parts.total).toFormat()}
|
||||||
|
</strong>
|
||||||
|
</Table.Summary.Cell>
|
||||||
|
</Table.Summary.Row>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ mutation UNVOID_JOB($jobId: uuid!) {
|
|||||||
}
|
}
|
||||||
insert_notes(objects: {jobid: $jobId, audit: true, created_by: "${
|
insert_notes(objects: {jobid: $jobId, audit: true, created_by: "${
|
||||||
currentUser.email
|
currentUser.email
|
||||||
}", text: "${t("jobs.labels.unvoidnote", { email: currentUser.email })}"}) {
|
}", text: "${t("jobs.labels.unvoidnote")}"}) {
|
||||||
returning {
|
returning {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,10 +124,7 @@ export function JobsAvailableContainer({ bodyshop, currentUser }) {
|
|||||||
data: {
|
data: {
|
||||||
created_by: currentUser.email,
|
created_by: currentUser.email,
|
||||||
audit: true,
|
audit: true,
|
||||||
text: t("jobs.labels.importnote", {
|
text: t("jobs.labels.importnote"),
|
||||||
date: moment().format("MM/DD/yyy"),
|
|
||||||
time: moment().format("hh:mm a"),
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
queued_for_parts: true,
|
queued_for_parts: true,
|
||||||
@@ -278,10 +275,7 @@ export function JobsAvailableContainer({ bodyshop, currentUser }) {
|
|||||||
jobid: selectedJob,
|
jobid: selectedJob,
|
||||||
created_by: currentUser.email,
|
created_by: currentUser.email,
|
||||||
audit: true,
|
audit: true,
|
||||||
text: t("jobs.labels.supplementnote", {
|
text: t("jobs.labels.supplementnote"),
|
||||||
date: moment().format("MM/DD/yyy"),
|
|
||||||
time: moment().format("hh:mm a"),
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { DownCircleFilled } from "@ant-design/icons";
|
import { DownCircleFilled } from "@ant-design/icons";
|
||||||
import { useApolloClient, useMutation } from "@apollo/client";
|
import { useApolloClient, useMutation } from "@apollo/client";
|
||||||
import { Button, Dropdown, Menu, notification, Popconfirm } from "antd";
|
import { Button, Dropdown, Menu, notification, Popconfirm } from "antd";
|
||||||
import moment from "moment";
|
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -385,10 +384,7 @@ export function JobsDetailHeaderActions({
|
|||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
created_by: currentUser.email,
|
created_by: currentUser.email,
|
||||||
audit: true,
|
audit: true,
|
||||||
text: t("jobs.labels.voidnote", {
|
text: t("jobs.labels.voidnote"),
|
||||||
date: moment().format("MM/DD/yyy"),
|
|
||||||
time: moment().format("hh:mm a"),
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -151,17 +151,21 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
|||||||
<Card
|
<Card
|
||||||
style={{ height: "100%" }}
|
style={{ height: "100%" }}
|
||||||
title={
|
title={
|
||||||
<Link
|
job.vehicle ? (
|
||||||
to={
|
<Link
|
||||||
disabled
|
to={
|
||||||
? "#"
|
disabled
|
||||||
: job.vehicle && `/manage/vehicles/${job.vehicle.id}`
|
? "#"
|
||||||
}
|
: job.vehicle && `/manage/vehicles/${job.vehicle.id}`
|
||||||
>
|
}
|
||||||
{`${job.v_model_yr || ""} ${job.v_color || ""}
|
>
|
||||||
|
{`${job.v_model_yr || ""} ${job.v_color || ""}
|
||||||
${job.v_make_desc || ""}
|
${job.v_make_desc || ""}
|
||||||
${job.v_model_desc || ""}`}
|
${job.v_model_desc || ""}`}
|
||||||
</Link>
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span></span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -31,7 +31,9 @@ function JobsDocumentsComponent({
|
|||||||
acc.images.push({
|
acc.images.push({
|
||||||
src: `${
|
src: `${
|
||||||
process.env.REACT_APP_CLOUDINARY_ENDPOINT
|
process.env.REACT_APP_CLOUDINARY_ENDPOINT
|
||||||
}/${DetermineFileType(value.type)}/upload/${value.key}`,
|
}/${DetermineFileType(value.type)}/upload/${value.key}${
|
||||||
|
value.extension ? `.${value.extension}` : ""
|
||||||
|
}`,
|
||||||
thumbnail: `${
|
thumbnail: `${
|
||||||
process.env.REACT_APP_CLOUDINARY_ENDPOINT
|
process.env.REACT_APP_CLOUDINARY_ENDPOINT
|
||||||
}/${DetermineFileType(value.type)}/upload/${
|
}/${DetermineFileType(value.type)}/upload/${
|
||||||
@@ -51,13 +53,17 @@ function JobsDocumentsComponent({
|
|||||||
let thumb;
|
let thumb;
|
||||||
switch (fileType) {
|
switch (fileType) {
|
||||||
case "video":
|
case "video":
|
||||||
thumb = `${process.env.REACT_APP_CLOUDINARY_ENDPOINT}/${fileType}/upload/c_fill,f_png,h_250,w_250/${value.key}`;
|
thumb = `${process.env.REACT_APP_CLOUDINARY_ENDPOINT}/${fileType}/upload/${process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS}/${value.key}`;
|
||||||
break;
|
break;
|
||||||
case "raw":
|
case "raw":
|
||||||
thumb = `${window.location.origin}/file.png`;
|
thumb = `${window.location.origin}/file.png`;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
thumb = `${process.env.REACT_APP_CLOUDINARY_ENDPOINT}/${fileType}/upload/${process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS}/${value.key}`;
|
thumb = `${
|
||||||
|
process.env.REACT_APP_CLOUDINARY_ENDPOINT
|
||||||
|
}/${fileType}/upload/${
|
||||||
|
process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS
|
||||||
|
}/${value.key}${value.extension ? `.${value.extension}` : ""}`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,9 +71,9 @@ function JobsDocumentsComponent({
|
|||||||
acc.other.push({
|
acc.other.push({
|
||||||
src: `${
|
src: `${
|
||||||
process.env.REACT_APP_CLOUDINARY_ENDPOINT
|
process.env.REACT_APP_CLOUDINARY_ENDPOINT
|
||||||
}/${fileType}/upload/${fileType === "video" ? "q_auto/" : ""}${
|
}/${fileType}/upload/${value.key}${
|
||||||
value.key
|
value.extension ? `.${value.extension}` : ""
|
||||||
}${fileType === "raw" ? `.${value.extension}` : ""}`,
|
}`,
|
||||||
thumbnail: thumb,
|
thumbnail: thumb,
|
||||||
tags: [
|
tags: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -82,7 +82,31 @@ export function PhonebookFormComponent({
|
|||||||
<Form.Item label={t("phonebook.fields.lastname")} name="lastname">
|
<Form.Item label={t("phonebook.fields.lastname")} name="lastname">
|
||||||
<Input disabled={hasNoAccess} />
|
<Input disabled={hasNoAccess} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("phonebook.fields.company")} name="company">
|
<Form.Item
|
||||||
|
label={t("phonebook.fields.company")}
|
||||||
|
name="company"
|
||||||
|
dependencies={["firstname", "lastname"]}
|
||||||
|
rules={[
|
||||||
|
({ getFieldsValue }) => ({
|
||||||
|
validator(rule, value) {
|
||||||
|
const { firstname, lastname, company } = getFieldsValue([
|
||||||
|
"firstname",
|
||||||
|
"lastname",
|
||||||
|
"company",
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (
|
||||||
|
(firstname && firstname.trim() !== "") ||
|
||||||
|
(lastname && lastname.trim() !== "") ||
|
||||||
|
(company && company.trim() !== "")
|
||||||
|
) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return Promise.reject(t("phonebook.labels.onenamerequired"));
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
>
|
||||||
<Input disabled={hasNoAccess} />
|
<Input disabled={hasNoAccess} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import PhoneFormItem, {
|
|||||||
} from "../form-items-formatted/phone-form-item.component";
|
} from "../form-items-formatted/phone-form-item.component";
|
||||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
|
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||||
export default function ShopInfoGeneral({ form }) {
|
export default function ShopInfoGeneral({ form }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
@@ -425,6 +425,18 @@ export default function ShopInfoGeneral({ form }) {
|
|||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} />
|
<InputNumber min={0} max={1} precision={2} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.jc_hourly_rates.mapa")}
|
||||||
|
name={["jc_hourly_rates", "mapa"]}
|
||||||
|
>
|
||||||
|
<CurrencyInput />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.jc_hourly_rates.mash")}
|
||||||
|
name={["jc_hourly_rates", "mash"]}
|
||||||
|
>
|
||||||
|
<CurrencyInput />
|
||||||
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}>
|
<LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}>
|
||||||
<Form.List name={["md_messaging_presets"]}>
|
<Form.List name={["md_messaging_presets"]}>
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ export const QUERY_BODYSHOP = gql`
|
|||||||
md_ccc_rates
|
md_ccc_rates
|
||||||
enforce_referral
|
enforce_referral
|
||||||
website
|
website
|
||||||
|
jc_hourly_rates
|
||||||
employees {
|
employees {
|
||||||
id
|
id
|
||||||
active
|
active
|
||||||
@@ -171,6 +172,7 @@ export const UPDATE_SHOP = gql`
|
|||||||
md_ccc_rates
|
md_ccc_rates
|
||||||
enforce_referral
|
enforce_referral
|
||||||
website
|
website
|
||||||
|
jc_hourly_rates
|
||||||
employees {
|
employees {
|
||||||
id
|
id
|
||||||
first_name
|
first_name
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export const GET_DOCUMENTS_BY_JOB = gql`
|
|||||||
type
|
type
|
||||||
size
|
size
|
||||||
takenat
|
takenat
|
||||||
|
extension
|
||||||
bill {
|
bill {
|
||||||
id
|
id
|
||||||
invoice_number
|
invoice_number
|
||||||
|
|||||||
@@ -528,6 +528,7 @@ export const GET_JOB_BY_PK = gql`
|
|||||||
tax_part
|
tax_part
|
||||||
db_ref
|
db_ref
|
||||||
manual_line
|
manual_line
|
||||||
|
prt_dsmk_p
|
||||||
billlines(limit: 1, order_by: { bill: { date: desc } }) {
|
billlines(limit: 1, order_by: { bill: { date: desc } }) {
|
||||||
id
|
id
|
||||||
quantity
|
quantity
|
||||||
|
|||||||
@@ -385,7 +385,7 @@ export function Manage({ match, conflict, bodyshop }) {
|
|||||||
<Footer>
|
<Footer>
|
||||||
<div style={{ textAlign: "center", margin: "1rem 0rem" }}>
|
<div style={{ textAlign: "center", margin: "1rem 0rem" }}>
|
||||||
<div>
|
<div>
|
||||||
{`ImEX Online V.${process.env.NODE_ENV} - ${
|
{`ImEX Online ${
|
||||||
process.env.REACT_APP_GIT_SHA
|
process.env.REACT_APP_GIT_SHA
|
||||||
} - ${preval`module.exports = new Date().toLocaleString("en-US", {timeZone: "America/Los_Angeles"});`}`}
|
} - ${preval`module.exports = new Date().toLocaleString("en-US", {timeZone: "America/Los_Angeles"});`}`}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -48,3 +48,8 @@ export const setPartnerVersion = (version) => ({
|
|||||||
type: ApplicationActionTypes.SET_PARTNER_VERSION,
|
type: ApplicationActionTypes.SET_PARTNER_VERSION,
|
||||||
payload: version,
|
payload: version,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setOnline = (isOnline) => ({
|
||||||
|
type: ApplicationActionTypes.SET_ONLINE_STATUS,
|
||||||
|
payload: isOnline,
|
||||||
|
});
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import ApplicationActionTypes from "./application.types";
|
|||||||
|
|
||||||
const INITIAL_STATE = {
|
const INITIAL_STATE = {
|
||||||
loading: false,
|
loading: false,
|
||||||
|
online: true,
|
||||||
breadcrumbs: [],
|
breadcrumbs: [],
|
||||||
recentItems: [],
|
recentItems: [],
|
||||||
selectedHeader: "home",
|
selectedHeader: "home",
|
||||||
@@ -21,6 +22,11 @@ const applicationReducer = (state = INITIAL_STATE, action) => {
|
|||||||
...state,
|
...state,
|
||||||
selectedHeader: action.payload,
|
selectedHeader: action.payload,
|
||||||
};
|
};
|
||||||
|
case ApplicationActionTypes.SET_ONLINE_STATUS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
online: action.payload,
|
||||||
|
};
|
||||||
case ApplicationActionTypes.ADD_RECENT_ITEM:
|
case ApplicationActionTypes.ADD_RECENT_ITEM:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|||||||
@@ -40,3 +40,7 @@ export const selectJobReadOnly = createSelector(
|
|||||||
[selectApplication],
|
[selectApplication],
|
||||||
(application) => application.jobReadOnly
|
(application) => application.jobReadOnly
|
||||||
);
|
);
|
||||||
|
export const selectOnline = createSelector(
|
||||||
|
[selectApplication],
|
||||||
|
(application) => application.online
|
||||||
|
);
|
||||||
|
|||||||
@@ -9,5 +9,6 @@ const ApplicationActionTypes = {
|
|||||||
SET_SELECTED_HEADER: "SET_SELECTED_HEADER",
|
SET_SELECTED_HEADER: "SET_SELECTED_HEADER",
|
||||||
SET_JOB_READONLY: "SET_JOB_READONLY",
|
SET_JOB_READONLY: "SET_JOB_READONLY",
|
||||||
SET_PARTNER_VERSION: "SET_PARTNER_VERSION",
|
SET_PARTNER_VERSION: "SET_PARTNER_VERSION",
|
||||||
|
SET_ONLINE_STATUS: "SET_ONLINE_STATUS",
|
||||||
};
|
};
|
||||||
export default ApplicationActionTypes;
|
export default ApplicationActionTypes;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import Fingerprint2 from "@fingerprintjs/fingerprintjs";
|
import Fingerprint2 from "@fingerprintjs/fingerprintjs";
|
||||||
|
import { notification } from "antd";
|
||||||
|
import i18next from "i18next";
|
||||||
import LogRocket from "logrocket";
|
import LogRocket from "logrocket";
|
||||||
import { all, call, delay, put, select, takeLatest } from "redux-saga/effects";
|
import { all, call, delay, put, select, takeLatest } from "redux-saga/effects";
|
||||||
import {
|
import {
|
||||||
@@ -100,6 +102,10 @@ export function* updateUserDetails(userDetails) {
|
|||||||
try {
|
try {
|
||||||
yield updateCurrentUser(userDetails.payload);
|
yield updateCurrentUser(userDetails.payload);
|
||||||
yield put(updateUserDetailsSuccess(userDetails.payload));
|
yield put(updateUserDetailsSuccess(userDetails.payload));
|
||||||
|
notification.open({
|
||||||
|
type: "success",
|
||||||
|
message: i18next.t("profile.successes.updated"),
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
//yield put(signOutFailure(error.message));
|
//yield put(signOutFailure(error.message));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,6 +218,10 @@
|
|||||||
"invoice_federal_tax_rate": "Invoices - Federal Tax Rate",
|
"invoice_federal_tax_rate": "Invoices - Federal Tax Rate",
|
||||||
"invoice_local_tax_rate": "Invoices - Local Tax Rate",
|
"invoice_local_tax_rate": "Invoices - Local Tax Rate",
|
||||||
"invoice_state_tax_rate": "Invoices - Provincial/State Tax Rate",
|
"invoice_state_tax_rate": "Invoices - Provincial/State Tax Rate",
|
||||||
|
"jc_hourly_rates": {
|
||||||
|
"mapa": "Job Costing - Paint Materials Hourly Cost Rate",
|
||||||
|
"mash": "Job Costing - Shop Materials Hourly Cost Rate"
|
||||||
|
},
|
||||||
"lastnumberworkingdays": "Scoreboard - Last Number of Working Days",
|
"lastnumberworkingdays": "Scoreboard - Last Number of Working Days",
|
||||||
"logo_img_path": "Shop Logo",
|
"logo_img_path": "Shop Logo",
|
||||||
"logo_img_path_height": "Logo Image Height",
|
"logo_img_path_height": "Logo Image Height",
|
||||||
@@ -555,7 +559,7 @@
|
|||||||
"driverinformation": "Driver's Information",
|
"driverinformation": "Driver's Information",
|
||||||
"findcontract": "Find Contract",
|
"findcontract": "Find Contract",
|
||||||
"findermodal": "Contract Finder",
|
"findermodal": "Contract Finder",
|
||||||
"noteconvertedfrom": "R.O. created from converted Courtesy Car Contract {{agreementnumber}}.",
|
"noteconvertedfrom": "R.O. created from converted Courtesy Car Contract {{agreementnumber}}.",
|
||||||
"populatefromjob": "Populate from Job",
|
"populatefromjob": "Populate from Job",
|
||||||
"rates": "Contract Rates",
|
"rates": "Contract Rates",
|
||||||
"time": "Time",
|
"time": "Time",
|
||||||
@@ -683,7 +687,8 @@
|
|||||||
"deleting_cloudinary": "Error deleting document from storage. {{message}}",
|
"deleting_cloudinary": "Error deleting document from storage. {{message}}",
|
||||||
"getpresignurl": "Error obtaining presigned URL for document. {{message}}",
|
"getpresignurl": "Error obtaining presigned URL for document. {{message}}",
|
||||||
"insert": "Unable to upload file. {{message}}",
|
"insert": "Unable to upload file. {{message}}",
|
||||||
"nodocuments": "There are no documents."
|
"nodocuments": "There are no documents.",
|
||||||
|
"updating": "Error updating document. {{error}}"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"confirmdelete": "Are you sure you want to delete these documents. This CANNOT be undone.",
|
"confirmdelete": "Are you sure you want to delete these documents. This CANNOT be undone.",
|
||||||
@@ -820,6 +825,8 @@
|
|||||||
"monday": "Monday",
|
"monday": "Monday",
|
||||||
"na": "N/A",
|
"na": "N/A",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
|
"nointernet": "It looks like you're not connected to the internet.",
|
||||||
|
"nointernet_sub": "Please check your connection and try again. ",
|
||||||
"none": "None",
|
"none": "None",
|
||||||
"out": "Out",
|
"out": "Out",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
@@ -1281,7 +1288,7 @@
|
|||||||
"gppercent": "% G.P.",
|
"gppercent": "% G.P.",
|
||||||
"hrs_claimed": "Hours Claimed",
|
"hrs_claimed": "Hours Claimed",
|
||||||
"hrs_total": "Hours Total",
|
"hrs_total": "Hours Total",
|
||||||
"importnote": "The job was initially imported on {{date}} at {{time}}.",
|
"importnote": "The job was initially imported.",
|
||||||
"inproduction": "In Production",
|
"inproduction": "In Production",
|
||||||
"intakechecklist": "Intake Checklist",
|
"intakechecklist": "Intake Checklist",
|
||||||
"job": "Job Details",
|
"job": "Job Details",
|
||||||
@@ -1316,6 +1323,7 @@
|
|||||||
"partstotal": "This is the total of all parts and sublet amounts on the vehicle (some of these may require an in-house invoice).<br/>\nItems such as shop and paint materials, labor online lines, etc. are not included in this total.",
|
"partstotal": "This is the total of all parts and sublet amounts on the vehicle (some of these may require an in-house invoice).<br/>\nItems such as shop and paint materials, labor online lines, etc. are not included in this total.",
|
||||||
"totalreturns": "The total amount of returns created for this job."
|
"totalreturns": "The total amount of returns created for this job."
|
||||||
},
|
},
|
||||||
|
"prt_dsmk_total": "Line Item Markup",
|
||||||
"rates": "Rates",
|
"rates": "Rates",
|
||||||
"rates_subtotal": "All Rates Subtotal",
|
"rates_subtotal": "All Rates Subtotal",
|
||||||
"reconciliation": {
|
"reconciliation": {
|
||||||
@@ -1341,19 +1349,19 @@
|
|||||||
"state_tax_amt": "Provincial/State Taxes",
|
"state_tax_amt": "Provincial/State Taxes",
|
||||||
"subletstotal": "Sublets Total",
|
"subletstotal": "Sublets Total",
|
||||||
"subtotal": "Subtotal",
|
"subtotal": "Subtotal",
|
||||||
"supplementnote": "The job had a supplement imported on {{date}} at {{time}}.",
|
"supplementnote": "The job had a supplement imported.",
|
||||||
"suspense": "Suspense",
|
"suspense": "Suspense",
|
||||||
"total_cost": "Total Cost",
|
"total_cost": "Total Cost",
|
||||||
"total_cust_payable": "Total Customer Amount Payable",
|
"total_cust_payable": "Total Customer Amount Payable",
|
||||||
"total_repairs": "Total Repairs",
|
"total_repairs": "Total Repairs",
|
||||||
"total_sales": "Total Sales",
|
"total_sales": "Total Sales",
|
||||||
"totals": "Totals",
|
"totals": "Totals",
|
||||||
"unvoidnote": "This job was unvoided by {{email}}.",
|
"unvoidnote": "This job was unvoided.",
|
||||||
"vehicle_info": "Vehicle",
|
"vehicle_info": "Vehicle",
|
||||||
"vehicleassociation": "Vehicle Association",
|
"vehicleassociation": "Vehicle Association",
|
||||||
"viewallocations": "View Allocations",
|
"viewallocations": "View Allocations",
|
||||||
"voidjob": "Are you sure you want to void this job? This cannot be easily undone. ",
|
"voidjob": "Are you sure you want to void this job? This cannot be easily undone. ",
|
||||||
"voidnote": "This repair order was voided on {{date}} at {{time}}."
|
"voidnote": "This job was voided."
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"addedtoproduction": "Job added to production board.",
|
"addedtoproduction": "Job added to production board.",
|
||||||
@@ -1664,6 +1672,8 @@
|
|||||||
"state": "Province/State"
|
"state": "Province/State"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"noneselected": "No phone book entry selected. ",
|
||||||
|
"onenamerequired": "At least one name related field is required.",
|
||||||
"vendorcategory": "Vendor"
|
"vendorcategory": "Vendor"
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
@@ -1815,6 +1825,9 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"activeshop": "Active Shop"
|
"activeshop": "Active Shop"
|
||||||
|
},
|
||||||
|
"successes": {
|
||||||
|
"updated": "Profile updated successfully."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"reportcenter": {
|
"reportcenter": {
|
||||||
|
|||||||
@@ -218,6 +218,10 @@
|
|||||||
"invoice_federal_tax_rate": "",
|
"invoice_federal_tax_rate": "",
|
||||||
"invoice_local_tax_rate": "",
|
"invoice_local_tax_rate": "",
|
||||||
"invoice_state_tax_rate": "",
|
"invoice_state_tax_rate": "",
|
||||||
|
"jc_hourly_rates": {
|
||||||
|
"mapa": "",
|
||||||
|
"mash": ""
|
||||||
|
},
|
||||||
"lastnumberworkingdays": "",
|
"lastnumberworkingdays": "",
|
||||||
"logo_img_path": "",
|
"logo_img_path": "",
|
||||||
"logo_img_path_height": "",
|
"logo_img_path_height": "",
|
||||||
@@ -683,7 +687,8 @@
|
|||||||
"deleting_cloudinary": "",
|
"deleting_cloudinary": "",
|
||||||
"getpresignurl": "Error al obtener la URL prescrita para el documento. {{message}}",
|
"getpresignurl": "Error al obtener la URL prescrita para el documento. {{message}}",
|
||||||
"insert": "Incapaz de cargar el archivo. {{message}}",
|
"insert": "Incapaz de cargar el archivo. {{message}}",
|
||||||
"nodocuments": "No hay documentos"
|
"nodocuments": "No hay documentos",
|
||||||
|
"updating": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"confirmdelete": "",
|
"confirmdelete": "",
|
||||||
@@ -820,6 +825,8 @@
|
|||||||
"monday": "",
|
"monday": "",
|
||||||
"na": "N / A",
|
"na": "N / A",
|
||||||
"no": "",
|
"no": "",
|
||||||
|
"nointernet": "",
|
||||||
|
"nointernet_sub": "",
|
||||||
"none": "",
|
"none": "",
|
||||||
"out": "Afuera",
|
"out": "Afuera",
|
||||||
"password": "",
|
"password": "",
|
||||||
@@ -1316,6 +1323,7 @@
|
|||||||
"partstotal": "",
|
"partstotal": "",
|
||||||
"totalreturns": ""
|
"totalreturns": ""
|
||||||
},
|
},
|
||||||
|
"prt_dsmk_total": "",
|
||||||
"rates": "Tarifas",
|
"rates": "Tarifas",
|
||||||
"rates_subtotal": "",
|
"rates_subtotal": "",
|
||||||
"reconciliation": {
|
"reconciliation": {
|
||||||
@@ -1664,6 +1672,8 @@
|
|||||||
"state": ""
|
"state": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"noneselected": "",
|
||||||
|
"onenamerequired": "",
|
||||||
"vendorcategory": ""
|
"vendorcategory": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
@@ -1815,6 +1825,9 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"activeshop": ""
|
"activeshop": ""
|
||||||
|
},
|
||||||
|
"successes": {
|
||||||
|
"updated": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"reportcenter": {
|
"reportcenter": {
|
||||||
|
|||||||
@@ -218,6 +218,10 @@
|
|||||||
"invoice_federal_tax_rate": "",
|
"invoice_federal_tax_rate": "",
|
||||||
"invoice_local_tax_rate": "",
|
"invoice_local_tax_rate": "",
|
||||||
"invoice_state_tax_rate": "",
|
"invoice_state_tax_rate": "",
|
||||||
|
"jc_hourly_rates": {
|
||||||
|
"mapa": "",
|
||||||
|
"mash": ""
|
||||||
|
},
|
||||||
"lastnumberworkingdays": "",
|
"lastnumberworkingdays": "",
|
||||||
"logo_img_path": "",
|
"logo_img_path": "",
|
||||||
"logo_img_path_height": "",
|
"logo_img_path_height": "",
|
||||||
@@ -683,7 +687,8 @@
|
|||||||
"deleting_cloudinary": "",
|
"deleting_cloudinary": "",
|
||||||
"getpresignurl": "Erreur lors de l'obtention de l'URL présignée pour le document. {{message}}",
|
"getpresignurl": "Erreur lors de l'obtention de l'URL présignée pour le document. {{message}}",
|
||||||
"insert": "Incapable de télécharger le fichier. {{message}}",
|
"insert": "Incapable de télécharger le fichier. {{message}}",
|
||||||
"nodocuments": "Il n'y a pas de documents."
|
"nodocuments": "Il n'y a pas de documents.",
|
||||||
|
"updating": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"confirmdelete": "",
|
"confirmdelete": "",
|
||||||
@@ -820,6 +825,8 @@
|
|||||||
"monday": "",
|
"monday": "",
|
||||||
"na": "N / A",
|
"na": "N / A",
|
||||||
"no": "",
|
"no": "",
|
||||||
|
"nointernet": "",
|
||||||
|
"nointernet_sub": "",
|
||||||
"none": "",
|
"none": "",
|
||||||
"out": "En dehors",
|
"out": "En dehors",
|
||||||
"password": "",
|
"password": "",
|
||||||
@@ -1316,6 +1323,7 @@
|
|||||||
"partstotal": "",
|
"partstotal": "",
|
||||||
"totalreturns": ""
|
"totalreturns": ""
|
||||||
},
|
},
|
||||||
|
"prt_dsmk_total": "",
|
||||||
"rates": "Les taux",
|
"rates": "Les taux",
|
||||||
"rates_subtotal": "",
|
"rates_subtotal": "",
|
||||||
"reconciliation": {
|
"reconciliation": {
|
||||||
@@ -1664,6 +1672,8 @@
|
|||||||
"state": ""
|
"state": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"noneselected": "",
|
||||||
|
"onenamerequired": "",
|
||||||
"vendorcategory": ""
|
"vendorcategory": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
@@ -1815,6 +1825,9 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"activeshop": ""
|
"activeshop": ""
|
||||||
|
},
|
||||||
|
"successes": {
|
||||||
|
"updated": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"reportcenter": {
|
"reportcenter": {
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."bodyshops" ADD CONSTRAINT "bodyshops_autohouseid_key"
|
||||||
|
UNIQUE ("autohouseid");
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."bodyshops" DROP CONSTRAINT "bodyshops_autohouseid_key";
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."bodyshops" DROP COLUMN "jc_hourly_rates";
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."bodyshops" ADD COLUMN "jc_hourly_rates" jsonb NULL
|
||||||
|
DEFAULT jsonb_build_object();
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: false
|
||||||
|
columns:
|
||||||
|
- accountingconfig
|
||||||
|
- address1
|
||||||
|
- address2
|
||||||
|
- appt_alt_transport
|
||||||
|
- appt_colors
|
||||||
|
- appt_length
|
||||||
|
- bill_tax_rates
|
||||||
|
- city
|
||||||
|
- country
|
||||||
|
- created_at
|
||||||
|
- default_adjustment_rate
|
||||||
|
- deliverchecklist
|
||||||
|
- email
|
||||||
|
- enforce_class
|
||||||
|
- enforce_referral
|
||||||
|
- federal_tax_id
|
||||||
|
- id
|
||||||
|
- imexshopid
|
||||||
|
- inhousevendorid
|
||||||
|
- insurance_vendor_id
|
||||||
|
- intakechecklist
|
||||||
|
- jobsizelimit
|
||||||
|
- logo_img_path
|
||||||
|
- md_categories
|
||||||
|
- md_ccc_rates
|
||||||
|
- md_classes
|
||||||
|
- md_hour_split
|
||||||
|
- md_ins_cos
|
||||||
|
- md_labor_rates
|
||||||
|
- md_messaging_presets
|
||||||
|
- md_notes_presets
|
||||||
|
- md_order_statuses
|
||||||
|
- md_parts_locations
|
||||||
|
- md_payment_types
|
||||||
|
- md_rbac
|
||||||
|
- md_referral_sources
|
||||||
|
- md_responsibility_centers
|
||||||
|
- md_ro_statuses
|
||||||
|
- messagingservicesid
|
||||||
|
- phone
|
||||||
|
- prodtargethrs
|
||||||
|
- production_config
|
||||||
|
- region_config
|
||||||
|
- schedule_end_time
|
||||||
|
- schedule_start_time
|
||||||
|
- scoreboard_target
|
||||||
|
- shopname
|
||||||
|
- shoprates
|
||||||
|
- speedprint
|
||||||
|
- ssbuckets
|
||||||
|
- state
|
||||||
|
- state_tax_id
|
||||||
|
- stripe_acct_id
|
||||||
|
- sub_status
|
||||||
|
- target_touchtime
|
||||||
|
- template_header
|
||||||
|
- textid
|
||||||
|
- updated_at
|
||||||
|
- use_fippa
|
||||||
|
- website
|
||||||
|
- workingdays
|
||||||
|
- zip_post
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
associations:
|
||||||
|
user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: false
|
||||||
|
columns:
|
||||||
|
- accountingconfig
|
||||||
|
- address1
|
||||||
|
- address2
|
||||||
|
- appt_alt_transport
|
||||||
|
- appt_colors
|
||||||
|
- appt_length
|
||||||
|
- bill_tax_rates
|
||||||
|
- city
|
||||||
|
- country
|
||||||
|
- created_at
|
||||||
|
- default_adjustment_rate
|
||||||
|
- deliverchecklist
|
||||||
|
- email
|
||||||
|
- enforce_class
|
||||||
|
- enforce_referral
|
||||||
|
- federal_tax_id
|
||||||
|
- id
|
||||||
|
- imexshopid
|
||||||
|
- inhousevendorid
|
||||||
|
- insurance_vendor_id
|
||||||
|
- intakechecklist
|
||||||
|
- jc_hourly_rates
|
||||||
|
- jobsizelimit
|
||||||
|
- logo_img_path
|
||||||
|
- md_categories
|
||||||
|
- md_ccc_rates
|
||||||
|
- md_classes
|
||||||
|
- md_hour_split
|
||||||
|
- md_ins_cos
|
||||||
|
- md_labor_rates
|
||||||
|
- md_messaging_presets
|
||||||
|
- md_notes_presets
|
||||||
|
- md_order_statuses
|
||||||
|
- md_parts_locations
|
||||||
|
- md_payment_types
|
||||||
|
- md_rbac
|
||||||
|
- md_referral_sources
|
||||||
|
- md_responsibility_centers
|
||||||
|
- md_ro_statuses
|
||||||
|
- messagingservicesid
|
||||||
|
- phone
|
||||||
|
- prodtargethrs
|
||||||
|
- production_config
|
||||||
|
- region_config
|
||||||
|
- schedule_end_time
|
||||||
|
- schedule_start_time
|
||||||
|
- scoreboard_target
|
||||||
|
- shopname
|
||||||
|
- shoprates
|
||||||
|
- speedprint
|
||||||
|
- ssbuckets
|
||||||
|
- state
|
||||||
|
- state_tax_id
|
||||||
|
- stripe_acct_id
|
||||||
|
- sub_status
|
||||||
|
- target_touchtime
|
||||||
|
- template_header
|
||||||
|
- textid
|
||||||
|
- updated_at
|
||||||
|
- use_fippa
|
||||||
|
- website
|
||||||
|
- workingdays
|
||||||
|
- zip_post
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
associations:
|
||||||
|
user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- accountingconfig
|
||||||
|
- address1
|
||||||
|
- address2
|
||||||
|
- appt_alt_transport
|
||||||
|
- appt_colors
|
||||||
|
- appt_length
|
||||||
|
- bill_tax_rates
|
||||||
|
- city
|
||||||
|
- country
|
||||||
|
- created_at
|
||||||
|
- default_adjustment_rate
|
||||||
|
- deliverchecklist
|
||||||
|
- email
|
||||||
|
- enforce_class
|
||||||
|
- enforce_referral
|
||||||
|
- federal_tax_id
|
||||||
|
- id
|
||||||
|
- inhousevendorid
|
||||||
|
- insurance_vendor_id
|
||||||
|
- intakechecklist
|
||||||
|
- logo_img_path
|
||||||
|
- md_categories
|
||||||
|
- md_ccc_rates
|
||||||
|
- md_classes
|
||||||
|
- md_hour_split
|
||||||
|
- md_ins_cos
|
||||||
|
- md_labor_rates
|
||||||
|
- md_messaging_presets
|
||||||
|
- md_notes_presets
|
||||||
|
- md_order_statuses
|
||||||
|
- md_parts_locations
|
||||||
|
- md_payment_types
|
||||||
|
- md_rbac
|
||||||
|
- md_referral_sources
|
||||||
|
- md_responsibility_centers
|
||||||
|
- md_ro_statuses
|
||||||
|
- phone
|
||||||
|
- prodtargethrs
|
||||||
|
- production_config
|
||||||
|
- schedule_end_time
|
||||||
|
- schedule_start_time
|
||||||
|
- scoreboard_target
|
||||||
|
- shopname
|
||||||
|
- shoprates
|
||||||
|
- speedprint
|
||||||
|
- ssbuckets
|
||||||
|
- state
|
||||||
|
- state_tax_id
|
||||||
|
- target_touchtime
|
||||||
|
- updated_at
|
||||||
|
- use_fippa
|
||||||
|
- website
|
||||||
|
- workingdays
|
||||||
|
- zip_post
|
||||||
|
filter:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- accountingconfig
|
||||||
|
- address1
|
||||||
|
- address2
|
||||||
|
- appt_alt_transport
|
||||||
|
- appt_colors
|
||||||
|
- appt_length
|
||||||
|
- bill_tax_rates
|
||||||
|
- city
|
||||||
|
- country
|
||||||
|
- created_at
|
||||||
|
- default_adjustment_rate
|
||||||
|
- deliverchecklist
|
||||||
|
- email
|
||||||
|
- enforce_class
|
||||||
|
- enforce_referral
|
||||||
|
- federal_tax_id
|
||||||
|
- id
|
||||||
|
- inhousevendorid
|
||||||
|
- insurance_vendor_id
|
||||||
|
- intakechecklist
|
||||||
|
- jc_hourly_rates
|
||||||
|
- logo_img_path
|
||||||
|
- md_categories
|
||||||
|
- md_ccc_rates
|
||||||
|
- md_classes
|
||||||
|
- md_hour_split
|
||||||
|
- md_ins_cos
|
||||||
|
- md_labor_rates
|
||||||
|
- md_messaging_presets
|
||||||
|
- md_notes_presets
|
||||||
|
- md_order_statuses
|
||||||
|
- md_parts_locations
|
||||||
|
- md_payment_types
|
||||||
|
- md_rbac
|
||||||
|
- md_referral_sources
|
||||||
|
- md_responsibility_centers
|
||||||
|
- md_ro_statuses
|
||||||
|
- phone
|
||||||
|
- prodtargethrs
|
||||||
|
- production_config
|
||||||
|
- schedule_end_time
|
||||||
|
- schedule_start_time
|
||||||
|
- scoreboard_target
|
||||||
|
- shopname
|
||||||
|
- shoprates
|
||||||
|
- speedprint
|
||||||
|
- ssbuckets
|
||||||
|
- state
|
||||||
|
- state_tax_id
|
||||||
|
- target_touchtime
|
||||||
|
- updated_at
|
||||||
|
- use_fippa
|
||||||
|
- website
|
||||||
|
- workingdays
|
||||||
|
- zip_post
|
||||||
|
filter:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -771,6 +771,7 @@ tables:
|
|||||||
- inhousevendorid
|
- inhousevendorid
|
||||||
- insurance_vendor_id
|
- insurance_vendor_id
|
||||||
- intakechecklist
|
- intakechecklist
|
||||||
|
- jc_hourly_rates
|
||||||
- jobsizelimit
|
- jobsizelimit
|
||||||
- logo_img_path
|
- logo_img_path
|
||||||
- md_categories
|
- md_categories
|
||||||
@@ -841,6 +842,7 @@ tables:
|
|||||||
- inhousevendorid
|
- inhousevendorid
|
||||||
- insurance_vendor_id
|
- insurance_vendor_id
|
||||||
- intakechecklist
|
- intakechecklist
|
||||||
|
- jc_hourly_rates
|
||||||
- logo_img_path
|
- logo_img_path
|
||||||
- md_categories
|
- md_categories
|
||||||
- md_ccc_rates
|
- md_ccc_rates
|
||||||
|
|||||||
@@ -203,7 +203,8 @@ const generateInvoiceQbxml = (
|
|||||||
//Create the invoice lines mapping.
|
//Create the invoice lines mapping.
|
||||||
jobs_by_pk.joblines.map((jobline) => {
|
jobs_by_pk.joblines.map((jobline) => {
|
||||||
//Parts Lines
|
//Parts Lines
|
||||||
if (jobline.db_ref === "936008") { //If either of these DB REFs change, they also need to change in job-totals calculations.
|
if (jobline.db_ref === "936008") {
|
||||||
|
//If either of these DB REFs change, they also need to change in job-totals calculations.
|
||||||
hasMapaLine = true;
|
hasMapaLine = true;
|
||||||
}
|
}
|
||||||
if (jobline.db_ref === "936007") {
|
if (jobline.db_ref === "936007") {
|
||||||
@@ -213,7 +214,15 @@ const generateInvoiceQbxml = (
|
|||||||
if (jobline.profitcenter_part && jobline.act_price) {
|
if (jobline.profitcenter_part && jobline.act_price) {
|
||||||
const DineroAmount = Dinero({
|
const DineroAmount = Dinero({
|
||||||
amount: Math.round(jobline.act_price * 100),
|
amount: Math.round(jobline.act_price * 100),
|
||||||
}).multiply(jobline.part_qty || 1);
|
})
|
||||||
|
.multiply(jobline.part_qty || 1)
|
||||||
|
.add(
|
||||||
|
Dinero({
|
||||||
|
amount: Math.round((jobline.act_price || 0) * 100),
|
||||||
|
})
|
||||||
|
.multiply(jobline.part_qty || 0)
|
||||||
|
.percentage(jobline.prt_dsmk_p)
|
||||||
|
);
|
||||||
const account = responsibilityCenters.profits.find(
|
const account = responsibilityCenters.profits.find(
|
||||||
(i) => jobline.profitcenter_part.toLowerCase() === i.name.toLowerCase()
|
(i) => jobline.profitcenter_part.toLowerCase() === i.name.toLowerCase()
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -566,6 +566,7 @@ exports.GET_JOB_BY_PK = ` query GET_JOB_BY_PK($id: uuid!) {
|
|||||||
tax_part
|
tax_part
|
||||||
db_ref
|
db_ref
|
||||||
manual_line
|
manual_line
|
||||||
|
prt_dsmk_p
|
||||||
parts_order_lines {
|
parts_order_lines {
|
||||||
id
|
id
|
||||||
parts_order {
|
parts_order {
|
||||||
@@ -679,6 +680,7 @@ exports.QUERY_JOB_COSTING_DETAILS = ` query QUERY_JOB_COSTING_DETAILS($id: uuid!
|
|||||||
bodyshop{
|
bodyshop{
|
||||||
id
|
id
|
||||||
md_responsibility_centers
|
md_responsibility_centers
|
||||||
|
jc_hourly_rates
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
@@ -779,6 +781,7 @@ exports.QUERY_JOB_COSTING_DETAILS_MULTI = ` query QUERY_JOB_COSTING_DETAILS_MULT
|
|||||||
bodyshop {
|
bodyshop {
|
||||||
id
|
id
|
||||||
md_responsibility_centers
|
md_responsibility_centers
|
||||||
|
jc_hourly_rates
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ Dinero.globalRoundingMode = "HALF_EVEN";
|
|||||||
|
|
||||||
async function JobCosting(req, res) {
|
async function JobCosting(req, res) {
|
||||||
const { jobid } = req.body;
|
const { jobid } = req.body;
|
||||||
console.log("🚀 ~ file: job-costing.js ~ line 13 ~ jobid", jobid);
|
console.time("Query for Data");
|
||||||
console.time("querydata");
|
|
||||||
const BearerToken = req.headers.authorization;
|
const BearerToken = req.headers.authorization;
|
||||||
|
|
||||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||||
@@ -122,27 +121,34 @@ async function JobCostingMulti(req, res) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//Add all summary data.
|
//Add all summary data.
|
||||||
multiSummary.summaryData.totalPartsSales = multiSummary.summaryData.totalPartsSales.add(
|
multiSummary.summaryData.totalPartsSales =
|
||||||
costingData.summaryData.totalPartsSales
|
multiSummary.summaryData.totalPartsSales.add(
|
||||||
);
|
costingData.summaryData.totalPartsSales
|
||||||
multiSummary.summaryData.totalSales = multiSummary.summaryData.totalSales.add(
|
);
|
||||||
costingData.summaryData.totalSales
|
multiSummary.summaryData.totalSales =
|
||||||
);
|
multiSummary.summaryData.totalSales.add(
|
||||||
multiSummary.summaryData.totalLaborCost = multiSummary.summaryData.totalLaborCost.add(
|
costingData.summaryData.totalSales
|
||||||
costingData.summaryData.totalLaborCost
|
);
|
||||||
);
|
multiSummary.summaryData.totalLaborCost =
|
||||||
multiSummary.summaryData.totalLaborSales = multiSummary.summaryData.totalLaborSales.add(
|
multiSummary.summaryData.totalLaborCost.add(
|
||||||
costingData.summaryData.totalLaborSales
|
costingData.summaryData.totalLaborCost
|
||||||
);
|
);
|
||||||
multiSummary.summaryData.totalPartsCost = multiSummary.summaryData.totalPartsCost.add(
|
multiSummary.summaryData.totalLaborSales =
|
||||||
costingData.summaryData.totalPartsCost
|
multiSummary.summaryData.totalLaborSales.add(
|
||||||
);
|
costingData.summaryData.totalLaborSales
|
||||||
multiSummary.summaryData.totalCost = multiSummary.summaryData.totalCost.add(
|
);
|
||||||
costingData.summaryData.totalCost
|
multiSummary.summaryData.totalPartsCost =
|
||||||
);
|
multiSummary.summaryData.totalPartsCost.add(
|
||||||
multiSummary.summaryData.gpdollars = multiSummary.summaryData.gpdollars.add(
|
costingData.summaryData.totalPartsCost
|
||||||
costingData.summaryData.gpdollars
|
);
|
||||||
);
|
multiSummary.summaryData.totalCost =
|
||||||
|
multiSummary.summaryData.totalCost.add(
|
||||||
|
costingData.summaryData.totalCost
|
||||||
|
);
|
||||||
|
multiSummary.summaryData.gpdollars =
|
||||||
|
multiSummary.summaryData.gpdollars.add(
|
||||||
|
costingData.summaryData.gpdollars
|
||||||
|
);
|
||||||
console.timeEnd(`SummaryOfCostingData-${job.id}`);
|
console.timeEnd(`SummaryOfCostingData-${job.id}`);
|
||||||
//Take the summary data & add it to total summary data.
|
//Take the summary data & add it to total summary data.
|
||||||
});
|
});
|
||||||
@@ -202,6 +208,8 @@ function GenerateCostingData(job) {
|
|||||||
job.bodyshop.md_responsibility_centers.costs.map((p) => p.name)
|
job.bodyshop.md_responsibility_centers.costs.map((p) => p.name)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const materialsHours = { mapaHrs: 0, mashHrs: 0 };
|
||||||
|
|
||||||
//Massage the data.
|
//Massage the data.
|
||||||
const jobLineTotalsByProfitCenter =
|
const jobLineTotalsByProfitCenter =
|
||||||
job &&
|
job &&
|
||||||
@@ -220,14 +228,14 @@ function GenerateCostingData(job) {
|
|||||||
}).multiply(val.mod_lb_hrs || 0);
|
}).multiply(val.mod_lb_hrs || 0);
|
||||||
if (!acc.labor[laborProfitCenter])
|
if (!acc.labor[laborProfitCenter])
|
||||||
acc.labor[laborProfitCenter] = Dinero();
|
acc.labor[laborProfitCenter] = Dinero();
|
||||||
acc.labor[laborProfitCenter] = acc.labor[laborProfitCenter].add(
|
acc.labor[laborProfitCenter] =
|
||||||
laborAmount
|
acc.labor[laborProfitCenter].add(laborAmount);
|
||||||
);
|
|
||||||
|
|
||||||
if (val.mod_lbr_ty === "LAR") {
|
if (val.mod_lbr_ty === "LAR") {
|
||||||
if (!acc.labor[defaultProfits["MAPA"]])
|
if (!acc.labor[defaultProfits["MAPA"]])
|
||||||
acc.labor[defaultProfits["MAPA"]] = Dinero();
|
acc.labor[defaultProfits["MAPA"]] = Dinero();
|
||||||
|
|
||||||
|
materialsHours.mapaHrs += val.mod_lb_hrs || 0;
|
||||||
acc.labor[defaultProfits["MAPA"]] = acc.labor[
|
acc.labor[defaultProfits["MAPA"]] = acc.labor[
|
||||||
defaultProfits["MAPA"]
|
defaultProfits["MAPA"]
|
||||||
].add(
|
].add(
|
||||||
@@ -247,6 +255,7 @@ function GenerateCostingData(job) {
|
|||||||
amount: Math.round((job.rate_mash || 0) * 100),
|
amount: Math.round((job.rate_mash || 0) * 100),
|
||||||
}).multiply(val.mod_lb_hrs || 0)
|
}).multiply(val.mod_lb_hrs || 0)
|
||||||
);
|
);
|
||||||
|
materialsHours.mashHrs += val.mod_lb_hrs || 0;
|
||||||
}
|
}
|
||||||
//If labor line, add to paint and shop materials.
|
//If labor line, add to paint and shop materials.
|
||||||
}
|
}
|
||||||
@@ -265,12 +274,19 @@ function GenerateCostingData(job) {
|
|||||||
);
|
);
|
||||||
const partsAmount = Dinero({
|
const partsAmount = Dinero({
|
||||||
amount: Math.round((val.act_price || 0) * 100),
|
amount: Math.round((val.act_price || 0) * 100),
|
||||||
}).multiply(val.part_qty || 1);
|
})
|
||||||
|
.multiply(val.part_qty || 1)
|
||||||
|
.add(
|
||||||
|
Dinero({
|
||||||
|
amount: Math.round((val.act_price || 0) * 100),
|
||||||
|
})
|
||||||
|
.multiply(val.part_qty || 0)
|
||||||
|
.percentage(val.prt_dsmk_p)
|
||||||
|
);
|
||||||
if (!acc.parts[partsProfitCenter])
|
if (!acc.parts[partsProfitCenter])
|
||||||
acc.parts[partsProfitCenter] = Dinero();
|
acc.parts[partsProfitCenter] = Dinero();
|
||||||
acc.parts[partsProfitCenter] = acc.parts[partsProfitCenter].add(
|
acc.parts[partsProfitCenter] =
|
||||||
partsAmount
|
acc.parts[partsProfitCenter].add(partsAmount);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//To deal with additional costs.
|
//To deal with additional costs.
|
||||||
@@ -287,18 +303,20 @@ function GenerateCostingData(job) {
|
|||||||
} else {
|
} else {
|
||||||
const partsAmount = Dinero({
|
const partsAmount = Dinero({
|
||||||
amount: Math.round((val.act_price || 0) * 100),
|
amount: Math.round((val.act_price || 0) * 100),
|
||||||
}).multiply(val.part_qty || 1);
|
})
|
||||||
console.log(
|
.multiply(val.part_qty || 1)
|
||||||
`*** partsAmount`,
|
.add(
|
||||||
val.line_desc,
|
Dinero({
|
||||||
partsProfitCenter,
|
amount: Math.round((val.act_price || 0) * 100),
|
||||||
partsAmount.toJSON()
|
})
|
||||||
);
|
.multiply(val.part_qty || 0)
|
||||||
|
.percentage(val.prt_dsmk_p)
|
||||||
|
);
|
||||||
|
|
||||||
if (!acc.parts[partsProfitCenter])
|
if (!acc.parts[partsProfitCenter])
|
||||||
acc.parts[partsProfitCenter] = Dinero();
|
acc.parts[partsProfitCenter] = Dinero();
|
||||||
acc.parts[partsProfitCenter] = acc.parts[partsProfitCenter].add(
|
acc.parts[partsProfitCenter] =
|
||||||
partsAmount
|
acc.parts[partsProfitCenter].add(partsAmount);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,6 +346,52 @@ function GenerateCostingData(job) {
|
|||||||
return bill_acc;
|
return bill_acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
//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[
|
||||||
|
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||||
|
]
|
||||||
|
)
|
||||||
|
billTotalsByCostCenters[
|
||||||
|
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||||
|
] = Dinero();
|
||||||
|
billTotalsByCostCenters[
|
||||||
|
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||||
|
] = billTotalsByCostCenters[
|
||||||
|
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||||
|
].add(
|
||||||
|
Dinero({
|
||||||
|
amount:
|
||||||
|
(job.bodyshop.jc_hourly_rates &&
|
||||||
|
job.bodyshop.jc_hourly_rates.mapa * 100) ||
|
||||||
|
0,
|
||||||
|
}).multiply(materialsHours.mapaHrs)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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(materialsHours.mashHrs)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const ticketTotalsByCostCenter = job.timetickets.reduce(
|
const ticketTotalsByCostCenter = job.timetickets.reduce(
|
||||||
(ticket_acc, ticket_val) => {
|
(ticket_acc, ticket_val) => {
|
||||||
//At the invoice level.
|
//At the invoice level.
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ async function Totals(req, res) {
|
|||||||
res.status(400).send(JSON.stringify(error));
|
res.status(400).send(JSON.stringify(error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function CalculateRatesTotals(ratesList) {
|
function CalculateRatesTotals(ratesList) {
|
||||||
const jobLines = ratesList.joblines.filter((jl) => !jl.removed);
|
const jobLines = ratesList.joblines.filter((jl) => !jl.removed);
|
||||||
|
|
||||||
@@ -211,6 +212,13 @@ function CalculatePartsTotals(jobLines) {
|
|||||||
...acc,
|
...acc,
|
||||||
parts: {
|
parts: {
|
||||||
...acc.parts,
|
...acc.parts,
|
||||||
|
prt_dsmk_total: acc.parts.prt_dsmk_total.add(
|
||||||
|
Dinero({
|
||||||
|
amount: Math.round((value.act_price || 0) * 100),
|
||||||
|
})
|
||||||
|
.multiply(value.part_qty || 0)
|
||||||
|
.percentage(value.prt_dsmk_p)
|
||||||
|
),
|
||||||
list: {
|
list: {
|
||||||
...acc.parts.list,
|
...acc.parts.list,
|
||||||
[value.part_type]:
|
[value.part_type]:
|
||||||
@@ -229,11 +237,19 @@ function CalculatePartsTotals(jobLines) {
|
|||||||
}).multiply(value.part_qty || 0),
|
}).multiply(value.part_qty || 0),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
subtotal: acc.parts.subtotal.add(
|
subtotal: acc.parts.subtotal
|
||||||
Dinero({
|
.add(
|
||||||
amount: Math.round(value.act_price * 100),
|
Dinero({
|
||||||
}).multiply(value.part_qty || 0)
|
amount: Math.round(value.act_price * 100),
|
||||||
),
|
}).multiply(value.part_qty || 0)
|
||||||
|
)
|
||||||
|
.add(
|
||||||
|
Dinero({
|
||||||
|
amount: Math.round((value.act_price || 0) * 100),
|
||||||
|
})
|
||||||
|
.multiply(value.part_qty || 0)
|
||||||
|
.percentage(value.prt_dsmk_p)
|
||||||
|
),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -241,6 +257,7 @@ function CalculatePartsTotals(jobLines) {
|
|||||||
{
|
{
|
||||||
parts: {
|
parts: {
|
||||||
list: {},
|
list: {},
|
||||||
|
prt_dsmk_total: Dinero(),
|
||||||
subtotal: Dinero({ amount: 0 }),
|
subtotal: Dinero({ amount: 0 }),
|
||||||
total: Dinero({ amount: 0 }),
|
total: Dinero({ amount: 0 }),
|
||||||
},
|
},
|
||||||
@@ -360,6 +377,13 @@ function CalculateTaxesTotals(job, otherTotals) {
|
|||||||
statePartsTax = statePartsTax.add(
|
statePartsTax = statePartsTax.add(
|
||||||
Dinero({ amount: Math.round((val.act_price || 0) * 100) })
|
Dinero({ amount: Math.round((val.act_price || 0) * 100) })
|
||||||
.multiply(val.part_qty || 1)
|
.multiply(val.part_qty || 1)
|
||||||
|
.add(
|
||||||
|
Dinero({
|
||||||
|
amount: Math.round((val.act_price || 0) * 100),
|
||||||
|
})
|
||||||
|
.multiply(val.part_qty || 0)
|
||||||
|
.percentage(val.prt_dsmk_p)
|
||||||
|
)
|
||||||
.percentage(
|
.percentage(
|
||||||
((job.parts_tax_rates &&
|
((job.parts_tax_rates &&
|
||||||
job.parts_tax_rates[val.part_type] &&
|
job.parts_tax_rates[val.part_type] &&
|
||||||
|
|||||||
@@ -40,7 +40,9 @@ exports.renameKeys = async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const res = {
|
const res = {
|
||||||
id: d.id,
|
id: d.id,
|
||||||
...(await cloudinary.uploader.rename(d.from, d.to)),
|
...(await cloudinary.uploader.rename(d.from, d.to, {
|
||||||
|
resource_type: DetermineFileType(d.type),
|
||||||
|
})),
|
||||||
};
|
};
|
||||||
return res;
|
return res;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -56,3 +58,14 @@ exports.renameKeys = async (req, res) => {
|
|||||||
|
|
||||||
res.send(result);
|
res.send(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Also needs to be updated in upload utility and mobile app.
|
||||||
|
function DetermineFileType(filetype) {
|
||||||
|
if (!filetype) return "auto";
|
||||||
|
else if (filetype.startsWith("image")) return "image";
|
||||||
|
else if (filetype.startsWith("video")) return "video";
|
||||||
|
else if (filetype.startsWith("application/pdf")) return "image";
|
||||||
|
else if (filetype.startsWith("application")) return "raw";
|
||||||
|
|
||||||
|
return "auto";
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user