@@ -24030,6 +24030,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>help</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>home</name>
|
<name>home</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -26853,6 +26874,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>orderedby</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>quantity</name>
|
<name>quantity</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
"jsreport-browser-client-dist": "^1.3.0",
|
"jsreport-browser-client-dist": "^1.3.0",
|
||||||
"libphonenumber-js": "^1.9.17",
|
"libphonenumber-js": "^1.9.17",
|
||||||
"logrocket": "^1.2.0",
|
"logrocket": "^1.2.0",
|
||||||
|
"markerjs2": "^2.8.1",
|
||||||
"moment-business-days": "^1.2.0",
|
"moment-business-days": "^1.2.0",
|
||||||
"phone": "^2.4.21",
|
"phone": "^2.4.21",
|
||||||
"preval.macro": "^5.0.0",
|
"preval.macro": "^5.0.0",
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import { ConfigProvider } from "antd";
|
|||||||
import enLocale from "antd/es/locale/en_US";
|
import enLocale from "antd/es/locale/en_US";
|
||||||
import LogRocket from "logrocket";
|
import LogRocket from "logrocket";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
|
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
|
||||||
import client from "../utils/GraphQLClient";
|
import client from "../utils/GraphQLClient";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import JiraSupportComponent from "../components/jira-support-widget/jira-support-widget.component";
|
||||||
moment.locale("en-US");
|
moment.locale("en-US");
|
||||||
|
|
||||||
if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
|
if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
|
||||||
@@ -15,6 +16,23 @@ if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
|
|||||||
export default function AppContainer() {
|
export default function AppContainer() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Include the Crisp code here, without the <script></script> tags
|
||||||
|
window.$crisp = [];
|
||||||
|
window.CRISP_WEBSITE_ID = "36724f62-2eb0-4b29-9cdd-9905fb99913e";
|
||||||
|
|
||||||
|
var d = document;
|
||||||
|
var s = d.createElement("script");
|
||||||
|
|
||||||
|
s.src = "https://client.crisp.chat/l.js";
|
||||||
|
s.async = 1;
|
||||||
|
d.getElementsByTagName("head")[0].appendChild(s);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
d.getElementsByTagName("head")[0].removeChild(s);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ApolloProvider client={client}>
|
<ApolloProvider client={client}>
|
||||||
<ConfigProvider
|
<ConfigProvider
|
||||||
@@ -30,6 +48,7 @@ export default function AppContainer() {
|
|||||||
>
|
>
|
||||||
<GlobalLoadingBar />
|
<GlobalLoadingBar />
|
||||||
<App />
|
<App />
|
||||||
|
<JiraSupportComponent />
|
||||||
</ConfigProvider>
|
</ConfigProvider>
|
||||||
</ApolloProvider>
|
</ApolloProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Route, Switch } from "react-router-dom";
|
import { Route, Switch } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import DocumentEditorContainer from "../components/document-editor/document-editor.container";
|
||||||
import ErrorBoundary from "../components/error-boundary/error-boundary.component";
|
import ErrorBoundary from "../components/error-boundary/error-boundary.component";
|
||||||
//Component Imports
|
//Component Imports
|
||||||
import LoadingSpinner from "../components/loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../components/loading-spinner/loading-spinner.component";
|
||||||
@@ -39,8 +40,12 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
|
|
||||||
export function App({ checkUserSession, currentUser, online, setOnline }) {
|
export function App({ checkUserSession, currentUser, online, setOnline }) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!navigator.onLine) {
|
||||||
|
setOnline(false);
|
||||||
|
}
|
||||||
|
|
||||||
checkUserSession();
|
checkUserSession();
|
||||||
}, [checkUserSession]);
|
}, [checkUserSession, setOnline]);
|
||||||
|
|
||||||
//const b = Grid.useBreakpoint();
|
//const b = Grid.useBreakpoint();
|
||||||
// console.log("Breakpoints:", b);
|
// console.log("Breakpoints:", b);
|
||||||
@@ -118,6 +123,13 @@ export function App({ checkUserSession, currentUser, online, setOnline }) {
|
|||||||
component={TechPageContainer}
|
component={TechPageContainer}
|
||||||
/>
|
/>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
<ErrorBoundary>
|
||||||
|
<PrivateRoute
|
||||||
|
isAuthorized={currentUser.authorized}
|
||||||
|
path="/edit"
|
||||||
|
component={DocumentEditorContainer}
|
||||||
|
/>
|
||||||
|
</ErrorBoundary>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</Switch>
|
</Switch>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Link } from "react-router-dom";
|
|||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
import PaymentExportButton from "../payment-export-button/payment-export-button.component";
|
import PaymentExportButton from "../payment-export-button/payment-export-button.component";
|
||||||
import PaymentsExportAllButton from "../payments-export-all-button/payments-export-all-button.component";
|
import PaymentsExportAllButton from "../payments-export-all-button/payments-export-all-button.component";
|
||||||
|
|
||||||
@@ -41,19 +41,12 @@ export default function AccountingPayablesTableComponent({
|
|||||||
title: t("payments.fields.date"),
|
title: t("payments.fields.date"),
|
||||||
dataIndex: "date",
|
dataIndex: "date",
|
||||||
key: "date",
|
key: "date",
|
||||||
sorter: (a, b) => alphaSort(a.date, b.date),
|
sorter: (a, b) => dateSort(a.date, b.date),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "date" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "date" && state.sortedInfo.order,
|
||||||
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
|
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: t("payments.fields.date"),
|
|
||||||
dataIndex: "date",
|
|
||||||
key: "date",
|
|
||||||
sorter: (a, b) => alphaSort(a.date, b.date),
|
|
||||||
sortOrder:
|
|
||||||
state.sortedInfo.columnKey === "date" && state.sortedInfo.order,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.owner"),
|
title: t("jobs.fields.owner"),
|
||||||
dataIndex: "owner",
|
dataIndex: "owner",
|
||||||
@@ -61,7 +54,7 @@ export default function AccountingPayablesTableComponent({
|
|||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
sorter: (a, b) => alphaSort(a.job.ownr_ln, b.job.ownr_ln),
|
sorter: (a, b) => alphaSort(a.job.ownr_ln, b.job.ownr_ln),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "ownr_ln" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.job.owner ? (
|
return record.job.owner ? (
|
||||||
<Link to={"/manage/owners/" + record.job.owner.id}>
|
<Link to={"/manage/owners/" + record.job.owner.id}>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Space } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||||
import ChatConversationTitleTags from "../chat-conversation-title-tags/chat-conversation-title-tags.component";
|
import ChatConversationTitleTags from "../chat-conversation-title-tags/chat-conversation-title-tags.component";
|
||||||
@@ -5,20 +6,16 @@ import ChatTagRoContainer from "../chat-tag-ro/chat-tag-ro.container";
|
|||||||
|
|
||||||
export default function ChatConversationTitle({ conversation }) {
|
export default function ChatConversationTitle({ conversation }) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Space flex>
|
||||||
<div className="imex-flex-row">
|
<PhoneNumberFormatter>
|
||||||
<ChatConversationTitleTags
|
{conversation && conversation.phone_num}
|
||||||
jobConversations={
|
</PhoneNumberFormatter>
|
||||||
(conversation && conversation.job_conversations) || []
|
<ChatConversationTitleTags
|
||||||
}
|
jobConversations={
|
||||||
/>
|
(conversation && conversation.job_conversations) || []
|
||||||
<ChatTagRoContainer conversation={conversation || []} />
|
}
|
||||||
</div>
|
/>
|
||||||
<div className="imex-flex-row">
|
<ChatTagRoContainer conversation={conversation || []} />
|
||||||
<PhoneNumberFormatter>
|
</Space>
|
||||||
{conversation && conversation.phone_num}
|
|
||||||
</PhoneNumberFormatter>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CloseCircleOutlined, LoadingOutlined } from "@ant-design/icons";
|
import { CloseCircleOutlined, LoadingOutlined } from "@ant-design/icons";
|
||||||
import { Select, Empty } from "antd";
|
import { Select, Empty, Space } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
@@ -13,27 +13,27 @@ export default function ChatTagRoComponent({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Space flex>
|
||||||
<Select
|
<div style={{ width: "15rem" }}>
|
||||||
showSearch
|
<Select
|
||||||
autoFocus
|
showSearch
|
||||||
style={{
|
autoFocus
|
||||||
width: 300,
|
dropdownMatchSelectWidth
|
||||||
}}
|
placeholder={t("general.labels.search")}
|
||||||
placeholder={t("general.labels.search")}
|
filterOption={false}
|
||||||
filterOption={false}
|
onSearch={handleSearch}
|
||||||
onSearch={handleSearch}
|
onSelect={handleInsertTag}
|
||||||
onSelect={handleInsertTag}
|
notFoundContent={loading ? <LoadingOutlined /> : <Empty />}
|
||||||
notFoundContent={loading ? <LoadingOutlined /> : <Empty />}
|
>
|
||||||
>
|
{roOptions.map((item, idx) => (
|
||||||
{roOptions.map((item, idx) => (
|
<Select.Option key={item.id || idx}>
|
||||||
<Select.Option key={item.id || idx}>
|
{` ${item.ro_number || ""} | ${item.ownr_fn || ""} ${
|
||||||
{` ${item.ro_number || ""} | ${item.ownr_fn || ""} ${
|
item.ownr_ln || ""
|
||||||
item.ownr_ln || ""
|
} ${item.ownr_co_nm || ""}`}
|
||||||
} ${item.ownr_co_nm || ""}`}
|
</Select.Option>
|
||||||
</Select.Option>
|
))}
|
||||||
))}
|
</Select>
|
||||||
</Select>
|
</div>
|
||||||
{loading ? <LoadingOutlined /> : null}
|
{loading ? <LoadingOutlined /> : null}
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
@@ -41,6 +41,6 @@ export default function ChatTagRoComponent({
|
|||||||
) : (
|
) : (
|
||||||
<CloseCircleOutlined onClick={() => setVisible(false)} />
|
<CloseCircleOutlined onClick={() => setVisible(false)} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
//import "tui-image-editor/dist/tui-image-editor.css";
|
||||||
|
import { Result } from "antd";
|
||||||
|
import * as markerjs2 from "markerjs2";
|
||||||
|
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import {
|
||||||
|
selectBodyshop,
|
||||||
|
selectCurrentUser,
|
||||||
|
} from "../../redux/user/user.selectors";
|
||||||
|
import { handleUpload } from "../documents-upload/documents-upload.utility";
|
||||||
|
import { GenerateSrcUrl } from "../jobs-documents-gallery/job-documents.utility";
|
||||||
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
currentUser: selectCurrentUser,
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
|
||||||
|
export function DocumentEditorComponent({ currentUser, bodyshop, document }) {
|
||||||
|
const imgRef = useRef(null);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [uploaded, setuploaded] = useState(false);
|
||||||
|
const markerArea = useRef(null);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const triggerUpload = useCallback(
|
||||||
|
async (dataUrl) => {
|
||||||
|
setLoading(true);
|
||||||
|
handleUpload(
|
||||||
|
{
|
||||||
|
filename: `${document.key.split("/").pop()}-${Date.now()}.jpg`,
|
||||||
|
file: await b64toBlob(dataUrl),
|
||||||
|
onSuccess: () => {
|
||||||
|
setLoading(false);
|
||||||
|
setuploaded(true);
|
||||||
|
},
|
||||||
|
onError: () => setLoading(false),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
bodyshop: bodyshop,
|
||||||
|
uploaded_by: currentUser.email,
|
||||||
|
jobId: document.jobid,
|
||||||
|
//billId: billId,
|
||||||
|
tagsArray: ["edited"],
|
||||||
|
//callback: callbackAfterUpload,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[bodyshop, currentUser, document]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (imgRef.current !== null) {
|
||||||
|
// create a marker.js MarkerArea
|
||||||
|
markerArea.current = new markerjs2.MarkerArea(imgRef.current);
|
||||||
|
console.log(`markerArea.current`, markerArea.current);
|
||||||
|
// attach an event handler to assign annotated image back to our image element
|
||||||
|
markerArea.current.addCloseEventListener((closeEvent) => {
|
||||||
|
console.log("Close Event", closeEvent);
|
||||||
|
});
|
||||||
|
|
||||||
|
markerArea.current.addRenderEventListener((dataUrl) => {
|
||||||
|
imgRef.current.src = dataUrl;
|
||||||
|
markerArea.current.close();
|
||||||
|
triggerUpload(dataUrl);
|
||||||
|
});
|
||||||
|
// launch marker.js
|
||||||
|
|
||||||
|
markerArea.current.renderAtNaturalSize = true;
|
||||||
|
markerArea.current.renderImageType = "image/jpeg";
|
||||||
|
markerArea.current.renderImageQuality = 1;
|
||||||
|
//markerArea.current.settings.displayMode = "inline";
|
||||||
|
markerArea.current.show();
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [triggerUpload]);
|
||||||
|
|
||||||
|
async function b64toBlob(url) {
|
||||||
|
const res = await fetch(url);
|
||||||
|
return await res.blob();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{!loading && !uploaded && (
|
||||||
|
<img
|
||||||
|
ref={imgRef}
|
||||||
|
src={GenerateSrcUrl(document)}
|
||||||
|
alt="sample"
|
||||||
|
crossOrigin="anonymous"
|
||||||
|
style={{ maxWidth: "90vw", maxHeight: "90vh" }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{loading && <LoadingSpinner message={t("documents.labels.uploading")} />}
|
||||||
|
{uploaded && (
|
||||||
|
<Result
|
||||||
|
status="success"
|
||||||
|
title={t("documents.successes.edituploaded")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(DocumentEditorComponent);
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import { useQuery } from "@apollo/client";
|
||||||
|
import { Result } from "antd";
|
||||||
|
import queryString from "query-string";
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { useLocation } from "react-router";
|
||||||
|
import { QUERY_BODYSHOP } from "../../graphql/bodyshop.queries";
|
||||||
|
import { GET_DOCUMENT_BY_PK } from "../../graphql/documents.queries";
|
||||||
|
import { setBodyshop } from "../../redux/user/user.actions";
|
||||||
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
|
import DocumentEditor from "./document-editor.component";
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
setBodyshop: (bs) => dispatch(setBodyshop(bs)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(null, mapDispatchToProps)(DocumentEditorContainer);
|
||||||
|
|
||||||
|
export function DocumentEditorContainer({ setBodyshop }) {
|
||||||
|
//Get the image details for the image to be saved.
|
||||||
|
//Get the document id from the search string.
|
||||||
|
const { documentId } = queryString.parse(useLocation().search);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const {
|
||||||
|
loading: loadingShop,
|
||||||
|
error: errorShop,
|
||||||
|
data: dataShop,
|
||||||
|
} = useQuery(QUERY_BODYSHOP, {
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (dataShop) setBodyshop(dataShop.bodyshops[0]);
|
||||||
|
}, [dataShop, setBodyshop]);
|
||||||
|
|
||||||
|
const { loading, error, data } = useQuery(GET_DOCUMENT_BY_PK, {
|
||||||
|
variables: { documentId },
|
||||||
|
skip: !documentId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (loading || loadingShop) return <LoadingSpinner />;
|
||||||
|
if (error || errorShop)
|
||||||
|
return (
|
||||||
|
<AlertComponent
|
||||||
|
message={error.message || errorShop.message}
|
||||||
|
type="error"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!data || !data.documents_by_pk)
|
||||||
|
return <Result status="404" title={t("general.errors.notfound")} />;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<DocumentEditor document={data ? data.documents_by_pk : null} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -21,8 +21,13 @@ export const handleUpload = (ev, context) => {
|
|||||||
const { onError, onSuccess, onProgress } = ev;
|
const { onError, onSuccess, onProgress } = ev;
|
||||||
const { bodyshop, jobId } = context;
|
const { bodyshop, jobId } = context;
|
||||||
|
|
||||||
let key = `${bodyshop.id}/${jobId}/${ev.file.name.replace(/\.[^/.]+$/, "")}`;
|
const fileName = ev.file.name || ev.filename;
|
||||||
let extension = ev.file.name.split(".").pop();
|
|
||||||
|
let key = `${bodyshop.id}/${jobId}/${fileName.replace(
|
||||||
|
/\.[^/.]+$/,
|
||||||
|
""
|
||||||
|
)}-${new Date().getTime()}`;
|
||||||
|
let extension = fileName.split(".").pop();
|
||||||
uploadToCloudinary(
|
uploadToCloudinary(
|
||||||
key,
|
key,
|
||||||
extension,
|
extension,
|
||||||
|
|||||||
@@ -57,6 +57,23 @@ ${this.state.error.stack}
|
|||||||
if (this.state.hasErrored === true) {
|
if (this.state.hasErrored === true) {
|
||||||
logImEXEvent("error_boundary_rendered", { error, info });
|
logImEXEvent("error_boundary_rendered", { error, info });
|
||||||
|
|
||||||
|
window.$crisp.push([
|
||||||
|
"set",
|
||||||
|
"session:event",
|
||||||
|
[
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"error_boundary",
|
||||||
|
{
|
||||||
|
error: this.state.error.message,
|
||||||
|
stack: this.state.error.stack,
|
||||||
|
},
|
||||||
|
"red",
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Result
|
<Result
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import Icon, {
|
|||||||
ToolFilled,
|
ToolFilled,
|
||||||
UnorderedListOutlined,
|
UnorderedListOutlined,
|
||||||
UserOutlined,
|
UserOutlined,
|
||||||
|
QuestionCircleFilled,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { Layout, Menu } from "antd";
|
import { Layout, Menu } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
@@ -284,6 +285,15 @@ function Header({
|
|||||||
<Link to="/manage/shop/csi">{t("menus.header.shop_csi")}</Link>
|
<Link to="/manage/shop/csi">{t("menus.header.shop_csi")}</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu.SubMenu>
|
</Menu.SubMenu>
|
||||||
|
<Menu.Item
|
||||||
|
key="shop"
|
||||||
|
onClick={() => {
|
||||||
|
window.open("https://help.imex.online/", "_blank");
|
||||||
|
}}
|
||||||
|
icon={<Icon component={QuestionCircleFilled} />}
|
||||||
|
>
|
||||||
|
{t("menus.header.help")}
|
||||||
|
</Menu.Item>
|
||||||
<Menu.SubMenu
|
<Menu.SubMenu
|
||||||
style={{ float: "right" }}
|
style={{ float: "right" }}
|
||||||
title={
|
title={
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
import React, { useEffect } from "react";
|
import React from "react";
|
||||||
|
|
||||||
export default function JiraSupportComponent() {
|
export default function JiraSupportComponent() {
|
||||||
useScript();
|
//useScript();
|
||||||
|
|
||||||
return <div></div>;
|
return <div></div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useScript = () => {
|
// const useScript = () => {
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
const script = document.createElement("script");
|
// const script = document.createElement("script");
|
||||||
script.src = "https://jsd-widget.atlassian.com/assets/embed.js";
|
// script.src = "https://jsd-widget.atlassian.com/assets/embed.js";
|
||||||
script.setAttribute("data-jsd-embedded", true);
|
// script.setAttribute("data-jsd-embedded", true);
|
||||||
script.setAttribute("data-key", "d69bb65c-1dd3-483f-b109-66a970d03f44");
|
// script.setAttribute("data-key", "d69bb65c-1dd3-483f-b109-66a970d03f44");
|
||||||
script.setAttribute("data-base-url", "https://jsd-widget.atlassian.com");
|
// script.setAttribute("data-base-url", "https://jsd-widget.atlassian.com");
|
||||||
//script.async = true;
|
// //script.async = true;
|
||||||
script.onload = () => {
|
// script.onload = () => {
|
||||||
var DOMContentLoaded_event = document.createEvent("Event");
|
// var DOMContentLoaded_event = document.createEvent("Event");
|
||||||
DOMContentLoaded_event.initEvent("DOMContentLoaded", true, true);
|
// DOMContentLoaded_event.initEvent("DOMContentLoaded", true, true);
|
||||||
window.document.dispatchEvent(DOMContentLoaded_event);
|
// window.document.dispatchEvent(DOMContentLoaded_event);
|
||||||
};
|
// };
|
||||||
document.head.appendChild(script);
|
// document.head.appendChild(script);
|
||||||
|
|
||||||
return () => {
|
// return () => {
|
||||||
document.head.removeChild(script);
|
// document.head.removeChild(script);
|
||||||
};
|
// };
|
||||||
}, []);
|
// }, []);
|
||||||
};
|
// };
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export default function JobDetailCardsNotesComponent({ loading, data }) {
|
|||||||
bordered
|
bordered
|
||||||
dataSource={data.notes}
|
dataSource={data.notes}
|
||||||
renderItem={(item) => (
|
renderItem={(item) => (
|
||||||
<List.Item>
|
<List.Item style={{ whiteSpace: "pre-line" }}>
|
||||||
{item.critical ? (
|
{item.critical ? (
|
||||||
<EyeInvisibleFilled style={{ margin: 4, color: "red" }} />
|
<EyeInvisibleFilled style={{ margin: 4, color: "red" }} />
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { createStructuredSelector } from "reselect";
|
|||||||
import { DELETE_JOB_LINE_BY_PK } from "../../graphql/jobs-lines.queries";
|
import { DELETE_JOB_LINE_BY_PK } from "../../graphql/jobs-lines.queries";
|
||||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
|
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||||
import { onlyUnique } from "../../utils/arrayHelper";
|
import { onlyUnique } from "../../utils/arrayHelper";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
import { alphaSort } from "../../utils/sorters";
|
||||||
@@ -37,6 +38,7 @@ import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.con
|
|||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly,
|
||||||
|
technician: selectTechnician,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
@@ -48,6 +50,7 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
|
|
||||||
export function JobLinesComponent({
|
export function JobLinesComponent({
|
||||||
jobRO,
|
jobRO,
|
||||||
|
technician,
|
||||||
setPartsOrderContext,
|
setPartsOrderContext,
|
||||||
loading,
|
loading,
|
||||||
refetch,
|
refetch,
|
||||||
@@ -366,7 +369,8 @@ export function JobLinesComponent({
|
|||||||
disabled={
|
disabled={
|
||||||
(job && !job.converted) ||
|
(job && !job.converted) ||
|
||||||
(selectedLines.length > 0 ? false : true) ||
|
(selectedLines.length > 0 ? false : true) ||
|
||||||
jobRO
|
jobRO ||
|
||||||
|
technician
|
||||||
}
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPartsOrderContext({
|
setPartsOrderContext({
|
||||||
@@ -401,7 +405,7 @@ export function JobLinesComponent({
|
|||||||
<Button>{t("jobs.actions.mark")}</Button>
|
<Button>{t("jobs.actions.mark")}</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
<Button
|
<Button
|
||||||
disabled={jobRO}
|
disabled={jobRO || technician}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setJobLineEditContext({
|
setJobLineEditContext({
|
||||||
actions: { refetch: refetch },
|
actions: { refetch: refetch },
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import { selectJobReadOnly } from "../../redux/application/application.selectors
|
|||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
import DataLabel from "../data-label/data-label.component";
|
import DataLabel from "../data-label/data-label.component";
|
||||||
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
||||||
@@ -40,14 +40,14 @@ export function JobPayments({
|
|||||||
});
|
});
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t("payments.fields.created_at"),
|
title: t("payments.fields.date"),
|
||||||
dataIndex: "created_at",
|
dataIndex: "date",
|
||||||
key: "created_at",
|
key: "date",
|
||||||
|
sorter: (a, b) => dateSort(a.date, b.date),
|
||||||
|
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "created_at" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "date" && state.sortedInfo.order,
|
||||||
render: (text, record) => (
|
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
|
||||||
<DateTimeFormatter>{record.created_at}</DateTimeFormatter>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("payments.fields.payer"),
|
title: t("payments.fields.payer"),
|
||||||
|
|||||||
@@ -104,7 +104,10 @@ export function JobsAvailableContainer({ bodyshop, currentUser }) {
|
|||||||
).data;
|
).data;
|
||||||
|
|
||||||
let existingVehicles;
|
let existingVehicles;
|
||||||
if (estData.data.available_jobs_by_pk.est_data.vehicle) {
|
if (
|
||||||
|
estData.data.available_jobs_by_pk.est_data.vehicle &&
|
||||||
|
estData.data.available_jobs_by_pk.est_data.vin
|
||||||
|
) {
|
||||||
//There's vehicle data, need to double check the VIN.
|
//There's vehicle data, need to double check the VIN.
|
||||||
existingVehicles = await client.query({
|
existingVehicles = await client.query({
|
||||||
query: SEARCH_VEHICLE_BY_VIN,
|
query: SEARCH_VEHICLE_BY_VIN,
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import Axios from "axios";
|
import Axios from "axios";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import {
|
import { INSERT_NEW_JOB, QUERY_JOB_FOR_DUPE } from "../../graphql/jobs.queries";
|
||||||
INSERT_NEW_JOB,
|
|
||||||
QUERY_ALL_JOB_FIELDS,
|
|
||||||
} from "../../graphql/jobs.queries";
|
|
||||||
|
|
||||||
export default async function DuplicateJob(
|
export default async function DuplicateJob(
|
||||||
apolloClient,
|
apolloClient,
|
||||||
@@ -18,33 +15,21 @@ export default async function DuplicateJob(
|
|||||||
const { defaultOpenStatus } = config;
|
const { defaultOpenStatus } = config;
|
||||||
//get a list of all fields on the job
|
//get a list of all fields on the job
|
||||||
const res = await apolloClient.query({
|
const res = await apolloClient.query({
|
||||||
query: QUERY_ALL_JOB_FIELDS,
|
query: QUERY_JOB_FOR_DUPE,
|
||||||
variables: { id: jobId },
|
variables: { id: jobId },
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("res", res);
|
console.log("res", res);
|
||||||
const { jobs_by_pk: existingJob } = res.data;
|
const { jobs_by_pk } = res.data;
|
||||||
|
const existingJob = _.cloneDeep(jobs_by_pk);
|
||||||
|
delete existingJob.__typename;
|
||||||
|
delete existingJob.id;
|
||||||
|
delete existingJob.createdat;
|
||||||
|
delete existingJob.updatedat;
|
||||||
|
|
||||||
const newJob = {
|
const newJob = {
|
||||||
date_estimated: new Date(),
|
...existingJob,
|
||||||
shopid: existingJob.shopid,
|
|
||||||
status: defaultOpenStatus,
|
status: defaultOpenStatus,
|
||||||
ownerid: existingJob.ownerid,
|
|
||||||
ownr_fn: existingJob.ownr_fn,
|
|
||||||
ownr_ln: existingJob.ownr_ln,
|
|
||||||
ownr_co_nm: existingJob.ownr_co_nm,
|
|
||||||
ownr_addr1: existingJob.ownr_addr1,
|
|
||||||
ownr_addr2: existingJob.ownr_addr2,
|
|
||||||
ownr_st: existingJob.ownr_st,
|
|
||||||
ownr_zip: existingJob.ownr_zip,
|
|
||||||
ownr_ctry: existingJob.ownr_ctry,
|
|
||||||
|
|
||||||
ownr_ph1: existingJob.ownr_ph1,
|
|
||||||
vehicleid: existingJob.vehicleid,
|
|
||||||
v_vin: existingJob.v_vin,
|
|
||||||
v_make_desc: existingJob.v_make_desc,
|
|
||||||
v_model_desc: existingJob.v_model_desc,
|
|
||||||
v_model_yr: existingJob.v_model_yr,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const _tempLines = _.cloneDeep(existingJob.joblines);
|
const _tempLines = _.cloneDeep(existingJob.joblines);
|
||||||
@@ -54,24 +39,20 @@ export default async function DuplicateJob(
|
|||||||
line.manual_line = true;
|
line.manual_line = true;
|
||||||
});
|
});
|
||||||
newJob.joblines = keepJobLines ? _tempLines : [];
|
newJob.joblines = keepJobLines ? _tempLines : [];
|
||||||
newJob.job_totals = (
|
|
||||||
await Axios.post("/job/totals", {
|
|
||||||
job: newJob,
|
|
||||||
})
|
|
||||||
).data;
|
|
||||||
|
|
||||||
delete newJob.joblines;
|
delete newJob.joblines;
|
||||||
newJob.joblines = keepJobLines ? { data: _tempLines } : null;
|
newJob.joblines = keepJobLines ? { data: _tempLines } : null;
|
||||||
|
|
||||||
apolloClient
|
const res2 = await apolloClient.mutate({
|
||||||
.mutate({
|
mutation: INSERT_NEW_JOB,
|
||||||
mutation: INSERT_NEW_JOB,
|
variables: { job: [newJob] },
|
||||||
variables: { job: [newJob] },
|
});
|
||||||
})
|
await Axios.post("/job/totalsssu", {
|
||||||
.then((res2) => {
|
id: res2.data.insert_jobs.returning[0].id,
|
||||||
if (completionCallback)
|
});
|
||||||
completionCallback(res2.data.insert_jobs.returning[0].id);
|
|
||||||
});
|
if (completionCallback)
|
||||||
|
completionCallback(res2.data.insert_jobs.returning[0].id);
|
||||||
|
|
||||||
//insert the new job. call the callback with the returned ID when done.
|
//insert the new job. call the callback with the returned ID when done.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { DetermineFileType } from "../documents-upload/documents-upload.utility";
|
||||||
|
|
||||||
|
export const GenerateSrcUrl = (value) => {
|
||||||
|
let extension = value.extension;
|
||||||
|
if (extension && extension.toLowerCase().includes("heic")) extension = "jpg";
|
||||||
|
|
||||||
|
return `${process.env.REACT_APP_CLOUDINARY_ENDPOINT}/${DetermineFileType(
|
||||||
|
value.type
|
||||||
|
)}/upload/${value.key}${extension ? `.${extension}` : ""}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GenerateThumbUrl = (value) =>
|
||||||
|
`${process.env.REACT_APP_CLOUDINARY_ENDPOINT}/${DetermineFileType(
|
||||||
|
value.type
|
||||||
|
)}/upload/${process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS}/${
|
||||||
|
value.key
|
||||||
|
}`;
|
||||||
@@ -14,11 +14,11 @@ export default function JobsDocumentsDownloadButton({
|
|||||||
const [download, setDownload] = useState(null);
|
const [download, setDownload] = useState(null);
|
||||||
const imagesToDownload = [
|
const imagesToDownload = [
|
||||||
...galleryImages.images.filter((image) => image.isSelected),
|
...galleryImages.images.filter((image) => image.isSelected),
|
||||||
...galleryImages.other.filter((image) => image.isSelected),
|
// ...galleryImages.other.filter((image) => image.isSelected),
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleDownload = () => {
|
const handleDownload = () => {
|
||||||
logImEXEvent("jobs_documents_download");
|
logImEXEvent("jobs_documents_download");
|
||||||
|
|
||||||
axios
|
axios
|
||||||
.post("/media/download", {
|
.post("/media/download", {
|
||||||
ids: imagesToDownload.map((_) => _.key),
|
ids: imagesToDownload.map((_) => _.key),
|
||||||
@@ -27,12 +27,8 @@ export default function JobsDocumentsDownloadButton({
|
|||||||
// window.open(r.data);
|
// window.open(r.data);
|
||||||
downloadAs(
|
downloadAs(
|
||||||
r.data,
|
r.data,
|
||||||
`${identifier || "images"}.zip`,
|
`${identifier || "documents"}.zip`,
|
||||||
(progressEvent) => {
|
(progressEvent) => {
|
||||||
const percentage = Math.round(
|
|
||||||
(progressEvent.loaded * 100) / progressEvent.total
|
|
||||||
);
|
|
||||||
console.log(progressEvent, percentage);
|
|
||||||
setDownload((currentDownloadState) => {
|
setDownload((currentDownloadState) => {
|
||||||
return {
|
return {
|
||||||
downloaded: progressEvent.loaded || 0,
|
downloaded: progressEvent.loaded || 0,
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { FileExcelFilled } from "@ant-design/icons";
|
import { FileExcelFilled, EditFilled, SyncOutlined } from "@ant-design/icons";
|
||||||
import { Card, Col, Row, Space } from "antd";
|
import { Card, Col, Row, Space, Button } from "antd";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Gallery from "react-grid-gallery";
|
import Gallery from "react-grid-gallery";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import DocumentsUploadComponent from "../documents-upload/documents-upload.component";
|
import DocumentsUploadComponent from "../documents-upload/documents-upload.component";
|
||||||
import { DetermineFileType } from "../documents-upload/documents-upload.utility";
|
import { DetermineFileType } from "../documents-upload/documents-upload.utility";
|
||||||
|
import { GenerateSrcUrl, GenerateThumbUrl } from "./job-documents.utility";
|
||||||
import JobsDocumentsDownloadButton from "./jobs-document-gallery.download.component";
|
import JobsDocumentsDownloadButton from "./jobs-document-gallery.download.component";
|
||||||
import JobsDocumentsGalleryReassign from "./jobs-document-gallery.reassign.component";
|
import JobsDocumentsGalleryReassign from "./jobs-document-gallery.reassign.component";
|
||||||
import JobsDocumentsDeleteButton from "./jobs-documents-gallery.delete.component";
|
import JobsDocumentsDeleteButton from "./jobs-documents-gallery.delete.component";
|
||||||
@@ -22,6 +23,25 @@ function JobsDocumentsComponent({
|
|||||||
}) {
|
}) {
|
||||||
const [galleryImages, setgalleryImages] = useState({ images: [], other: [] });
|
const [galleryImages, setgalleryImages] = useState({ images: [], other: [] });
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [index, setIndex] = useState(0);
|
||||||
|
|
||||||
|
const onCurrentImageChange = (index) => {
|
||||||
|
setIndex(index);
|
||||||
|
};
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// console.log("Added event listening for reteching.");
|
||||||
|
// window.addEventListener("storage", (ev) => {
|
||||||
|
// if (ev.key === "refetch" && ev.newValue === true) {
|
||||||
|
// refetch && refetch();
|
||||||
|
// localStorage.setItem("refetch", false);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// return () => {
|
||||||
|
// window.removeEventListener("storage");
|
||||||
|
// };
|
||||||
|
// }, [refetch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let documents = data.reduce(
|
let documents = data.reduce(
|
||||||
@@ -29,21 +49,14 @@ function JobsDocumentsComponent({
|
|||||||
const fileType = DetermineFileType(value.type);
|
const fileType = DetermineFileType(value.type);
|
||||||
if (value.type.startsWith("image")) {
|
if (value.type.startsWith("image")) {
|
||||||
acc.images.push({
|
acc.images.push({
|
||||||
src: `${
|
src: GenerateSrcUrl(value),
|
||||||
process.env.REACT_APP_CLOUDINARY_ENDPOINT
|
thumbnail: GenerateThumbUrl(value),
|
||||||
}/${DetermineFileType(value.type)}/upload/${value.key}${
|
|
||||||
value.extension ? `.${value.extension}` : ""
|
|
||||||
}`,
|
|
||||||
thumbnail: `${
|
|
||||||
process.env.REACT_APP_CLOUDINARY_ENDPOINT
|
|
||||||
}/${DetermineFileType(value.type)}/upload/${
|
|
||||||
process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS
|
|
||||||
}/${value.key}`,
|
|
||||||
thumbnailHeight: 225,
|
thumbnailHeight: 225,
|
||||||
thumbnailWidth: 225,
|
thumbnailWidth: 225,
|
||||||
isSelected: false,
|
isSelected: false,
|
||||||
key: value.key,
|
key: value.key,
|
||||||
extension: value.extension,
|
extension: value.extension,
|
||||||
|
|
||||||
id: value.id,
|
id: value.id,
|
||||||
type: value.type,
|
type: value.type,
|
||||||
size: value.size,
|
size: value.size,
|
||||||
@@ -52,28 +65,18 @@ function JobsDocumentsComponent({
|
|||||||
} else {
|
} else {
|
||||||
let thumb;
|
let thumb;
|
||||||
switch (fileType) {
|
switch (fileType) {
|
||||||
case "video":
|
|
||||||
thumb = `${process.env.REACT_APP_CLOUDINARY_ENDPOINT}/${fileType}/upload/${process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS}/${value.key}`;
|
|
||||||
break;
|
|
||||||
case "raw":
|
case "raw":
|
||||||
thumb = `${window.location.origin}/file.png`;
|
thumb = `${window.location.origin}/file.png`;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
thumb = `${
|
thumb = GenerateThumbUrl(value);
|
||||||
process.env.REACT_APP_CLOUDINARY_ENDPOINT
|
|
||||||
}/${fileType}/upload/${
|
|
||||||
process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS
|
|
||||||
}/${value.key}${value.extension ? `.${value.extension}` : ""}`;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileName = value.key.split("/").pop();
|
const fileName = value.key.split("/").pop();
|
||||||
acc.other.push({
|
acc.other.push({
|
||||||
src: `${
|
source: GenerateSrcUrl(value),
|
||||||
process.env.REACT_APP_CLOUDINARY_ENDPOINT
|
src: "",
|
||||||
}/${fileType}/upload/${value.key}${
|
|
||||||
value.extension ? `.${value.extension}` : ""
|
|
||||||
}`,
|
|
||||||
thumbnail: thumb,
|
thumbnail: thumb,
|
||||||
tags: [
|
tags: [
|
||||||
{
|
{
|
||||||
@@ -99,6 +102,7 @@ function JobsDocumentsComponent({
|
|||||||
thumbnailHeight: 225,
|
thumbnailHeight: 225,
|
||||||
thumbnailWidth: 225,
|
thumbnailWidth: 225,
|
||||||
isSelected: false,
|
isSelected: false,
|
||||||
|
|
||||||
extension: value.extension,
|
extension: value.extension,
|
||||||
key: value.key,
|
key: value.key,
|
||||||
id: value.id,
|
id: value.id,
|
||||||
@@ -119,6 +123,9 @@ function JobsDocumentsComponent({
|
|||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
|
<Button onClick={() => refetch && refetch()}>
|
||||||
|
<SyncOutlined />
|
||||||
|
</Button>
|
||||||
<JobsDocumentsGallerySelectAllComponent
|
<JobsDocumentsGallerySelectAllComponent
|
||||||
galleryImages={galleryImages}
|
galleryImages={galleryImages}
|
||||||
setGalleryImages={setgalleryImages}
|
setGalleryImages={setgalleryImages}
|
||||||
@@ -153,6 +160,26 @@ function JobsDocumentsComponent({
|
|||||||
<Gallery
|
<Gallery
|
||||||
images={galleryImages.images}
|
images={galleryImages.images}
|
||||||
backdropClosesModal={true}
|
backdropClosesModal={true}
|
||||||
|
currentImageWillChange={onCurrentImageChange}
|
||||||
|
customControls={[
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
float: "right",
|
||||||
|
|
||||||
|
zIndex: "5",
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
const newWindow = window.open(
|
||||||
|
`${window.location.protocol}//${window.location.host}/edit?documentId=${galleryImages.images[index].id}`,
|
||||||
|
"_blank",
|
||||||
|
"noopener,noreferrer"
|
||||||
|
);
|
||||||
|
if (newWindow) newWindow.opener = null;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<EditFilled style={{}} />
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
onClickImage={(props) => {
|
onClickImage={(props) => {
|
||||||
window.open(
|
window.open(
|
||||||
props.target.src,
|
props.target.src,
|
||||||
@@ -187,7 +214,7 @@ function JobsDocumentsComponent({
|
|||||||
}}
|
}}
|
||||||
onClickThumbnail={(index) => {
|
onClickThumbnail={(index) => {
|
||||||
window.open(
|
window.open(
|
||||||
galleryImages.other[index].src,
|
galleryImages.other[index].source,
|
||||||
"_blank",
|
"_blank",
|
||||||
"toolbar=0,location=0,menubar=0"
|
"toolbar=0,location=0,menubar=0"
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ export function JobNotesComponent({
|
|||||||
dataIndex: "text",
|
dataIndex: "text",
|
||||||
key: "text",
|
key: "text",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
render: (text, record) => (
|
||||||
|
<span style={{ whiteSpace: "pre-line" }}>{text}</span>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -237,6 +237,11 @@ export function PartsOrderListTableComponent({
|
|||||||
<DateFormatter>{record.deliver_by}</DateFormatter>
|
<DateFormatter>{record.deliver_by}</DateFormatter>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t("parts_orders.fields.orderedby"),
|
||||||
|
dataIndex: "orderedby",
|
||||||
|
key: "orderedby",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("general.labels.actions"),
|
title: t("general.labels.actions"),
|
||||||
dataIndex: "actions",
|
dataIndex: "actions",
|
||||||
@@ -336,6 +341,7 @@ export function PartsOrderListTableComponent({
|
|||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
title: t("general.labels.actions"),
|
title: t("general.labels.actions"),
|
||||||
dataIndex: "actions",
|
dataIndex: "actions",
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ export default function PartsOrderModalComponent({
|
|||||||
<Form.Item required={false} key={field.key}>
|
<Form.Item required={false} key={field.key}>
|
||||||
<LayoutFormRow grow noDivider>
|
<LayoutFormRow grow noDivider>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
span={8}
|
||||||
label={t("parts_orders.fields.line_desc")}
|
label={t("parts_orders.fields.line_desc")}
|
||||||
key={`${index}line_desc`}
|
key={`${index}line_desc`}
|
||||||
name={[field.name, "line_desc"]}
|
name={[field.name, "line_desc"]}
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ export function PartsOrderModalContainer({
|
|||||||
po: [
|
po: [
|
||||||
{
|
{
|
||||||
...values,
|
...values,
|
||||||
|
orderedby: currentUser.email,
|
||||||
jobid: jobId,
|
jobid: jobId,
|
||||||
user_email: currentUser.email,
|
user_email: currentUser.email,
|
||||||
return: isReturn,
|
return: isReturn,
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export const QUERY_BILLS_BY_JOBID = gql`
|
|||||||
order_date
|
order_date
|
||||||
deliver_by
|
deliver_by
|
||||||
return
|
return
|
||||||
|
orderedby
|
||||||
parts_order_lines {
|
parts_order_lines {
|
||||||
id
|
id
|
||||||
act_price
|
act_price
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
import { gql } from "@apollo/client";
|
import { gql } from "@apollo/client";
|
||||||
|
|
||||||
|
export const GET_DOCUMENT_BY_PK = gql`
|
||||||
|
query GET_DOCUMENT_BY_PK($documentId: uuid!) {
|
||||||
|
documents_by_pk(id: $documentId) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
key
|
||||||
|
type
|
||||||
|
size
|
||||||
|
takenat
|
||||||
|
extension
|
||||||
|
jobid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const GET_DOCUMENTS_BY_JOB = gql`
|
export const GET_DOCUMENTS_BY_JOB = gql`
|
||||||
query GET_DOCUMENTS_BY_JOB($jobId: uuid!) {
|
query GET_DOCUMENTS_BY_JOB($jobId: uuid!) {
|
||||||
jobs_by_pk(id: $jobId) {
|
jobs_by_pk(id: $jobId) {
|
||||||
|
|||||||
@@ -565,6 +565,7 @@ export const GET_JOB_BY_PK = gql`
|
|||||||
stripeid
|
stripeid
|
||||||
transactionid
|
transactionid
|
||||||
memo
|
memo
|
||||||
|
date
|
||||||
}
|
}
|
||||||
cccontracts {
|
cccontracts {
|
||||||
id
|
id
|
||||||
@@ -1056,6 +1057,236 @@ export const SEARCH_FOR_JOBS = gql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const QUERY_JOB_FOR_DUPE = gql`
|
||||||
|
query QUERY_JOB_FOR_DUPE($id: uuid!) {
|
||||||
|
jobs_by_pk(id: $id) {
|
||||||
|
id
|
||||||
|
agt_addr1
|
||||||
|
agt_addr2
|
||||||
|
agt_city
|
||||||
|
agt_co_id
|
||||||
|
agt_co_nm
|
||||||
|
agt_ct_fn
|
||||||
|
agt_ct_ln
|
||||||
|
agt_ct_ph
|
||||||
|
agt_ct_phx
|
||||||
|
agt_ctry
|
||||||
|
agt_ea
|
||||||
|
agt_faxx
|
||||||
|
agt_fax
|
||||||
|
agt_lic_no
|
||||||
|
agt_ph1
|
||||||
|
agt_ph1x
|
||||||
|
agt_ph2
|
||||||
|
agt_zip
|
||||||
|
agt_st
|
||||||
|
agt_ph2x
|
||||||
|
area_of_damage
|
||||||
|
cat_no
|
||||||
|
cieca_stl
|
||||||
|
cieca_ttl
|
||||||
|
clm_addr1
|
||||||
|
clm_addr2
|
||||||
|
clm_city
|
||||||
|
clm_ct_fn
|
||||||
|
clm_ct_ln
|
||||||
|
clm_ct_ph
|
||||||
|
clm_ct_phx
|
||||||
|
clm_ctry
|
||||||
|
clm_ea
|
||||||
|
clm_fax
|
||||||
|
clm_faxx
|
||||||
|
clm_ofc_id
|
||||||
|
clm_ofc_nm
|
||||||
|
clm_ph1
|
||||||
|
clm_ph1x
|
||||||
|
clm_ph2
|
||||||
|
clm_ph2x
|
||||||
|
clm_st
|
||||||
|
clm_title
|
||||||
|
clm_total
|
||||||
|
clm_zip
|
||||||
|
cust_pr
|
||||||
|
est_addr1
|
||||||
|
est_addr2
|
||||||
|
est_city
|
||||||
|
est_co_nm
|
||||||
|
est_ct_fn
|
||||||
|
est_ct_ln
|
||||||
|
est_ctry
|
||||||
|
est_ea
|
||||||
|
est_ph1
|
||||||
|
est_st
|
||||||
|
est_zip
|
||||||
|
federal_tax_rate
|
||||||
|
ins_addr1
|
||||||
|
ins_addr2
|
||||||
|
ins_city
|
||||||
|
ins_co_id
|
||||||
|
ins_co_nm
|
||||||
|
ins_ct_fn
|
||||||
|
ins_ct_ln
|
||||||
|
ins_ct_ph
|
||||||
|
ins_ct_phx
|
||||||
|
ins_ctry
|
||||||
|
ins_ea
|
||||||
|
ins_fax
|
||||||
|
ins_faxx
|
||||||
|
ins_memo
|
||||||
|
ins_ph1
|
||||||
|
ins_ph1x
|
||||||
|
ins_ph2
|
||||||
|
ins_ph2x
|
||||||
|
ins_st
|
||||||
|
ins_title
|
||||||
|
ins_zip
|
||||||
|
insd_addr1
|
||||||
|
insd_addr2
|
||||||
|
insd_city
|
||||||
|
insd_co_nm
|
||||||
|
insd_ctry
|
||||||
|
insd_ea
|
||||||
|
insd_fax
|
||||||
|
insd_faxx
|
||||||
|
insd_fn
|
||||||
|
insd_ln
|
||||||
|
insd_ph1
|
||||||
|
insd_ph1x
|
||||||
|
insd_ph2
|
||||||
|
insd_ph2x
|
||||||
|
insd_st
|
||||||
|
insd_title
|
||||||
|
insd_zip
|
||||||
|
labor_rate_desc
|
||||||
|
labor_rate_id
|
||||||
|
local_tax_rate
|
||||||
|
other_amount_payable
|
||||||
|
owner_owing
|
||||||
|
ownerid
|
||||||
|
ownr_addr1
|
||||||
|
ownr_addr2
|
||||||
|
ownr_city
|
||||||
|
ownr_co_nm
|
||||||
|
ownr_ctry
|
||||||
|
ownr_ea
|
||||||
|
ownr_fax
|
||||||
|
ownr_faxx
|
||||||
|
ownr_fn
|
||||||
|
ownr_ln
|
||||||
|
ownr_ph1
|
||||||
|
ownr_ph1x
|
||||||
|
ownr_ph2
|
||||||
|
ownr_ph2x
|
||||||
|
ownr_st
|
||||||
|
ownr_title
|
||||||
|
ownr_zip
|
||||||
|
parts_tax_rates
|
||||||
|
plate_no
|
||||||
|
plate_st
|
||||||
|
po_number
|
||||||
|
rate_la1
|
||||||
|
rate_la2
|
||||||
|
rate_la3
|
||||||
|
rate_la4
|
||||||
|
rate_laa
|
||||||
|
rate_lab
|
||||||
|
rate_lad
|
||||||
|
rate_lae
|
||||||
|
rate_lag
|
||||||
|
rate_laf
|
||||||
|
rate_lam
|
||||||
|
rate_lar
|
||||||
|
rate_las
|
||||||
|
rate_lau
|
||||||
|
rate_ma2s
|
||||||
|
rate_ma2t
|
||||||
|
rate_ma3s
|
||||||
|
rate_mabl
|
||||||
|
rate_macs
|
||||||
|
rate_mahw
|
||||||
|
rate_mapa
|
||||||
|
rate_mash
|
||||||
|
rate_matd
|
||||||
|
regie_number
|
||||||
|
selling_dealer
|
||||||
|
selling_dealer_contact
|
||||||
|
servicing_dealer
|
||||||
|
servicing_dealer_contact
|
||||||
|
shopid
|
||||||
|
|
||||||
|
state_tax_rate
|
||||||
|
|
||||||
|
tax_lbr_rt
|
||||||
|
tax_levies_rt
|
||||||
|
tax_paint_mat_rt
|
||||||
|
tax_predis
|
||||||
|
tax_prethr
|
||||||
|
tax_pstthr
|
||||||
|
tax_str_rt
|
||||||
|
tax_sub_rt
|
||||||
|
tax_thramt
|
||||||
|
tax_tow_rt
|
||||||
|
unit_number
|
||||||
|
v_color
|
||||||
|
v_make_desc
|
||||||
|
v_model_desc
|
||||||
|
v_model_yr
|
||||||
|
v_vin
|
||||||
|
vehicleid
|
||||||
|
joblines(where: { removed: { _eq: false } }) {
|
||||||
|
act_price
|
||||||
|
alt_co_id
|
||||||
|
alt_overrd
|
||||||
|
alt_part_i
|
||||||
|
alt_partm
|
||||||
|
bett_amt
|
||||||
|
alt_partno
|
||||||
|
bett_pctg
|
||||||
|
bett_tax
|
||||||
|
bett_type
|
||||||
|
cert_part
|
||||||
|
db_hrs
|
||||||
|
db_price
|
||||||
|
db_ref
|
||||||
|
est_seq
|
||||||
|
glass_flag
|
||||||
|
id
|
||||||
|
lbr_amt
|
||||||
|
lbr_hrs_j
|
||||||
|
lbr_inc
|
||||||
|
lbr_op
|
||||||
|
lbr_op_j
|
||||||
|
lbr_tax
|
||||||
|
lbr_typ_j
|
||||||
|
line_desc
|
||||||
|
line_ind
|
||||||
|
line_ref
|
||||||
|
misc_amt
|
||||||
|
misc_sublt
|
||||||
|
misc_tax
|
||||||
|
mod_lb_hrs
|
||||||
|
mod_lbr_ty
|
||||||
|
oem_partno
|
||||||
|
op_code_desc
|
||||||
|
paint_stg
|
||||||
|
paint_tone
|
||||||
|
part_qty
|
||||||
|
part_type
|
||||||
|
price_inc
|
||||||
|
price_j
|
||||||
|
prt_dsmk_m
|
||||||
|
prt_dsmk_p
|
||||||
|
status
|
||||||
|
tax_part
|
||||||
|
unq_seq
|
||||||
|
manual_line
|
||||||
|
}
|
||||||
|
driveable
|
||||||
|
towin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const QUERY_ALL_JOB_FIELDS = gql`
|
export const QUERY_ALL_JOB_FIELDS = gql`
|
||||||
query QUERY_ALL_JOB_FIELDS($id: uuid!) {
|
query QUERY_ALL_JOB_FIELDS($id: uuid!) {
|
||||||
jobs_by_pk(id: $id) {
|
jobs_by_pk(id: $id) {
|
||||||
|
|||||||
@@ -164,6 +164,16 @@ export function* onSignInSuccess() {
|
|||||||
|
|
||||||
export function* signInSuccessSaga({ payload }) {
|
export function* signInSuccessSaga({ payload }) {
|
||||||
LogRocket.identify(payload.email);
|
LogRocket.identify(payload.email);
|
||||||
|
try {
|
||||||
|
window.$crisp.push(["set", "user:email", [payload.email]]);
|
||||||
|
window.$crisp.push([
|
||||||
|
"set",
|
||||||
|
"user:nickname",
|
||||||
|
[payload.displayName || payload.email],
|
||||||
|
]);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error updating Crisp settings.", error);
|
||||||
|
}
|
||||||
|
|
||||||
// if (!payload.email.includes("@imex.")) yield put(setInstanceId(payload.uid));
|
// if (!payload.email.includes("@imex.")) yield put(setInstanceId(payload.uid));
|
||||||
analytics.setUserId(payload.email);
|
analytics.setUserId(payload.email);
|
||||||
@@ -220,6 +230,8 @@ export function* SetAuthLevelFromShopDetails({ payload }) {
|
|||||||
try {
|
try {
|
||||||
const userEmail = yield select((state) => state.user.currentUser.email);
|
const userEmail = yield select((state) => state.user.currentUser.email);
|
||||||
|
|
||||||
|
window.$crisp.push(["set", "user:company", [payload.shopname]]);
|
||||||
|
|
||||||
const authRecord = payload.associations.filter(
|
const authRecord = payload.associations.filter(
|
||||||
(a) => a.useremail === userEmail
|
(a) => a.useremail === userEmail
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -677,7 +677,7 @@
|
|||||||
"documents": {
|
"documents": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"delete": "Delete Selected Documents",
|
"delete": "Delete Selected Documents",
|
||||||
"download": "Download Selected Documents",
|
"download": "Download Selected Images",
|
||||||
"reassign": "Reassign to another Job",
|
"reassign": "Reassign to another Job",
|
||||||
"selectallimages": "Select All Images",
|
"selectallimages": "Select All Images",
|
||||||
"selectallotherdocuments": "Select All Other Documents"
|
"selectallotherdocuments": "Select All Other Documents"
|
||||||
@@ -1420,6 +1420,7 @@
|
|||||||
"entertimeticket": "Enter Time Tickets",
|
"entertimeticket": "Enter Time Tickets",
|
||||||
"export": "Export",
|
"export": "Export",
|
||||||
"export-logs": "Export Logs",
|
"export-logs": "Export Logs",
|
||||||
|
"help": "Help",
|
||||||
"home": "Home",
|
"home": "Home",
|
||||||
"jobs": "Jobs",
|
"jobs": "Jobs",
|
||||||
"owners": "Owners",
|
"owners": "Owners",
|
||||||
@@ -1602,6 +1603,7 @@
|
|||||||
"oem_partno": "Part #",
|
"oem_partno": "Part #",
|
||||||
"order_date": "Order Date",
|
"order_date": "Order Date",
|
||||||
"order_number": "Order Number",
|
"order_number": "Order Number",
|
||||||
|
"orderedby": "Ordered By",
|
||||||
"quantity": "Qty.",
|
"quantity": "Qty.",
|
||||||
"return": "Return",
|
"return": "Return",
|
||||||
"status": "Status"
|
"status": "Status"
|
||||||
|
|||||||
@@ -1420,6 +1420,7 @@
|
|||||||
"entertimeticket": "",
|
"entertimeticket": "",
|
||||||
"export": "",
|
"export": "",
|
||||||
"export-logs": "",
|
"export-logs": "",
|
||||||
|
"help": "",
|
||||||
"home": "Casa",
|
"home": "Casa",
|
||||||
"jobs": "Trabajos",
|
"jobs": "Trabajos",
|
||||||
"owners": "propietarios",
|
"owners": "propietarios",
|
||||||
@@ -1602,6 +1603,7 @@
|
|||||||
"oem_partno": "",
|
"oem_partno": "",
|
||||||
"order_date": "",
|
"order_date": "",
|
||||||
"order_number": "",
|
"order_number": "",
|
||||||
|
"orderedby": "",
|
||||||
"quantity": "",
|
"quantity": "",
|
||||||
"return": "",
|
"return": "",
|
||||||
"status": ""
|
"status": ""
|
||||||
|
|||||||
@@ -1420,6 +1420,7 @@
|
|||||||
"entertimeticket": "",
|
"entertimeticket": "",
|
||||||
"export": "",
|
"export": "",
|
||||||
"export-logs": "",
|
"export-logs": "",
|
||||||
|
"help": "",
|
||||||
"home": "Accueil",
|
"home": "Accueil",
|
||||||
"jobs": "Emplois",
|
"jobs": "Emplois",
|
||||||
"owners": "Propriétaires",
|
"owners": "Propriétaires",
|
||||||
@@ -1602,6 +1603,7 @@
|
|||||||
"oem_partno": "",
|
"oem_partno": "",
|
||||||
"order_date": "",
|
"order_date": "",
|
||||||
"order_number": "",
|
"order_number": "",
|
||||||
|
"orderedby": "",
|
||||||
"quantity": "",
|
"quantity": "",
|
||||||
"return": "",
|
"return": "",
|
||||||
"status": ""
|
"status": ""
|
||||||
|
|||||||
@@ -230,6 +230,14 @@ export const TemplateList = (type, context) => {
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
group: "financial",
|
group: "financial",
|
||||||
},
|
},
|
||||||
|
ro_totals: {
|
||||||
|
title: i18n.t("printcenter.jobs.ro_totals"),
|
||||||
|
description: "All Jobs Notes",
|
||||||
|
subject: i18n.t("printcenter.jobs.ro_totals"),
|
||||||
|
key: "ro_totals",
|
||||||
|
disabled: false,
|
||||||
|
group: "financial",
|
||||||
|
},
|
||||||
job_costing_ro: {
|
job_costing_ro: {
|
||||||
title: i18n.t("printcenter.jobs.job_costing_ro"),
|
title: i18n.t("printcenter.jobs.job_costing_ro"),
|
||||||
description: "All Jobs Notes",
|
description: "All Jobs Notes",
|
||||||
@@ -238,6 +246,22 @@ export const TemplateList = (type, context) => {
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
group: "financial",
|
group: "financial",
|
||||||
},
|
},
|
||||||
|
purchases_by_ro_detail: {
|
||||||
|
title: i18n.t("printcenter.jobs.purchases_by_ro_detail"),
|
||||||
|
description: "All Jobs Notes",
|
||||||
|
subject: i18n.t("printcenter.jobs.purchases_by_ro_detail"),
|
||||||
|
key: "purchases_by_ro_detail",
|
||||||
|
disabled: false,
|
||||||
|
group: "financial",
|
||||||
|
},
|
||||||
|
purchases_by_ro_summary: {
|
||||||
|
title: i18n.t("printcenter.jobs.purchases_by_ro_summary"),
|
||||||
|
description: "All Jobs Notes",
|
||||||
|
subject: i18n.t("printcenter.jobs.purchases_by_ro_summary"),
|
||||||
|
key: "purchases_by_ro_summary",
|
||||||
|
disabled: false,
|
||||||
|
group: "financial",
|
||||||
|
},
|
||||||
filing_coversheet_portrait: {
|
filing_coversheet_portrait: {
|
||||||
title: i18n.t("printcenter.jobs.filing_coversheet_portrait"),
|
title: i18n.t("printcenter.jobs.filing_coversheet_portrait"),
|
||||||
description: "All Jobs Notes",
|
description: "All Jobs Notes",
|
||||||
@@ -361,6 +385,16 @@ export const TemplateList = (type, context) => {
|
|||||||
...(!type || type === "csi" ? {} : {}),
|
...(!type || type === "csi" ? {} : {}),
|
||||||
...(!type || type === "report_center"
|
...(!type || type === "report_center"
|
||||||
? {
|
? {
|
||||||
|
payments_by_date: {
|
||||||
|
title: i18n.t("reportcenter.templates.payments_by_date"),
|
||||||
|
subject: i18n.t("reportcenter.templates.payments_by_date"),
|
||||||
|
key: "payments_by_date",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.payments"),
|
||||||
|
field: i18n.t("payments.fields.date"),
|
||||||
|
},
|
||||||
|
},
|
||||||
payments_by_date_type: {
|
payments_by_date_type: {
|
||||||
title: i18n.t("reportcenter.templates.payments_by_date_type"),
|
title: i18n.t("reportcenter.templates.payments_by_date_type"),
|
||||||
subject: i18n.t("reportcenter.templates.payments_by_date_type"),
|
subject: i18n.t("reportcenter.templates.payments_by_date_type"),
|
||||||
@@ -521,7 +555,28 @@ export const TemplateList = (type, context) => {
|
|||||||
idtype: "employee",
|
idtype: "employee",
|
||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
|
attendance: {
|
||||||
|
title: i18n.t("reportcenter.templates.attendance"),
|
||||||
|
subject: i18n.t("reportcenter.templates.attendance"),
|
||||||
|
key: "attendance",
|
||||||
|
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.timetickets"),
|
||||||
|
field: i18n.t("timetickets.fields.date"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
attendance_summary: {
|
||||||
|
title: i18n.t("reportcenter.templates.attendance_summary"),
|
||||||
|
subject: i18n.t("reportcenter.templates.attendance_summary"),
|
||||||
|
key: "attendance_summary",
|
||||||
|
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.timetickets"),
|
||||||
|
field: i18n.t("timetickets.fields.date"),
|
||||||
|
},
|
||||||
|
},
|
||||||
attendance_employee: {
|
attendance_employee: {
|
||||||
title: i18n.t("reportcenter.templates.attendance_employee"),
|
title: i18n.t("reportcenter.templates.attendance_employee"),
|
||||||
subject: i18n.t("reportcenter.templates.attendance_employee"),
|
subject: i18n.t("reportcenter.templates.attendance_employee"),
|
||||||
@@ -559,15 +614,15 @@ export const TemplateList = (type, context) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
hours_sold_detail_closed_source: {
|
hours_sold_detail_closed_ins_co: {
|
||||||
title: i18n.t(
|
title: i18n.t(
|
||||||
"reportcenter.templates.hours_sold_detail_closed_source"
|
"reportcenter.templates.hours_sold_detail_closed_ins_co"
|
||||||
),
|
),
|
||||||
description: "",
|
description: "",
|
||||||
subject: i18n.t(
|
subject: i18n.t(
|
||||||
"reportcenter.templates.hours_sold_detail_closed_source"
|
"reportcenter.templates.hours_sold_detail_closed_ins_co"
|
||||||
),
|
),
|
||||||
key: "hours_sold_detail_closed_source",
|
key: "hours_sold_detail_closed_ins_co",
|
||||||
//idtype: "vendor",
|
//idtype: "vendor",
|
||||||
disabled: false,
|
disabled: false,
|
||||||
rangeFilter: {
|
rangeFilter: {
|
||||||
@@ -589,15 +644,15 @@ export const TemplateList = (type, context) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
hours_sold_summary_closed_source: {
|
hours_sold_summary_closed_ins_co: {
|
||||||
title: i18n.t(
|
title: i18n.t(
|
||||||
"reportcenter.templates.hours_sold_summary_closed_source"
|
"reportcenter.templates.hours_sold_summary_closed_ins_co"
|
||||||
),
|
),
|
||||||
description: "",
|
description: "",
|
||||||
subject: i18n.t(
|
subject: i18n.t(
|
||||||
"reportcenter.templates.hours_sold_summary_closed_source"
|
"reportcenter.templates.hours_sold_summary_closed_ins_co"
|
||||||
),
|
),
|
||||||
key: "hours_sold_summary_closed_source",
|
key: "hours_sold_summary_closed_ins_co",
|
||||||
//idtype: "vendor",
|
//idtype: "vendor",
|
||||||
disabled: false,
|
disabled: false,
|
||||||
rangeFilter: {
|
rangeFilter: {
|
||||||
@@ -619,15 +674,15 @@ export const TemplateList = (type, context) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
hours_sold_detail_open_source: {
|
hours_sold_detail_open_ins_co: {
|
||||||
title: i18n.t(
|
title: i18n.t(
|
||||||
"reportcenter.templates.hours_sold_detail_open_source"
|
"reportcenter.templates.hours_sold_detail_open_ins_co"
|
||||||
),
|
),
|
||||||
description: "",
|
description: "",
|
||||||
subject: i18n.t(
|
subject: i18n.t(
|
||||||
"reportcenter.templates.hours_sold_detail_open_source"
|
"reportcenter.templates.hours_sold_detail_open_ins_co"
|
||||||
),
|
),
|
||||||
key: "hours_sold_detail_open_source",
|
key: "hours_sold_detail_open_ins_co",
|
||||||
//idtype: "vendor",
|
//idtype: "vendor",
|
||||||
disabled: false,
|
disabled: false,
|
||||||
rangeFilter: {
|
rangeFilter: {
|
||||||
@@ -649,15 +704,15 @@ export const TemplateList = (type, context) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
hours_sold_summary_open_source: {
|
hours_sold_summary_open_ins_co: {
|
||||||
title: i18n.t(
|
title: i18n.t(
|
||||||
"reportcenter.templates.hours_sold_summary_open_source"
|
"reportcenter.templates.hours_sold_summary_open_ins_co"
|
||||||
),
|
),
|
||||||
description: "",
|
description: "",
|
||||||
subject: i18n.t(
|
subject: i18n.t(
|
||||||
"reportcenter.templates.hours_sold_summary_open_source"
|
"reportcenter.templates.hours_sold_summary_open_ins_cƒo"
|
||||||
),
|
),
|
||||||
key: "hours_sold_summary_open_source",
|
key: "hours_sold_summary_open_ins_co",
|
||||||
//idtype: "vendor",
|
//idtype: "vendor",
|
||||||
disabled: false,
|
disabled: false,
|
||||||
rangeFilter: {
|
rangeFilter: {
|
||||||
@@ -689,11 +744,11 @@ export const TemplateList = (type, context) => {
|
|||||||
field: i18n.t("jobs.fields.date_invoiced"),
|
field: i18n.t("jobs.fields.date_invoiced"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
supplement_ratio_source: {
|
supplement_ratio_ins_co: {
|
||||||
title: i18n.t("reportcenter.templates.supplement_ratio_source"),
|
title: i18n.t("reportcenter.templates.supplement_ratio_ins_co"),
|
||||||
description: "",
|
description: "",
|
||||||
subject: i18n.t("reportcenter.templates.supplement_ratio_source"),
|
subject: i18n.t("reportcenter.templates.supplement_ratio_ins_co"),
|
||||||
key: "supplement_ratio_source",
|
key: "supplement_ratio_ins_co",
|
||||||
//idtype: "vendor",
|
//idtype: "vendor",
|
||||||
disabled: false,
|
disabled: false,
|
||||||
rangeFilter: {
|
rangeFilter: {
|
||||||
@@ -727,11 +782,11 @@ export const TemplateList = (type, context) => {
|
|||||||
field: i18n.t("jobs.fields.date_invoiced"),
|
field: i18n.t("jobs.fields.date_invoiced"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
job_costing_ro_source: {
|
job_costing_ro_ins_co: {
|
||||||
title: i18n.t("reportcenter.templates.job_costing_ro_source"),
|
title: i18n.t("reportcenter.templates.job_costing_ro_ins_co"),
|
||||||
description: "",
|
description: "",
|
||||||
subject: i18n.t("reportcenter.templates.job_costing_ro_source"),
|
subject: i18n.t("reportcenter.templates.job_costing_ro_ins_co"),
|
||||||
key: "job_costing_ro_source",
|
key: "job_costing_ro_ins_co",
|
||||||
//idtype: "vendor",
|
//idtype: "vendor",
|
||||||
disabled: false,
|
disabled: false,
|
||||||
rangeFilter: {
|
rangeFilter: {
|
||||||
@@ -789,6 +844,67 @@ export const TemplateList = (type, context) => {
|
|||||||
field: i18n.t("jobs.fields.date_invoiced"),
|
field: i18n.t("jobs.fields.date_invoiced"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
gsr_by_delivery_date: {
|
||||||
|
title: i18n.t("reportcenter.templates.gsr_by_delivery_date"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t("reportcenter.templates.gsr_by_delivery_date"),
|
||||||
|
key: "gsr_by_delivery_date",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||||
|
field: i18n.t("jobs.fields.actual_delivery"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gsr_by_referral: {
|
||||||
|
title: i18n.t("reportcenter.templates.gsr_by_referral"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t("reportcenter.templates.gsr_by_referral"),
|
||||||
|
key: "gsr_by_referral",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||||
|
field: i18n.t("jobs.fields.date_invoiced"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gsr_by_ro: {
|
||||||
|
title: i18n.t("reportcenter.templates.gsr_by_ro"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t("reportcenter.templates.gsr_by_ro"),
|
||||||
|
key: "gsr_by_ro",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||||
|
field: i18n.t("jobs.fields.date_invoiced"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gsr_by_ins_co: {
|
||||||
|
title: i18n.t("reportcenter.templates.gsr_by_ins_co"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t("reportcenter.templates.gsr_by_ins_co"),
|
||||||
|
key: "gsr_by_ins_co",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||||
|
field: i18n.t("jobs.fields.date_invoiced"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gsr_by_exported_date: {
|
||||||
|
title: i18n.t("reportcenter.templates.gsr_by_exported_date"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t("reportcenter.templates.gsr_by_exported_date"),
|
||||||
|
key: "gsr_by_exported_date",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||||
|
field: i18n.t("jobs.fields.date_exported"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
gsr_by_estimator: {
|
gsr_by_estimator: {
|
||||||
title: i18n.t("reportcenter.templates.gsr_by_estimator"),
|
title: i18n.t("reportcenter.templates.gsr_by_estimator"),
|
||||||
description: "",
|
description: "",
|
||||||
@@ -825,6 +941,126 @@ export const TemplateList = (type, context) => {
|
|||||||
field: i18n.t("jobs.fields.date_open"),
|
field: i18n.t("jobs.fields.date_open"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
open_orders_estimator: {
|
||||||
|
title: i18n.t("reportcenter.templates.open_orders_estimator"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t("reportcenter.templates.open_orders_estimator"),
|
||||||
|
key: "open_orders_estimator",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||||
|
field: i18n.t("jobs.fields.date_open"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
open_orders_ins_co: {
|
||||||
|
title: i18n.t("reportcenter.templates.open_orders_ins_co"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t("reportcenter.templates.open_orders_ins_co"),
|
||||||
|
key: "open_orders_ins_co",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||||
|
field: i18n.t("jobs.fields.date_open"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
export_payables: {
|
||||||
|
title: i18n.t("reportcenter.templates.export_payables"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t("reportcenter.templates.export_payables"),
|
||||||
|
key: "export_payables",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.exportlogs"),
|
||||||
|
field: i18n.t("exportlogs.fields.createdat"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
export_payments: {
|
||||||
|
title: i18n.t("reportcenter.templates.export_payments"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t("reportcenter.templates.export_payments"),
|
||||||
|
key: "export_payments",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.exportlogs"),
|
||||||
|
field: i18n.t("exportlogs.fields.createdat"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
export_receivables: {
|
||||||
|
title: i18n.t("reportcenter.templates.export_receivables"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t("reportcenter.templates.export_receivables"),
|
||||||
|
key: "export_receivables",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.exportlogs"),
|
||||||
|
field: i18n.t("exportlogs.fields.createdat"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
parts_backorder: {
|
||||||
|
title: i18n.t("reportcenter.templates.parts_backorder"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t("reportcenter.templates.parts_backorder"),
|
||||||
|
key: "parts_backorder",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||||
|
field: i18n.t("jobs.fields.actual_in"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
thank_you_date: {
|
||||||
|
title: i18n.t("reportcenter.templates.thank_you_date"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t("reportcenter.templates.thank_you_date"),
|
||||||
|
key: "thank_you_date",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||||
|
field: i18n.t("jobs.fields.date_invoiced"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unclaimed_hrs: {
|
||||||
|
title: i18n.t("reportcenter.templates.unclaimed_hrs"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t("reportcenter.templates.unclaimed_hrs"),
|
||||||
|
key: "unclaimed_hrs",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||||
|
field: i18n.t("jobs.fields.date_open"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
work_in_progress_labour: {
|
||||||
|
title: i18n.t("reportcenter.templates.work_in_progress_labour"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t("reportcenter.templates.work_in_progress_labour"),
|
||||||
|
key: "work_in_progress_labour",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||||
|
field: i18n.t("jobs.fields.date_open"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
work_in_progress_payables: {
|
||||||
|
title: i18n.t("reportcenter.templates.work_in_progress_payables"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t("reportcenter.templates.work_in_progress_payables"),
|
||||||
|
key: "work_in_progress_payables",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||||
|
field: i18n.t("jobs.fields.date_open"),
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
...(!type || type === "courtesycarcontract"
|
...(!type || type === "courtesycarcontract"
|
||||||
|
|||||||
@@ -8302,6 +8302,11 @@ map-visit@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
object-visit "^1.0.0"
|
object-visit "^1.0.0"
|
||||||
|
|
||||||
|
markerjs2@^2.8.1:
|
||||||
|
version "2.8.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/markerjs2/-/markerjs2-2.8.1.tgz#33c455cc1edd8fa9a5e5b39ed782dcd1b923c917"
|
||||||
|
integrity sha512-M9AflvjOD5aIcBM0HZWW6u1h/NRdzfq73B9ILv1YehF88PeF0tYT5HIsi9PaSJ6EUOR/vWysZN08f3EyDCJixw==
|
||||||
|
|
||||||
material-colors@^1.2.1:
|
material-colors@^1.2.1:
|
||||||
version "1.2.6"
|
version "1.2.6"
|
||||||
resolved "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz"
|
resolved "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz"
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."vehicles" ALTER COLUMN "v_vin" SET NOT NULL;
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."vehicles" ALTER COLUMN "v_vin" DROP NOT NULL;
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."conversations" DROP COLUMN "archived";
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."conversations" ADD COLUMN "archived" boolean NOT NULL
|
||||||
|
DEFAULT false;
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."parts_orders" DROP COLUMN "orderedby";
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."parts_orders" ADD COLUMN "orderedby" text NULL;
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: alter table "public"."parts_orders" drop constraint "parts_orders_orderedby_fkey";
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: |-
|
||||||
|
alter table "public"."parts_orders"
|
||||||
|
add constraint "parts_orders_orderedby_fkey"
|
||||||
|
foreign key ("orderedby")
|
||||||
|
references "public"."users"
|
||||||
|
("email") on update set null on delete set null;
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_orders
|
||||||
|
schema: public
|
||||||
|
type: drop_insert_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
check:
|
||||||
|
job:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
columns:
|
||||||
|
- created_at
|
||||||
|
- deliver_by
|
||||||
|
- id
|
||||||
|
- jobid
|
||||||
|
- order_date
|
||||||
|
- order_number
|
||||||
|
- return
|
||||||
|
- returnfrombill
|
||||||
|
- status
|
||||||
|
- updated_at
|
||||||
|
- user_email
|
||||||
|
- vendorid
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_orders
|
||||||
|
schema: public
|
||||||
|
type: create_insert_permission
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_orders
|
||||||
|
schema: public
|
||||||
|
type: drop_insert_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
check:
|
||||||
|
job:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
columns:
|
||||||
|
- created_at
|
||||||
|
- deliver_by
|
||||||
|
- id
|
||||||
|
- jobid
|
||||||
|
- order_date
|
||||||
|
- order_number
|
||||||
|
- orderedby
|
||||||
|
- return
|
||||||
|
- returnfrombill
|
||||||
|
- status
|
||||||
|
- updated_at
|
||||||
|
- user_email
|
||||||
|
- vendorid
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_orders
|
||||||
|
schema: public
|
||||||
|
type: create_insert_permission
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_orders
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: false
|
||||||
|
columns:
|
||||||
|
- created_at
|
||||||
|
- deliver_by
|
||||||
|
- id
|
||||||
|
- jobid
|
||||||
|
- order_date
|
||||||
|
- order_number
|
||||||
|
- return
|
||||||
|
- returnfrombill
|
||||||
|
- status
|
||||||
|
- updated_at
|
||||||
|
- user_email
|
||||||
|
- vendorid
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
job:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_orders
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_orders
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: false
|
||||||
|
columns:
|
||||||
|
- created_at
|
||||||
|
- deliver_by
|
||||||
|
- id
|
||||||
|
- jobid
|
||||||
|
- order_date
|
||||||
|
- order_number
|
||||||
|
- orderedby
|
||||||
|
- return
|
||||||
|
- returnfrombill
|
||||||
|
- status
|
||||||
|
- updated_at
|
||||||
|
- user_email
|
||||||
|
- vendorid
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
job:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_orders
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_orders
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- created_at
|
||||||
|
- deliver_by
|
||||||
|
- id
|
||||||
|
- jobid
|
||||||
|
- order_date
|
||||||
|
- order_number
|
||||||
|
- returnfrombill
|
||||||
|
- status
|
||||||
|
- updated_at
|
||||||
|
- user_email
|
||||||
|
- vendorid
|
||||||
|
filter:
|
||||||
|
job:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_orders
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_orders
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- created_at
|
||||||
|
- deliver_by
|
||||||
|
- id
|
||||||
|
- jobid
|
||||||
|
- order_date
|
||||||
|
- order_number
|
||||||
|
- orderedby
|
||||||
|
- returnfrombill
|
||||||
|
- status
|
||||||
|
- updated_at
|
||||||
|
- user_email
|
||||||
|
- vendorid
|
||||||
|
filter:
|
||||||
|
job:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_orders
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: conversations
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: true
|
||||||
|
columns:
|
||||||
|
- phone_num
|
||||||
|
- created_at
|
||||||
|
- updated_at
|
||||||
|
- bodyshopid
|
||||||
|
- id
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: conversations
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: conversations
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: true
|
||||||
|
columns:
|
||||||
|
- archived
|
||||||
|
- bodyshopid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
|
- phone_num
|
||||||
|
- updated_at
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: conversations
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: conversations
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- phone_num
|
||||||
|
- created_at
|
||||||
|
- updated_at
|
||||||
|
- bodyshopid
|
||||||
|
- id
|
||||||
|
filter:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: conversations
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: conversations
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- archived
|
||||||
|
- bodyshopid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
|
- phone_num
|
||||||
|
- updated_at
|
||||||
|
filter:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: conversations
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: |-
|
||||||
|
alter table "public"."exportlog" drop constraint "exportlog_billid_fkey",
|
||||||
|
add constraint "exportlog_billid_fkey"
|
||||||
|
foreign key ("billid")
|
||||||
|
references "public"."bills"
|
||||||
|
("id")
|
||||||
|
on update restrict
|
||||||
|
on delete restrict;
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: |-
|
||||||
|
alter table "public"."exportlog" drop constraint "exportlog_billid_fkey",
|
||||||
|
add constraint "exportlog_billid_fkey"
|
||||||
|
foreign key ("billid")
|
||||||
|
references "public"."bills"
|
||||||
|
("id") on update cascade on delete cascade;
|
||||||
|
type: run_sql
|
||||||
@@ -1114,11 +1114,12 @@ tables:
|
|||||||
- role: user
|
- role: user
|
||||||
permission:
|
permission:
|
||||||
columns:
|
columns:
|
||||||
- phone_num
|
- archived
|
||||||
- created_at
|
|
||||||
- updated_at
|
|
||||||
- bodyshopid
|
- bodyshopid
|
||||||
|
- created_at
|
||||||
- id
|
- id
|
||||||
|
- phone_num
|
||||||
|
- updated_at
|
||||||
filter:
|
filter:
|
||||||
bodyshop:
|
bodyshop:
|
||||||
associations:
|
associations:
|
||||||
@@ -1133,11 +1134,12 @@ tables:
|
|||||||
- role: user
|
- role: user
|
||||||
permission:
|
permission:
|
||||||
columns:
|
columns:
|
||||||
- phone_num
|
- archived
|
||||||
- created_at
|
|
||||||
- updated_at
|
|
||||||
- bodyshopid
|
- bodyshopid
|
||||||
|
- created_at
|
||||||
- id
|
- id
|
||||||
|
- phone_num
|
||||||
|
- updated_at
|
||||||
filter:
|
filter:
|
||||||
bodyshop:
|
bodyshop:
|
||||||
associations:
|
associations:
|
||||||
@@ -3622,6 +3624,7 @@ tables:
|
|||||||
- jobid
|
- jobid
|
||||||
- order_date
|
- order_date
|
||||||
- order_number
|
- order_number
|
||||||
|
- orderedby
|
||||||
- return
|
- return
|
||||||
- returnfrombill
|
- returnfrombill
|
||||||
- status
|
- status
|
||||||
@@ -3638,6 +3641,7 @@ tables:
|
|||||||
- jobid
|
- jobid
|
||||||
- order_date
|
- order_date
|
||||||
- order_number
|
- order_number
|
||||||
|
- orderedby
|
||||||
- return
|
- return
|
||||||
- returnfrombill
|
- returnfrombill
|
||||||
- status
|
- status
|
||||||
@@ -3664,6 +3668,7 @@ tables:
|
|||||||
- jobid
|
- jobid
|
||||||
- order_date
|
- order_date
|
||||||
- order_number
|
- order_number
|
||||||
|
- orderedby
|
||||||
- returnfrombill
|
- returnfrombill
|
||||||
- status
|
- status
|
||||||
- updated_at
|
- updated_at
|
||||||
|
|||||||
@@ -67,6 +67,12 @@ async function JobCostingMulti(req, res) {
|
|||||||
gpdollars: Dinero({ amount: 0 }),
|
gpdollars: Dinero({ amount: 0 }),
|
||||||
gppercent: null,
|
gppercent: null,
|
||||||
gppercentFormatted: null,
|
gppercentFormatted: null,
|
||||||
|
totalLaborGp: Dinero({ amount: 0 }),
|
||||||
|
totalPartsGp: Dinero({ amount: 0 }),
|
||||||
|
totalLaborGppercent: null,
|
||||||
|
totalLaborGppercentFormatted: null,
|
||||||
|
totalPartsGppercent: null,
|
||||||
|
totalPartsGppercentFormatted: null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -149,12 +155,39 @@ async function JobCostingMulti(req, res) {
|
|||||||
multiSummary.summaryData.gpdollars.add(
|
multiSummary.summaryData.gpdollars.add(
|
||||||
costingData.summaryData.gpdollars
|
costingData.summaryData.gpdollars
|
||||||
);
|
);
|
||||||
|
|
||||||
|
multiSummary.summaryData.totalLaborGp =
|
||||||
|
multiSummary.summaryData.totalLaborGp.add(
|
||||||
|
costingData.summaryData.totalLaborGp
|
||||||
|
);
|
||||||
|
multiSummary.summaryData.totalPartsGp =
|
||||||
|
multiSummary.summaryData.totalPartsGp.add(
|
||||||
|
costingData.summaryData.totalPartsGp
|
||||||
|
);
|
||||||
|
|
||||||
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.
|
||||||
});
|
});
|
||||||
|
|
||||||
//For each center, recalculate and toFormat() the values.
|
//For each center, recalculate and toFormat() the values.
|
||||||
multiSummary.summaryData.gpdollars;
|
|
||||||
|
multiSummary.summaryData.totalLaborGppercent = (
|
||||||
|
(multiSummary.summaryData.totalLaborGp.getAmount() /
|
||||||
|
multiSummary.summaryData.totalLaborSales.getAmount()) *
|
||||||
|
100
|
||||||
|
).toFixed(2);
|
||||||
|
multiSummary.summaryData.totalLaborGppercentFormatted = formatGpPercent(
|
||||||
|
multiSummary.summaryData.totalLaborGppercent
|
||||||
|
);
|
||||||
|
|
||||||
|
multiSummary.summaryData.totalPartsGppercent = (
|
||||||
|
(multiSummary.summaryData.totalPartsGp.getAmount() /
|
||||||
|
multiSummary.summaryData.totalPartsSales.getAmount()) *
|
||||||
|
100
|
||||||
|
).toFixed(2);
|
||||||
|
multiSummary.summaryData.totalPartsGppercentFormatted = formatGpPercent(
|
||||||
|
multiSummary.summaryData.totalPartsGppercent
|
||||||
|
);
|
||||||
|
|
||||||
multiSummary.summaryData.gppercent = (
|
multiSummary.summaryData.gppercent = (
|
||||||
(multiSummary.summaryData.gpdollars.getAmount() /
|
(multiSummary.summaryData.gpdollars.getAmount() /
|
||||||
@@ -232,11 +265,11 @@ function GenerateCostingData(job) {
|
|||||||
acc.labor[laborProfitCenter].add(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.parts[defaultProfits["MAPA"]])
|
||||||
acc.labor[defaultProfits["MAPA"]] = Dinero();
|
acc.parts[defaultProfits["MAPA"]] = Dinero();
|
||||||
|
|
||||||
materialsHours.mapaHrs += val.mod_lb_hrs || 0;
|
materialsHours.mapaHrs += val.mod_lb_hrs || 0;
|
||||||
acc.labor[defaultProfits["MAPA"]] = acc.labor[
|
acc.parts[defaultProfits["MAPA"]] = acc.parts[
|
||||||
defaultProfits["MAPA"]
|
defaultProfits["MAPA"]
|
||||||
].add(
|
].add(
|
||||||
Dinero({
|
Dinero({
|
||||||
@@ -244,11 +277,11 @@ function GenerateCostingData(job) {
|
|||||||
}).multiply(val.mod_lb_hrs || 0)
|
}).multiply(val.mod_lb_hrs || 0)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!acc.labor[defaultProfits["MASH"]])
|
if (!acc.parts[defaultProfits["MASH"]])
|
||||||
acc.labor[defaultProfits["MASH"]] = Dinero();
|
acc.parts[defaultProfits["MASH"]] = Dinero();
|
||||||
|
|
||||||
if (val.mod_lbr_ty !== "LAR") {
|
if (val.mod_lbr_ty !== "LAR") {
|
||||||
acc.labor[defaultProfits["MASH"]] = acc.labor[
|
acc.parts[defaultProfits["MASH"]] = acc.parts[
|
||||||
defaultProfits["MASH"]
|
defaultProfits["MASH"]
|
||||||
].add(
|
].add(
|
||||||
Dinero({
|
Dinero({
|
||||||
@@ -369,6 +402,7 @@ function GenerateCostingData(job) {
|
|||||||
}).multiply(materialsHours.mapaHrs)
|
}).multiply(materialsHours.mapaHrs)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (job.bodyshop.jc_hourly_rates && job.bodyshop.jc_hourly_rates.mash) {
|
if (job.bodyshop.jc_hourly_rates && job.bodyshop.jc_hourly_rates.mash) {
|
||||||
if (
|
if (
|
||||||
!billTotalsByCostCenters[
|
!billTotalsByCostCenters[
|
||||||
@@ -418,7 +452,14 @@ function GenerateCostingData(job) {
|
|||||||
totalLaborCost: Dinero({ amount: 0 }),
|
totalLaborCost: Dinero({ amount: 0 }),
|
||||||
totalPartsCost: Dinero({ amount: 0 }),
|
totalPartsCost: Dinero({ amount: 0 }),
|
||||||
totalCost: Dinero({ amount: 0 }),
|
totalCost: Dinero({ amount: 0 }),
|
||||||
|
totalLaborGp: Dinero({ amount: 0 }),
|
||||||
|
totalPartsGp: Dinero({ amount: 0 }),
|
||||||
gpdollars: Dinero({ amount: 0 }),
|
gpdollars: Dinero({ amount: 0 }),
|
||||||
|
|
||||||
|
totalLaborGppercent: null,
|
||||||
|
totalLaborGppercentFormatted: null,
|
||||||
|
totalPartsGppercent: null,
|
||||||
|
totalPartsGppercentFormatted: null,
|
||||||
gppercent: null,
|
gppercent: null,
|
||||||
gppercentFormatted: null,
|
gppercentFormatted: null,
|
||||||
};
|
};
|
||||||
@@ -503,6 +544,31 @@ function GenerateCostingData(job) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Final summary data massaging.
|
//Final summary data massaging.
|
||||||
|
|
||||||
|
summaryData.totalLaborGp = summaryData.totalLaborSales.subtract(
|
||||||
|
summaryData.totalLaborCost
|
||||||
|
);
|
||||||
|
summaryData.totalLaborGppercent = (
|
||||||
|
(summaryData.totalLaborGp.getAmount() /
|
||||||
|
summaryData.totalLaborSales.getAmount()) *
|
||||||
|
100
|
||||||
|
).toFixed(2);
|
||||||
|
summaryData.totalLaborGppercentFormatted = formatGpPercent(
|
||||||
|
summaryData.totalLaborGppercent
|
||||||
|
);
|
||||||
|
|
||||||
|
summaryData.totalPartsGp = summaryData.totalPartsSales.subtract(
|
||||||
|
summaryData.totalPartsCost
|
||||||
|
);
|
||||||
|
summaryData.totalPartsGppercent = (
|
||||||
|
(summaryData.totalPartsGp.getAmount() /
|
||||||
|
summaryData.totalPartsSales.getAmount()) *
|
||||||
|
100
|
||||||
|
).toFixed(2);
|
||||||
|
summaryData.totalPartsGppercentFormatted = formatGpPercent(
|
||||||
|
summaryData.totalPartsGppercent
|
||||||
|
);
|
||||||
|
|
||||||
summaryData.gpdollars = summaryData.totalSales.subtract(
|
summaryData.gpdollars = summaryData.totalSales.subtract(
|
||||||
summaryData.totalCost
|
summaryData.totalCost
|
||||||
);
|
);
|
||||||
@@ -510,6 +576,7 @@ function GenerateCostingData(job) {
|
|||||||
(summaryData.gpdollars.getAmount() / summaryData.totalSales.getAmount()) *
|
(summaryData.gpdollars.getAmount() / summaryData.totalSales.getAmount()) *
|
||||||
100
|
100
|
||||||
).toFixed(2);
|
).toFixed(2);
|
||||||
|
|
||||||
if (isNaN(summaryData.gppercent)) summaryData.gppercentFormatted = 0;
|
if (isNaN(summaryData.gppercent)) summaryData.gppercentFormatted = 0;
|
||||||
else if (!isFinite(summaryData.gppercent))
|
else if (!isFinite(summaryData.gppercent))
|
||||||
summaryData.gppercentFormatted = "- ∞";
|
summaryData.gppercentFormatted = "- ∞";
|
||||||
|
|||||||
Reference in New Issue
Block a user