Compare commits

..

97 Commits

Author SHA1 Message Date
Patrick Fic
fb5a146dee IO-1182 Restrict manual posting to in house vendor. 2021-06-04 09:26:15 -07:00
Patrick Fic
a8b555b773 IO-1140 Crisp will only set email for valid emails & release notes styles updates. 2021-06-04 08:21:40 -07:00
Patrick Fic
aa60424eae IO-1180 Resolved sorting issues on All Jobs Page. 2021-06-03 10:27:45 -07:00
Patrick Fic
95ba0e1f8a IO-1140 Added release notes integration. 2021-06-03 10:02:04 -07:00
Patrick Fic
786d76bb73 Upload documents with unique time stamp. 2021-06-02 13:00:38 -07:00
Patrick Fic
8427ea208b IO-1169 Download only images. 2021-06-02 10:48:01 -07:00
Patrick Fic
b897795c27 Move MAPA/MASH to parts profit centers 2021-06-02 10:08:51 -07:00
Patrick Fic
9dfff36edf IO-1174 Adjust bill delete export log FK 2021-06-02 09:13:42 -07:00
Patrick Fic
9d4f98d3ee IO-1167 Resolve sorting on payments table. 2021-06-02 09:07:29 -07:00
Patrick Fic
8770e95ee3 IO-1116 Notes Multiline display 2021-06-02 09:00:18 -07:00
Patrick Fic
9ff9baa78c IO-1096 Added order by to Parts Order 2021-06-02 08:49:10 -07:00
Patrick Fic
28326f2628 IO-1176 Move duplication totals to server side. 2021-06-01 16:49:54 -07:00
Patrick Fic
7c35a2e790 Merged in test (pull request #92)
Test
2021-06-01 21:58:40 +00:00
Patrick Fic
488d1be1cc Merged in hotfix/2021-06-01 (pull request #90)
Fix typo.
2021-06-01 21:45:25 +00:00
Patrick Fic
11d3e3c7ad Fix typo. 2021-06-01 14:44:11 -07:00
Patrick Fic
a61b4a47cf Merged in hotfix/2021-06-01 (pull request #89)
IO-1178 Resolve minor bugs.
2021-06-01 21:37:22 +00:00
Patrick Fic
0108135fe9 IO-1178 Resolve minor bugs. 2021-06-01 14:36:28 -07:00
Patrick Fic
372b7e8e30 Merged in hotfix/2021-06-01 (pull request #88)
Additional changes to translations.
2021-06-01 19:17:38 +00:00
Patrick Fic
74b3ceec63 Additional changes to translations. 2021-06-01 12:16:23 -07:00
Patrick Fic
144fe06e60 Merged in hotfix/2021-06-01 (pull request #87)
Emergency Hotfix for line markup issues. IO-1178
2021-06-01 19:15:47 +00:00
Patrick Fic
bef01385b2 Attempt to result PR conflicts. 2021-06-01 12:15:08 -07:00
Patrick Fic
fa3ccf0fea Resolve language conflicts. 2021-06-01 12:06:48 -07:00
Patrick Fic
d2bb7121cb IO-1178 Resolve line markup issues. 2021-06-01 11:52:09 -07:00
Patrick Fic
2dbcd203ce Merged in feature/2020-06-04 (pull request #86)
Feature/2020 06 04
2021-06-01 18:06:51 +00:00
Patrick Fic
1eb65dbc43 Rename all reports from source to ins_co 2021-06-01 10:51:27 -07:00
Patrick Fic
b066e08511 Resolved nesting issue in report center. 2021-06-01 10:24:47 -07:00
Patrick Fic
214d07f2ef Merged in feature/2020-06-04 (pull request #81)
Feature/2020 06 04 - Batch 2
2021-06-01 01:17:54 +00:00
Patrick Fic
16746dd2a0 Merged in hotfix/2021-05-31 (pull request #85)
Emergency fix #2 for GST Registrant.
2021-06-01 01:11:37 +00:00
Patrick Fic
bcec0cd902 Merged in hotfix/2021-05-31 (pull request #84)
Emergency fix #2 for GST Registrant.
2021-06-01 01:08:12 +00:00
Patrick Fic
6faf69a875 Emergency fix #2 for GST Registrant. 2021-05-31 18:07:42 -07:00
Patrick Fic
6f229a859b Merged in hotfix/2021-05-31 (pull request #83)
Emergency hotfix for GST registrant.
2021-06-01 00:54:34 +00:00
Patrick Fic
092904377f Merged in hotfix/2021-05-31 (pull request #82)
Emergency hotfix for GST registrant.
2021-06-01 00:53:00 +00:00
Patrick Fic
7277688de4 Emergency hotfix for GST registrant. 2021-05-31 17:51:11 -07:00
Patrick Fic
9e5ff432d7 Remove JIRA Widget. 2021-05-31 14:30:59 -07:00
Patrick Fic
5817daa4b3 IO-1175 Allow for null vin. 2021-05-31 14:20:27 -07:00
Patrick Fic
51292f50dc IO-1176 Duplicate job inversion. 2021-05-31 14:19:15 -07:00
Patrick Fic
b5e9e75751 IO-1175 Vehicle not required metadata changes. 2021-05-31 14:18:57 -07:00
Patrick Fic
f778b964fd ENV Changes. 2021-05-31 13:56:54 -07:00
Patrick Fic
06c14d2742 Merged in feature/2020-06-04 (pull request #80)
Feature/2020 06 04 - Batch 1 of updates
2021-05-31 20:16:13 +00:00
Patrick Fic
7441288cf5 IO-1173 Missing Reports 2021-05-31 13:11:04 -07:00
Patrick Fic
7524cdf0b1 IO-1166 Job Costing Summaries updates. 2021-05-31 12:21:27 -07:00
Patrick Fic
46dff9f52c IO-1163 Compatibility for HEIC. 2021-05-31 12:21:11 -07:00
Patrick Fic
afb0c85e9f IO-1167 IO-775 Minor bug fixes. 2021-05-31 10:56:13 -07:00
Patrick Fic
af6bb18db2 IO-1138 Additional offline check. 2021-05-31 10:50:28 -07:00
Patrick Fic
c28d4c15a0 IO-1144 Image Editor Changes. 2021-05-31 10:45:06 -07:00
Patrick Fic
0c167a1833 Merged in test (pull request #79)
Update 2021-06-04 Feature Branch with latest fixes.
2021-05-31 15:14:08 +00:00
Patrick Fic
f7938df5e4 WIP Photo Editor 2021-05-28 18:18:09 -07:00
Patrick Fic
5717677339 Merged in hotfix/2020-05-28 (pull request #78)
Hotfix/2020 05 28 - Production Deployment
2021-05-28 18:28:52 +00:00
Patrick Fic
cffe9cd4f6 Merged in feature/2020-06-04 (pull request #77)
Added error handling to Crisp.
2021-05-28 04:15:09 +00:00
Patrick Fic
5da8c77b3a Added error handling to Crisp. 2021-05-27 21:13:36 -07:00
Patrick Fic
3cc652d113 Merged in feature/2020-06-04 (pull request #76)
Major Features - Adding Crisp to Test
2021-05-28 03:20:57 +00:00
Patrick Fic
7d82fb8f04 Added Crisp scripts. 2021-05-27 20:16:50 -07:00
Patrick Fic
f1cbc9f775 Merged in hotfix/2020-05-28 (pull request #75)
Hotfix to test for IO-1152.
2021-05-27 23:50:08 +00:00
Patrick Fic
58c6ba2f87 IO-1152 Add missing fields to query. 2021-05-27 16:49:21 -07:00
Patrick Fic
0fbd4b495b Merged in hotfix/2020-05-28 (pull request #74)
Hotfix to test for IO-1152.
2021-05-27 23:25:55 +00:00
Patrick Fic
343274e1e2 IO-1152 Missed in last commit. 2021-05-27 16:25:07 -07:00
Patrick Fic
bc729c0f8c Merged in hotfix/2020-05-28 (pull request #73)
Hotfix to test for IO-1152.
2021-05-27 23:23:10 +00:00
Patrick Fic
09ce5ac892 IO-1152 Resolve parts markup issue. 2021-05-27 16:21:54 -07:00
Patrick Fic
2536b0b986 Merged in hotfix/2020-05-28 (pull request #72)
Hotfix/2020 05 28 deployment to Test.
2021-05-27 21:59:43 +00:00
Patrick Fic
c8506387f6 IO-1131 - Remove version #. 2021-05-27 14:58:23 -07:00
Patrick Fic
79f2c7dd3d IO-1086 confirmation on profile update. 2021-05-27 14:11:16 -07:00
Patrick Fic
d91a83a137 IO-1157 Add mapa/mash costs to costing. 2021-05-27 14:04:52 -07:00
Patrick Fic
0b21b8d976 IO-1138 Offline detection. 2021-05-27 13:32:57 -07:00
Patrick Fic
842cb54867 IO-1153 Phonebook Validation 2021-05-27 13:15:58 -07:00
Patrick Fic
a5628188d8 IO-1171 Null vehicle link on detail page. 2021-05-27 11:35:58 -07:00
Patrick Fic
1b0e37be45 IO-998 Resolve media rename issues for non images. 2021-05-27 08:49:43 -07:00
Patrick Fic
48ecfe0d98 Media File improvements + separate DEV testing instance for cloudinary. 2021-05-26 16:45:59 -07:00
Patrick Fic
b5b4a3a4f9 IO-1080 Standard audit note language. 2021-05-26 14:16:17 -07:00
Patrick Fic
585d585cd3 Merge branch 'hotfix/2020-05-26' into hotfix/2020-05-28 2021-05-26 14:10:04 -07:00
Patrick Fic
42356c4e99 Merged in hotfix/2020-05-26 (pull request #71)
Deployment of Hotfix/2020 05 26
2021-05-26 18:24:58 +00:00
Patrick Fic
cd9aeba9f7 Merged in hotfix/2020-05-26 (pull request #70)
Hotfix/2020 05 26 - Additional changes & retest
2021-05-26 17:45:20 +00:00
Patrick Fic
8a7a5f35c1 IO-1095 unrequire fields on vehicle create. 2021-05-26 10:34:32 -07:00
Patrick Fic
b24c5e7cf1 IO-1128 Change labor allocations table coloring. 2021-05-26 10:19:43 -07:00
Patrick Fic
e8b7e2f0b9 IO-1152 Add in part mark up for BC Economical claims. 2021-05-26 09:06:05 -07:00
Patrick Fic
b75e14e4f5 Merged in hotfix/2020-05-26 (pull request #69)
Hotfix/2020 05 26 to Testing Env.
2021-05-25 23:33:23 +00:00
Patrick Fic
3e59c60477 IO-1168 Resolve payments export all null error. 2021-05-25 16:31:09 -07:00
Patrick Fic
e89b4fe2a4 IO-1137 Download zip files with appropriate name. 2021-05-25 16:30:29 -07:00
Patrick Fic
73b0542b62 IO-1148 Prefill scheduled completion on intake if not present. 2021-05-25 14:58:50 -07:00
Patrick Fic
c9812c36c0 IO-1129 MIssing color on schedule modal. 2021-05-25 14:47:16 -07:00
Patrick Fic
d62a2c0aaf IO-1095 Remove required fields vehicle/owner. 2021-05-25 14:42:59 -07:00
Patrick Fic
ee15f063ce IO-1158 Change Ins Co Nm to select 2021-05-25 11:21:55 -07:00
Patrick Fic
5f0a683ec1 IO-1161 Line Modal Hours to precision 1. 2021-05-25 09:58:46 -07:00
Patrick Fic
6ae9de7df3 Merged in test (pull request #68)
Release Hotfix for 2021-05-21
2021-05-21 22:18:54 +00:00
Patrick Fic
19aa41ab5b Merged in hotfix/2021-05-21 (pull request #67)
IO-1156 Update order for temporary docs.
2021-05-21 21:04:39 +00:00
Patrick Fic
0494b3a9a6 IO-1156 Update order for temporary docs. 2021-05-21 14:02:25 -07:00
Patrick Fic
01d9dc9033 Added commit # to server api. 2021-05-21 13:47:11 -07:00
Patrick Fic
47b8d8d8b2 Merged in hotfix/2021-05-21 (pull request #66)
IO-1139 Adjust to 1 decimal.
2021-05-21 20:21:07 +00:00
Patrick Fic
6e3453cd90 IO-1139 Adjust to 1 decimal. 2021-05-21 13:18:57 -07:00
Patrick Fic
4e10451bea Merged in hotfix/2021-05-21 (pull request #65)
Hotfix/2021 05 21
2021-05-21 20:10:57 +00:00
Patrick Fic
81d5c8f449 IO-1139 Total hours to 2 decimals. 2021-05-21 13:08:59 -07:00
Patrick Fic
79fa71fc84 IO-1160 Target Touch Time Highlighting. 2021-05-21 12:44:08 -07:00
Patrick Fic
f6378daa89 IO-1151 IO-1159 Calculated Repair & Label Updates 2021-05-21 12:37:54 -07:00
Patrick Fic
f2a4eb1b65 IO-1101 Fix PAP marking of lines. 2021-05-21 09:38:30 -07:00
Patrick Fic
2d19c35177 IO-1147 Default tax rates on parts for manual jobs. 2021-05-21 09:31:55 -07:00
Patrick Fic
9787f7e377 Merged in feature/IOS-7-documents-should-upload-in-chronol (pull request #64)
IOS-7 Added takenat to documents and uploads.
2021-05-21 14:38:28 +00:00
Patrick Fic
3abac3e7ac Merged master into test 2021-05-20 14:58:05 +00:00
Patrick Fic
fddf75b40b IOS-7 Added takenat to documents and uploads. 2021-05-19 17:52:24 -07:00
117 changed files with 3845 additions and 478 deletions

View File

@@ -22,7 +22,7 @@ hooks.js:
module.exports = [ module.exports = [
{ {
path: "/pull", path: "/pull",
command: "git pull && npm i", command: "git pull && yarn && pm2 restart 0",
cwd: "/home/ubuntu/io/", cwd: "/home/ubuntu/io/",
method: "post", method: "post",
}, },

View File

@@ -28,7 +28,7 @@ import JobsShow from "../jobs/jobs.show";
const httpLink = new HttpLink({ const httpLink = new HttpLink({
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT, uri: process.env.REACT_APP_GRAPHQL_ENDPOINT,
headers: { headers: {
"x-hasura-admin-secret": `Dev-BodyShopAppBySnaptSoftware!`, "x-hasura-admin-secret": `Dev-BodyShopApp!`,
// 'Authorization': `Bearer xxxx`, // 'Authorization': `Bearer xxxx`,
}, },
}); });
@@ -67,7 +67,7 @@ const client = new ApolloClient({
// uri: process.env.REACT_APP_GRAPHQL_ENDPOINT, // uri: process.env.REACT_APP_GRAPHQL_ENDPOINT,
// cache: new InMemoryCache(), // cache: new InMemoryCache(),
// headers: { // headers: {
// "x-hasura-admin-secret": `Dev-BodyShopAppBySnaptSoftware!`, // "x-hasura-admin-secret": `Dev-BodyShopApp!`,
// // 'Authorization': `Bearer xxxx`, // // 'Authorization': `Bearer xxxx`,
// }, // },
// }); // });

File diff suppressed because it is too large Load Diff

View File

@@ -20,6 +20,7 @@
"dinero.js": "^1.8.1", "dinero.js": "^1.8.1",
"dotenv": "^9.0.2", "dotenv": "^9.0.2",
"env-cmd": "^10.1.0", "env-cmd": "^10.1.0",
"exifr": "^7.0.0",
"firebase": "^8.6.0", "firebase": "^8.6.0",
"graphql": "^15.5.0", "graphql": "^15.5.0",
"i18next": "^20.2.2", "i18next": "^20.2.2",
@@ -28,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",

View File

@@ -9,12 +9,50 @@
<!-- <link rel="apple-touch-icon" href="logo192.png" /> --> <!-- <link rel="apple-touch-icon" href="logo192.png" /> -->
<link rel="apple-touch-icon" href="logo192.png" /> <link rel="apple-touch-icon" href="logo192.png" />
<!-- <script <script>
data-jsd-embedded !(function () {
data-key="51adb36e-ee16-46b1-a4c6-4b6d5fcd8530" "use strict";
data-base-url="https://jsd-widget.atlassian.com" var e = [
src="https://jsd-widget.atlassian.com/assets/embed.js" "debug",
></script> --> "destroy",
"do",
"help",
"identify",
"is",
"off",
"on",
"ready",
"render",
"reset",
"safe",
"set",
];
if (window.noticeable)
console.warn("Noticeable SDK code snippet loaded more than once");
else {
var n = (window.noticeable = window.noticeable || []);
function t(e) {
return function () {
var t = Array.prototype.slice.call(arguments);
return t.unshift(e), n.push(t), n;
};
}
!(function () {
for (var o = 0; o < e.length; o++) {
var r = e[o];
n[r] = t(r);
}
})(),
(function () {
var e = document.createElement("script");
(e.async = !0), (e.src = "https://sdk.noticeable.io/l.js");
var n = document.head;
n.insertBefore(e, n.firstChild);
})();
}
})();
</script>
<!-- <!--
manifest.json provides metadata used when your web app is installed on a manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/

View File

@@ -3,17 +3,42 @@ 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");
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);
//Release Notes
// var rs = d.createElement("script");
// rs.src = "https://sdk.noticeable.io/s.js";
// //rs.async = 1;
// d.getElementsByTagName("head")[0].appendChild(rs);
// // window.noticeable.render("widget", "IABVNO4scRKY11XBQkNr");
return () => {
d.getElementsByTagName("head")[0].removeChild(s);
};
}, []);
return ( return (
<ApolloProvider client={client}> <ApolloProvider client={client}>
<ConfigProvider <ConfigProvider
@@ -29,6 +54,7 @@ export default function AppContainer() {
> >
<GlobalLoadingBar /> <GlobalLoadingBar />
<App /> <App />
<JiraSupportComponent />
</ConfigProvider> </ConfigProvider>
</ApolloProvider> </ApolloProvider>
); );

View File

@@ -1,13 +1,17 @@
import { Button, Result } from "antd";
import React, { lazy, Suspense, useEffect } from "react"; import React, { lazy, Suspense, useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
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";
import AboutPage from "../pages/about/about.page"; import AboutPage from "../pages/about/about.page";
import TechPageContainer from "../pages/tech/tech.page.container"; import TechPageContainer from "../pages/tech/tech.page.container";
import { setOnline } from "../redux/application/application.actions";
import { selectOnline } from "../redux/application/application.selectors";
import { checkUserSession } from "../redux/user/user.actions"; import { checkUserSession } from "../redux/user/user.actions";
import { selectCurrentUser } from "../redux/user/user.selectors"; import { selectCurrentUser } from "../redux/user/user.selectors";
import PrivateRoute from "../utils/private-route"; import PrivateRoute from "../utils/private-route";
@@ -27,25 +31,59 @@ const MobilePaymentContainer = lazy(() =>
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser, currentUser: selectCurrentUser,
online: selectOnline,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
checkUserSession: () => dispatch(checkUserSession()), checkUserSession: () => dispatch(checkUserSession()),
setOnline: (isOnline) => dispatch(setOnline(isOnline)),
}); });
export function App({ checkUserSession, currentUser }) { export function App({ checkUserSession, currentUser, online, setOnline }) {
useEffect(() => { useEffect(() => {
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);
const { t } = useTranslation(); const { t } = useTranslation();
window.addEventListener("offline", function (e) {
console.log("Internet connection lost.");
setOnline(false);
});
window.addEventListener("online", function (e) {
setOnline(true);
});
if (currentUser.authorized === null) { if (currentUser.authorized === null) {
return <LoadingSpinner message={t("general.labels.loggingin")} />; return <LoadingSpinner message={t("general.labels.loggingin")} />;
} }
if (!online)
return (
<Result
status="warning"
title={t("general.labels.nointernet")}
subTitle={t("general.labels.nointernet_sub")}
extra={
<Button
type="primary"
onClick={() => {
window.location.reload();
}}
>
{t("general.actions.refresh")}
</Button>
}
/>
);
return ( return (
<Switch> <Switch>
<Suspense fallback={<LoadingSpinner message="ImEX Online" />}> <Suspense fallback={<LoadingSpinner message="ImEX Online" />}>
@@ -85,6 +123,13 @@ export function App({ checkUserSession, currentUser }) {
component={TechPageContainer} component={TechPageContainer}
/> />
</ErrorBoundary> </ErrorBoundary>
<ErrorBoundary>
<PrivateRoute
isAuthorized={currentUser.authorized}
path="/edit"
component={DocumentEditorContainer}
/>
</ErrorBoundary>
</Suspense> </Suspense>
</Switch> </Switch>
); );

View File

@@ -118,3 +118,9 @@
.production-list-min-height { .production-list-min-height {
min-height: 19px; min-height: 19px;
} }
#noticeable-widget {
iframe {
z-index: 2 !important;
}
}

View File

@@ -5,9 +5,9 @@ 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";
export default function AccountingPayablesTableComponent({ export default function AccountingPayablesTableComponent({
loading, loading,
@@ -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}>

View File

@@ -106,6 +106,18 @@ export function BillFormComponent({
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
({ getFieldValue }) => ({
validator(rule, value) {
if (
value &&
!getFieldValue(["isinhouse"]) &&
value === bodyshop.inhousevendorid
) {
return Promise.reject(t("bills.validation.manualinhouse"));
}
return Promise.resolve();
},
}),
]} ]}
> >
<VendorSearchSelect <VendorSearchSelect

View File

@@ -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>
); );
} }

View File

@@ -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>
); );
} }

View File

@@ -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);

View File

@@ -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>
);
}

View File

@@ -5,6 +5,8 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_NEW_DOCUMENT } from "../../graphql/documents.queries"; import { INSERT_NEW_DOCUMENT } from "../../graphql/documents.queries";
import { axiosAuthInterceptorId } from "../../utils/CleanAxios"; import { axiosAuthInterceptorId } from "../../utils/CleanAxios";
import client from "../../utils/GraphQLClient"; import client from "../../utils/GraphQLClient";
import exifr from "exifr";
//Context: currentUserEmail, bodyshop, jobid, invoiceid //Context: currentUserEmail, bodyshop, jobid, invoiceid
//Required to prevent headers from getting set and rejected from Cloudinary. //Required to prevent headers from getting set and rejected from Cloudinary.
@@ -19,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,
@@ -85,6 +92,7 @@ export const uploadToCloudinary = async (
if (!!onProgress) onProgress({ percent: (e.loaded / e.total) * 100 }); if (!!onProgress) onProgress({ percent: (e.loaded / e.total) * 100 });
}, },
}; };
const formData = new FormData(); const formData = new FormData();
formData.append("file", file); formData.append("file", file);
@@ -122,6 +130,12 @@ export const uploadToCloudinary = async (
} }
//Insert the document with the matching key. //Insert the document with the matching key.
let takenat;
if (fileType.includes("image")) {
const exif = await exifr.parse(file);
takenat = exif && exif.DateTimeOriginal;
}
const documentInsert = await client.mutate({ const documentInsert = await client.mutate({
mutation: INSERT_NEW_DOCUMENT, mutation: INSERT_NEW_DOCUMENT,
variables: { variables: {
@@ -132,9 +146,10 @@ export const uploadToCloudinary = async (
uploaded_by: uploaded_by, uploaded_by: uploaded_by,
key: key, key: key,
type: fileType, type: fileType,
extension: extension, extension: cloudinaryUploadResponse.data.format || extension,
bodyshopid: bodyshop.id, bodyshopid: bodyshop.id,
size: cloudinaryUploadResponse.data.bytes || file.size, size: cloudinaryUploadResponse.data.bytes || file.size,
takenat,
}, },
], ],
}, },
@@ -166,6 +181,7 @@ export const uploadToCloudinary = async (
} }
}; };
//Also needs to be updated in media JS and mobile app.
export function DetermineFileType(filetype) { export function DetermineFileType(filetype) {
if (!filetype) return "auto"; if (!filetype) return "auto";
else if (filetype.startsWith("image")) return "image"; else if (filetype.startsWith("image")) return "image";

View File

@@ -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

View File

@@ -14,6 +14,7 @@ import Icon, {
LineChartOutlined, LineChartOutlined,
PaperClipOutlined, PaperClipOutlined,
PhoneOutlined, PhoneOutlined,
QuestionCircleFilled,
ScheduleOutlined, ScheduleOutlined,
SettingOutlined, SettingOutlined,
TeamOutlined, TeamOutlined,
@@ -255,6 +256,13 @@ function Header({
{t("menus.header.temporarydocs")} {t("menus.header.temporarydocs")}
</Link> </Link>
</Menu.Item> </Menu.Item>
<Menu.Item
key="help"
onClick={() => {
window.open("https://help.imex.online/", "_blank");
}}
icon={<Icon component={QuestionCircleFilled} />}
/>
<Menu.SubMenu title={t("menus.header.shop")} icon={<SettingOutlined />}> <Menu.SubMenu title={t("menus.header.shop")} icon={<SettingOutlined />}>
<Menu.Item key="shop" icon={<Icon component={GiSettingsKnobs} />}> <Menu.Item key="shop" icon={<Icon component={GiSettingsKnobs} />}>
<Link to="/manage/shop">{t("menus.header.shop_config")}</Link> <Link to="/manage/shop">{t("menus.header.shop_config")}</Link>

View File

@@ -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);
}; // };
}, []); // }, []);
}; // };

View File

@@ -15,6 +15,7 @@ import {
} from "../../../../redux/user/user.selectors"; } from "../../../../redux/user/user.selectors";
import ConfigFormComponents from "../../../config-form-components/config-form-components.component"; import ConfigFormComponents from "../../../config-form-components/config-form-components.component";
import DateTimePicker from "../../../form-date-time-picker/form-date-time-picker.component"; import DateTimePicker from "../../../form-date-time-picker/form-date-time-picker.component";
import moment from "moment-business-days";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -133,7 +134,14 @@ export function JobChecklistForm({
initialValues={{ initialValues={{
...(type === "intake" && { ...(type === "intake" && {
addToProduction: true, addToProduction: true,
scheduled_completion: job && job.scheduled_completion, scheduled_completion:
(job && job.scheduled_completion) ||
moment().businessAdd(
(job.labhrs.aggregate.sum.mod_lb_hrs +
job.larhrs.aggregate.sum.mod_lb_hrs) /
bodyshop.target_touchtime,
"days"
),
scheduled_delivery: job && job.scheduled_delivery, scheduled_delivery: job && job.scheduled_delivery,
}), }),
...(type === "deliver" && { ...(type === "deliver" && {

View File

@@ -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}

View File

@@ -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,
@@ -155,7 +158,16 @@ export function JobLinesComponent({
state.sortedInfo.columnKey === "act_price" && state.sortedInfo.order, state.sortedInfo.columnKey === "act_price" && state.sortedInfo.order,
ellipsis: true, ellipsis: true,
render: (text, record) => ( render: (text, record) => (
<CurrencyFormatter>{record.act_price}</CurrencyFormatter> <>
<CurrencyFormatter>{record.act_price}</CurrencyFormatter>
{record.prt_dsmk_p && record.prt_dsmk_p !== 0 ? (
<span
style={{ marginLeft: ".2rem" }}
>{`(${record.prt_dsmk_p}%)`}</span>
) : (
<></>
)}
</>
), ),
}, },
{ {
@@ -313,9 +325,12 @@ export function JobLinesComponent({
if (e.key === "clear") { if (e.key === "clear") {
setSelectedLines([]); setSelectedLines([]);
} else { } else {
const markedTypes = [e.key];
if (e.key === "PAN") markedTypes.push("PAP");
if (e.key === "PAS") markedTypes.push("PASL");
setSelectedLines([ setSelectedLines([
...selectedLines, ...selectedLines,
...jobLines.filter((item) => item.part_type === e.key), ...jobLines.filter((item) => markedTypes.includes(item.part_type)),
]); ]);
} }
}; };
@@ -354,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({
@@ -378,7 +394,7 @@ export function JobLinesComponent({
setState({ setState({
...state, ...state,
filteredInfo: { filteredInfo: {
part_type: ["PAN,PAL,PAA,PAS,PASL"], part_type: ["PAN,PAL,PAA,PAP,PAS,PASL"],
}, },
}); });
}} }}
@@ -389,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 },

View File

@@ -123,7 +123,7 @@ export default function JobLinesUpsertModalComponent({
// }), // }),
// ]} // ]}
> >
<InputCurrency /> <InputNumber precision={1} />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow> <LayoutFormRow>
@@ -210,6 +210,13 @@ export default function JobLinesUpsertModalComponent({
> >
<InputCurrency precision={2} min={0} /> <InputCurrency precision={2} min={0} />
</Form.Item> </Form.Item>
<Form.Item
label={t("joblines.fields.prt_dsmk_p")}
name="prt_dsmk_p"
initialValue={0}
>
<InputNumber precision={0} min={0} max={100} />
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
</Form> </Form>
</Modal> </Modal>

View File

@@ -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"),

View File

@@ -69,17 +69,28 @@ export default function JobTotalsTableParts({ job }) {
x: true, x: true,
}} }}
summary={() => ( summary={() => (
<Table.Summary.Row> <>
<Table.Summary.Cell> <Table.Summary.Row>
<strong>{t("jobs.labels.partstotal")}</strong> <Table.Summary.Cell>
</Table.Summary.Cell> {t("jobs.labels.prt_dsmk_total")}
</Table.Summary.Cell>
<Table.Summary.Cell align="right"> <Table.Summary.Cell align="right">
<strong> {Dinero(job.job_totals.parts.parts.prt_dsmk_total).toFormat()}
{Dinero(job.job_totals.parts.parts.total).toFormat()} </Table.Summary.Cell>
</strong> </Table.Summary.Row>
</Table.Summary.Cell> <Table.Summary.Row>
</Table.Summary.Row> <Table.Summary.Cell>
<strong>{t("jobs.labels.partstotal")}</strong>
</Table.Summary.Cell>
<Table.Summary.Cell align="right">
<strong>
{Dinero(job.job_totals.parts.parts.total).toFormat()}
</strong>
</Table.Summary.Cell>
</Table.Summary.Row>
</>
)} )}
/> />
); );

View File

@@ -32,7 +32,7 @@ mutation UNVOID_JOB($jobId: uuid!) {
} }
insert_notes(objects: {jobid: $jobId, audit: true, created_by: "${ insert_notes(objects: {jobid: $jobId, audit: true, created_by: "${
currentUser.email currentUser.email
}", text: "${t("jobs.labels.unvoidnote", { email: currentUser.email })}"}) { }", text: "${t("jobs.labels.unvoidnote")}"}) {
returning { returning {
id id
} }

View File

@@ -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,
@@ -124,10 +127,7 @@ export function JobsAvailableContainer({ bodyshop, currentUser }) {
data: { data: {
created_by: currentUser.email, created_by: currentUser.email,
audit: true, audit: true,
text: t("jobs.labels.importnote", { text: t("jobs.labels.importnote"),
date: moment().format("MM/DD/yyy"),
time: moment().format("hh:mm a"),
}),
}, },
}, },
queued_for_parts: true, queued_for_parts: true,
@@ -278,10 +278,7 @@ export function JobsAvailableContainer({ bodyshop, currentUser }) {
jobid: selectedJob, jobid: selectedJob,
created_by: currentUser.email, created_by: currentUser.email,
audit: true, audit: true,
text: t("jobs.labels.supplementnote", { text: t("jobs.labels.supplementnote"),
date: moment().format("MM/DD/yyy"),
time: moment().format("hh:mm a"),
}),
}, },
], ],
}, },

View File

@@ -26,12 +26,6 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
const { getFieldValue } = form; const { getFieldValue } = form;
return ( return (
<div> <div>
<JobsDetailRatesParts
jobRO={false}
expanded
required={selected && true}
form={form}
/>
<Collapse defaultActiveKey="insurance"> <Collapse defaultActiveKey="insurance">
<Collapse.Panel <Collapse.Panel
key="insurance" key="insurance"
@@ -57,7 +51,13 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
<FormDatePicker /> <FormDatePicker />
</Form.Item> </Form.Item>
<Form.Item label={t("jobs.fields.ins_co_nm")} name="ins_co_nm"> <Form.Item label={t("jobs.fields.ins_co_nm")} name="ins_co_nm">
<Input /> <Select>
{bodyshop.md_ins_cos.map((s) => (
<Select.Option key={s.name} value={s.name}>
{s.name}
</Select.Option>
))}
</Select>
</Form.Item> </Form.Item>
<Form.Item label={t("jobs.fields.ins_addr1")} name="ins_addr1"> <Form.Item label={t("jobs.fields.ins_addr1")} name="ins_addr1">
<Input /> <Input />
@@ -312,6 +312,12 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
</LayoutFormRow> </LayoutFormRow>
</Collapse.Panel> </Collapse.Panel>
</Collapse> </Collapse>
<JobsDetailRatesParts
jobRO={false}
expanded
required={selected && true}
form={form}
/>
</div> </div>
); );
} }

View File

@@ -83,24 +83,12 @@ export default function JobsCreateVehicleInfoNewComponent() {
<Form.Item <Form.Item
label={t("vehicles.fields.plate_st")} label={t("vehicles.fields.plate_st")}
name={["vehicle", "data", "plate_st"]} name={["vehicle", "data", "plate_st"]}
rules={[
{
required: state.vehicle.new,
//message: t("general.validation.required"),
},
]}
> >
<Input disabled={!state.vehicle.new} /> <Input disabled={!state.vehicle.new} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("vehicles.fields.plate_no")} label={t("vehicles.fields.plate_no")}
name={["vehicle", "data", "plate_no"]} name={["vehicle", "data", "plate_no"]}
rules={[
{
required: state.vehicle.new,
//message: t("general.validation.required"),
},
]}
> >
<Input disabled={!state.vehicle.new} /> <Input disabled={!state.vehicle.new} />
</Form.Item> </Form.Item>

View File

@@ -1,4 +1,4 @@
import { DatePicker, Form, Tooltip } from "antd"; import { DatePicker, Form, Statistic, Tooltip } from "antd";
import React, { useMemo } from "react"; import React, { useMemo } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -23,6 +23,10 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
); );
}, [job.status, bodyshop.md_ro_statuses.post_production_statuses]); }, [job.status, bodyshop.md_ro_statuses.post_production_statuses]);
const calcRepairDays =
job.joblines.reduce((acc, val) => acc + val.mod_lb_hrs, 0) /
(bodyshop.target_touchtime === 0 ? 1 : bodyshop.target_touchtime);
return ( return (
<div> <div>
<FormRow header={t("jobs.forms.estdates")}> <FormRow header={t("jobs.forms.estdates")}>
@@ -52,6 +56,17 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
<Form.Item label={t("jobs.fields.actual_in")} name="actual_in"> <Form.Item label={t("jobs.fields.actual_in")} name="actual_in">
<DateTimePicker disabled={jobRO} /> <DateTimePicker disabled={jobRO} />
</Form.Item> </Form.Item>
<Tooltip
title={t("jobs.labels.calc_repair_days_tt", {
target_touchtime: bodyshop.target_touchtime,
})}
>
<Statistic
value={calcRepairDays}
precision={1}
title={t("jobs.labels.calc_repair_days")}
/>
</Tooltip>
</FormRow> </FormRow>
<FormRow header={t("jobs.forms.repairdates")}> <FormRow header={t("jobs.forms.repairdates")}>
<Form.Item <Form.Item

View File

@@ -54,7 +54,13 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
</Form.Item> </Form.Item>
<Form.Item label={t("jobs.fields.ins_co_nm")} name="ins_co_nm"> <Form.Item label={t("jobs.fields.ins_co_nm")} name="ins_co_nm">
<Input disabled={jobRO} /> <Select disabled={jobRO}>
{bodyshop.md_ins_cos.map((s) => (
<Select.Option key={s.name} value={s.name}>
{s.name}
</Select.Option>
))}
</Select>
</Form.Item> </Form.Item>
<Form.Item label={t("jobs.fields.ins_addr1")} name="ins_addr1"> <Form.Item label={t("jobs.fields.ins_addr1")} name="ins_addr1">
<Input disabled={jobRO} /> <Input disabled={jobRO} />

View File

@@ -1,7 +1,6 @@
import { DownCircleFilled } from "@ant-design/icons"; import { DownCircleFilled } from "@ant-design/icons";
import { useApolloClient, useMutation } from "@apollo/client"; import { useApolloClient, useMutation } from "@apollo/client";
import { Button, Dropdown, Menu, notification, Popconfirm } from "antd"; import { Button, Dropdown, Menu, notification, Popconfirm } from "antd";
import moment from "moment";
import React, { useMemo } from "react"; import React, { useMemo } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -385,10 +384,7 @@ export function JobsDetailHeaderActions({
jobid: job.id, jobid: job.id,
created_by: currentUser.email, created_by: currentUser.email,
audit: true, audit: true,
text: t("jobs.labels.voidnote", { text: t("jobs.labels.voidnote"),
date: moment().format("MM/DD/yyy"),
time: moment().format("hh:mm a"),
}),
}, },
], ],
}, },

View File

@@ -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.

View File

@@ -151,17 +151,21 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
<Card <Card
style={{ height: "100%" }} style={{ height: "100%" }}
title={ title={
<Link job.vehicle ? (
to={ <Link
disabled to={
? "#" disabled
: job.vehicle && `/manage/vehicles/${job.vehicle.id}` ? "#"
} : job.vehicle && `/manage/vehicles/${job.vehicle.id}`
> }
{`${job.v_model_yr || ""} ${job.v_color || ""} >
{`${job.v_model_yr || ""} ${job.v_color || ""}
${job.v_make_desc || ""} ${job.v_make_desc || ""}
${job.v_model_desc || ""}`} ${job.v_model_desc || ""}`}
</Link> </Link>
) : (
<span></span>
)
} }
> >
<div> <div>

View File

@@ -36,8 +36,11 @@ export function JobsDetailRates({ jobRO, form, job }) {
<CurrencyInput <CurrencyInput
disabled={jobRO} disabled={jobRO}
max={ max={
((job.job_totals && job.job_totals.totals.federal_tax.amount) || Math.round(
0) / 100 (job.job_totals &&
job.job_totals.totals.federal_tax.amount) ||
0
) / 100
} }
/> />
</Form.Item> </Form.Item>

View File

@@ -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
}`;

View File

@@ -1,30 +1,87 @@
import { Button } from "antd"; import { Button, Space } from "antd";
import axios from "axios"; import axios from "axios";
import React from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
import cleanAxios from "../../utils/CleanAxios";
import formatBytes from "../../utils/formatbytes";
export default function JobsDocumentsDownloadButton({ galleryImages }) { export default function JobsDocumentsDownloadButton({
galleryImages,
identifier,
}) {
const { t } = useTranslation(); const { t } = useTranslation();
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),
}) })
.then((r) => { .then((r) => {
window.open(r.data); // window.open(r.data);
downloadAs(
r.data,
`${identifier || "documents"}.zip`,
(progressEvent) => {
setDownload((currentDownloadState) => {
return {
downloaded: progressEvent.loaded || 0,
speed:
(progressEvent.loaded || 0) -
((currentDownloadState && currentDownloadState.downloaded) ||
0),
};
});
},
() => setDownload(null)
);
}); });
}; };
return ( return (
<Button disabled={imagesToDownload.length < 1} onClick={handleDownload}> <>
{t("documents.actions.download")} <Button
</Button> loading={!!download}
disabled={imagesToDownload.length < 1}
onClick={handleDownload}
>
<Space>
<span>{t("documents.actions.download")}</span>
{download && (
<span>{`(${formatBytes(download.downloaded)} @ ${formatBytes(
download.speed
)} / second)`}</span>
)}
</Space>
</Button>
</>
); );
} }
const downloadAs = (url, name, onDownloadProgress, onCompleted) => {
cleanAxios
.get(url, {
headers: {
"Content-Type": "application/octet-stream",
},
responseType: "blob",
onDownloadProgress: onDownloadProgress,
})
.then((response) => {
onCompleted && onCompleted();
const a = document.createElement("a");
const url = window.URL.createObjectURL(response.data);
a.href = url;
a.download = name;
a.click();
})
.catch((err) => {
console.log("error", err);
});
};

View File

@@ -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";
@@ -17,11 +18,30 @@ function JobsDocumentsComponent({
billId, billId,
billsCallback, billsCallback,
totalSize, totalSize,
bodyshop, downloadIdentifier,
ignoreSizeLimit, ignoreSizeLimit,
}) { }) {
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,19 +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}`,
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,
@@ -50,24 +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/c_fill,f_png,h_250,w_250/${value.key}`;
break;
case "raw": case "raw":
thumb = `${window.location.origin}/file.png`; thumb = `${window.location.origin}/file.png`;
break; break;
default: default:
thumb = `${process.env.REACT_APP_CLOUDINARY_ENDPOINT}/${fileType}/upload/${process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS}/${value.key}`; thumb = GenerateThumbUrl(value);
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/${fileType === "video" ? "q_auto/" : ""}${
value.key
}${fileType === "raw" ? `.${value.extension}` : ""}`,
thumbnail: thumb, thumbnail: thumb,
tags: [ tags: [
{ {
@@ -93,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,
@@ -113,11 +123,17 @@ 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}
/> />
<JobsDocumentsDownloadButton galleryImages={galleryImages} /> <JobsDocumentsDownloadButton
galleryImages={galleryImages}
identifier={downloadIdentifier}
/>
<JobsDocumentsDeleteButton <JobsDocumentsDeleteButton
galleryImages={galleryImages} galleryImages={galleryImages}
deletionCallback={billsCallback || refetch} deletionCallback={billsCallback || refetch}
@@ -144,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,
@@ -173,11 +209,12 @@ function JobsDocumentsComponent({
backgroundImage: <FileExcelFilled />, backgroundImage: <FileExcelFilled />,
height: "100%", height: "100%",
width: "100%", width: "100%",
cursor: "pointer",
}; };
}} }}
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"
); );

View File

@@ -23,6 +23,7 @@ export default function JobsDocumentsContainer({
return ( return (
<JobDocuments <JobDocuments
data={(data && data.documents) || documentsList || []} data={(data && data.documents) || documentsList || []}
downloadIdentifier={data && data.jobs_by_pk.ro_number}
totalSize={data && data.documents_aggregate.aggregate.sum.size} totalSize={data && data.documents_aggregate.aggregate.sum.size}
billId={billId} billId={billId}
jobId={jobId} jobId={jobId}

View File

@@ -9,7 +9,6 @@ import { Link, useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
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 { alphaSort } from "../../utils/sorters";
import StartChatButton from "../chat-open-button/chat-open-button.component"; import StartChatButton from "../chat-open-button/chat-open-button.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
@@ -32,7 +31,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
dataIndex: "ro_number", dataIndex: "ro_number",
key: "ro_number", key: "ro_number",
width: "8%", width: "8%",
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number), sorter: true, //(a, b) => alphaSort(a.ro_number, b.ro_number),
sortOrder: sortcolumn === "ro_number" && sortorder, sortOrder: sortcolumn === "ro_number" && sortorder,
render: (text, record) => ( render: (text, record) => (
@@ -44,12 +43,12 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
{ {
title: t("jobs.fields.owner"), title: t("jobs.fields.owner"),
dataIndex: "owner", dataIndex: "ownr_ln",
key: "owner", key: "ownr_ln",
ellipsis: true, ellipsis: true,
// sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln), //sorter: true, // (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
width: "25%", width: "25%",
// sortOrder: sortcolumn === "owner" && sortorder, //sortOrder: sortcolumn === "ownr_ln" && sortorder,
render: (text, record) => { render: (text, record) => {
return record.owner ? ( return record.owner ? (
<Link to={"/manage/owners/" + record.owner.id}> <Link to={"/manage/owners/" + record.owner.id}>
@@ -80,7 +79,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
key: "status", key: "status",
width: "10%", width: "10%",
ellipsis: true, ellipsis: true,
sorter: (a, b) => alphaSort(a.status, b.status), sorter: true, // (a, b) => alphaSort(a.status, b.status),
sortOrder: sortcolumn === "status" && sortorder, sortOrder: sortcolumn === "status" && sortorder,
render: (text, record) => { render: (text, record) => {
return record.status || t("general.labels.na"); return record.status || t("general.labels.na");
@@ -117,7 +116,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
key: "plate_no", key: "plate_no",
width: "8%", width: "8%",
ellipsis: true, ellipsis: true,
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no), sorter: true, //(a, b) => alphaSort(a.plate_no, b.plate_no),
sortOrder: sortcolumn === "plate_no" && sortorder, sortOrder: sortcolumn === "plate_no" && sortorder,
render: (text, record) => { render: (text, record) => {
return record.plate_no ? record.plate_no : ""; return record.plate_no ? record.plate_no : "";
@@ -129,7 +128,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
key: "clm_no", key: "clm_no",
width: "12%", width: "12%",
ellipsis: true, ellipsis: true,
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no), sorter: true, //(a, b) => alphaSort(a.clm_no, b.clm_no),
sortOrder: sortcolumn === "clm_no" && sortorder, sortOrder: sortcolumn === "clm_no" && sortorder,
render: (text, record) => { render: (text, record) => {
return record.clm_no ? ( return record.clm_no ? (
@@ -150,7 +149,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
dataIndex: "clm_total", dataIndex: "clm_total",
key: "clm_total", key: "clm_total",
width: "10%", width: "10%",
sorter: (a, b) => a.clm_total - b.clm_total, sorter: true, //(a, b) => a.clm_total - b.clm_total,
sortOrder: sortcolumn === "clm_total" && sortorder, sortOrder: sortcolumn === "clm_total" && sortorder,
render: (text, record) => { render: (text, record) => {
return record.clm_total ? ( return record.clm_total ? (
@@ -173,7 +172,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
const handleTableChange = (pagination, filters, sorter) => { const handleTableChange = (pagination, filters, sorter) => {
search.page = pagination.current; search.page = pagination.current;
search.sortcolumn = sorter.columnKey; search.sortcolumn = sorter.column && sorter.column.key;
search.sortorder = sorter.order; search.sortorder = sorter.order;
if (filters.status) { if (filters.status) {
search.statusFilters = JSON.stringify(_.flattenDeep(filters.status)); search.statusFilters = JSON.stringify(_.flattenDeep(filters.status));

View File

@@ -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>
),
}, },
{ {

View File

@@ -110,7 +110,7 @@ export function LaborAllocationsTable({
render: (text, record) => ( render: (text, record) => (
<strong <strong
style={{ style={{
color: record.difference > 0 ? "green" : "red", color: record.difference >= 0 ? "green" : "red",
}} }}
> >
{record.difference} {record.difference}

View File

@@ -31,55 +31,19 @@ export default function OwnerDetailFormComponent({ form, loading }) {
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("owners.forms.address")}> <LayoutFormRow header={t("owners.forms.address")}>
<Form.Item <Form.Item label={t("owners.fields.ownr_addr1")} name="ownr_addr1">
label={t("owners.fields.ownr_addr1")}
name="ownr_addr1"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item label={t("owners.fields.ownr_addr2")} name="ownr_addr2"> <Form.Item label={t("owners.fields.ownr_addr2")} name="ownr_addr2">
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item label={t("owners.fields.ownr_city")} name="ownr_city">
label={t("owners.fields.ownr_city")}
name="ownr_city"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item label={t("owners.fields.ownr_st")} name="ownr_st">
label={t("owners.fields.ownr_st")}
name="ownr_st"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item label={t("owners.fields.ownr_zip")} name="ownr_zip">
label={t("owners.fields.ownr_zip")}
name="ownr_zip"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item label={t("owners.fields.ownr_ctry")} name="ownr_ctry"> <Form.Item label={t("owners.fields.ownr_ctry")} name="ownr_ctry">

View File

@@ -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",

View File

@@ -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"]}

View File

@@ -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,

View File

@@ -82,7 +82,31 @@ export function PhonebookFormComponent({
<Form.Item label={t("phonebook.fields.lastname")} name="lastname"> <Form.Item label={t("phonebook.fields.lastname")} name="lastname">
<Input disabled={hasNoAccess} /> <Input disabled={hasNoAccess} />
</Form.Item> </Form.Item>
<Form.Item label={t("phonebook.fields.company")} name="company"> <Form.Item
label={t("phonebook.fields.company")}
name="company"
dependencies={["firstname", "lastname"]}
rules={[
({ getFieldsValue }) => ({
validator(rule, value) {
const { firstname, lastname, company } = getFieldsValue([
"firstname",
"lastname",
"company",
]);
if (
(firstname && firstname.trim() !== "") ||
(lastname && lastname.trim() !== "") ||
(company && company.trim() !== "")
) {
return Promise.resolve();
}
return Promise.reject(t("phonebook.labels.onenamerequired"));
},
}),
]}
>
<Input disabled={hasNoAccess} /> <Input disabled={hasNoAccess} />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>

View File

@@ -225,8 +225,10 @@ const r = ({ technician, state }) => {
sortOrder: sortOrder:
state.sortedInfo.columnKey === "totalhours" && state.sortedInfo.order, state.sortedInfo.columnKey === "totalhours" && state.sortedInfo.order,
render: (text, record) => render: (text, record) =>
record.labhrs.aggregate.sum.mod_lb_hrs + (
record.larhrs.aggregate.sum.mod_lb_hrs, record.labhrs.aggregate.sum.mod_lb_hrs +
record.larhrs.aggregate.sum.mod_lb_hrs
).toFixed(1),
}, },
{ {
title: i18n.t("production.labels.alert"), title: i18n.t("production.labels.alert"),

View File

@@ -1,6 +1,20 @@
import moment from "moment"; import moment from "moment";
import React, { useMemo } from "react"; import React, { useMemo } from "react";
export default function ProductionlistColumnTouchTime({ job }) { import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(ProductionlistColumnTouchTime);
export function ProductionlistColumnTouchTime({ bodyshop, job }) {
let ct = useMemo(() => { let ct = useMemo(() => {
if (!!job.actual_in) { if (!!job.actual_in) {
const totalHrs = const totalHrs =
@@ -18,5 +32,7 @@ export default function ProductionlistColumnTouchTime({ job }) {
return 0; return 0;
}, [job]); }, [job]);
return <span>{ct}</span>; const underTarget = ct < bodyshop.target_touchtime;
return <span style={{ color: underTarget && "tomato" }}>{ct}</span>;
} }

View File

@@ -16,7 +16,7 @@ import PhoneFormItem, {
} from "../form-items-formatted/phone-form-item.component"; } from "../form-items-formatted/phone-form-item.component";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component"; import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
export default function ShopInfoGeneral({ form }) { export default function ShopInfoGeneral({ form }) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
@@ -425,6 +425,18 @@ export default function ShopInfoGeneral({ form }) {
> >
<InputNumber min={0} max={1} precision={2} /> <InputNumber min={0} max={1} precision={2} />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.jc_hourly_rates.mapa")}
name={["jc_hourly_rates", "mapa"]}
>
<CurrencyInput />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.jc_hourly_rates.mash")}
name={["jc_hourly_rates", "mash"]}
>
<CurrencyInput />
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}> <LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}>
<Form.List name={["md_messaging_presets"]}> <Form.List name={["md_messaging_presets"]}>

View File

@@ -76,28 +76,10 @@ export default function VehicleDetailFormComponent({ form, loading }) {
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("vehicles.forms.registration")}> <LayoutFormRow header={t("vehicles.forms.registration")}>
<Form.Item <Form.Item label={t("vehicles.fields.plate_st")} name="plate_st">
label={t("vehicles.fields.plate_st")}
name="plate_st"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item label={t("vehicles.fields.plate_no")} name="plate_no">
label={t("vehicles.fields.plate_no")}
name="plate_no"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input /> <Input />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>

View File

@@ -39,7 +39,7 @@ function VehicleDetailFormContainer({ vehicle, refetch }) {
return ( return (
<> <>
<PageHeader <PageHeader
title={t("menus.header.owners")} title={t("menus.header.vehicles")}
extra={ extra={
<Button <Button
type="primary" type="primary"

View File

@@ -85,6 +85,7 @@ export const INSERT_APPOINTMENT = gql`
title title
isintake isintake
block block
color
} }
} }
update_jobs( update_jobs(
@@ -110,6 +111,7 @@ export const QUERY_APPOINTMENT_BY_DATE = gql`
title title
isintake isintake
block block
color
job { job {
alt_transport alt_transport
ro_number ro_number

View File

@@ -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

View File

@@ -87,6 +87,7 @@ export const QUERY_BODYSHOP = gql`
md_ccc_rates md_ccc_rates
enforce_referral enforce_referral
website website
jc_hourly_rates
employees { employees {
id id
active active
@@ -171,6 +172,7 @@ export const UPDATE_SHOP = gql`
md_ccc_rates md_ccc_rates
enforce_referral enforce_referral
website website
jc_hourly_rates
employees { employees {
id id
first_name first_name
@@ -197,6 +199,28 @@ export const QUERY_INTAKE_CHECKLIST = gql`
scheduled_delivery scheduled_delivery
intakechecklist intakechecklist
status status
labhrs: joblines_aggregate(
where: {
_and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }]
}
) {
aggregate {
sum {
mod_lb_hrs
}
}
}
larhrs: joblines_aggregate(
where: {
_and: [{ mod_lbr_ty: { _eq: "LAR" } }, { removed: { _eq: false } }]
}
) {
aggregate {
sum {
mod_lb_hrs
}
}
}
} }
} }
`; `;

View File

@@ -1,7 +1,26 @@
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) {
id
ro_number
}
documents_aggregate(where: { jobid: { _eq: $jobId } }) { documents_aggregate(where: { jobid: { _eq: $jobId } }) {
aggregate { aggregate {
sum { sum {
@@ -9,15 +28,14 @@ export const GET_DOCUMENTS_BY_JOB = gql`
} }
} }
} }
documents( documents(order_by: { takenat: desc }, where: { jobid: { _eq: $jobId } }) {
order_by: { updated_at: desc }
where: { jobid: { _eq: $jobId } }
) {
id id
name name
key key
type type
size size
takenat
extension
bill { bill {
id id
invoice_number invoice_number
@@ -51,6 +69,7 @@ export const INSERT_NEW_DOCUMENT = gql`
name name
key key
size size
takenat
} }
} }
} }
@@ -70,7 +89,7 @@ export const QUERY_TEMPORARY_DOCS = gql`
query QUERY_TEMPORARY_DOCS { query QUERY_TEMPORARY_DOCS {
documents( documents(
where: { jobid: { _is_null: true } } where: { jobid: { _is_null: true } }
order_by: { updated_at: desc } order_by: { takenat: desc }
) { ) {
id id
name name
@@ -78,6 +97,7 @@ export const QUERY_TEMPORARY_DOCS = gql`
type type
extension extension
size size
takenat
} }
} }
`; `;
@@ -94,6 +114,7 @@ export const UPDATE_DOCUMENT = gql`
type type
key key
size size
takenat
} }
} }
`; `;

View File

@@ -528,6 +528,7 @@ export const GET_JOB_BY_PK = gql`
tax_part tax_part
db_ref db_ref
manual_line manual_line
prt_dsmk_p
billlines(limit: 1, order_by: { bill: { date: desc } }) { billlines(limit: 1, order_by: { bill: { date: desc } }) {
id id
quantity quantity
@@ -564,6 +565,7 @@ export const GET_JOB_BY_PK = gql`
stripeid stripeid
transactionid transactionid
memo memo
date
} }
cccontracts { cccontracts {
id id
@@ -1055,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) {
@@ -1484,6 +1716,7 @@ export const QUERY_JOB_CLOSE_DETAILS = gql`
op_code_desc op_code_desc
profitcenter_labor profitcenter_labor
profitcenter_part profitcenter_part
prt_dsmk_p
} }
} }
} }

View File

@@ -1,6 +1,6 @@
import { AlertOutlined } from "@ant-design/icons"; import { AlertOutlined } from "@ant-design/icons";
import * as Sentry from "@sentry/react"; import * as Sentry from "@sentry/react";
import { Button, notification } from "antd"; import { Button, notification, Space } from "antd";
//import "antd/dist/antd.css"; //import "antd/dist/antd.css";
import "antd/dist/antd.less"; import "antd/dist/antd.less";
import Dinero from "dinero.js"; import Dinero from "dinero.js";
@@ -26,8 +26,7 @@ Dinero.globalRoundingMode = "HALF_EVEN";
if (process.env.NODE_ENV !== "development") { if (process.env.NODE_ENV !== "development") {
Sentry.init({ Sentry.init({
dsn: dsn: "https://fd7e89369b6b4bdc9c6c4c9f22fa4ee4@o492140.ingest.sentry.io/5651027",
"https://fd7e89369b6b4bdc9c6c4c9f22fa4ee4@o492140.ingest.sentry.io/5651027",
integrations: [ integrations: [
// new Integrations.BrowserTracing(), // new Integrations.BrowserTracing(),
// new Sentry.Integrations.Breadcrumbs({ console: true }), // new Sentry.Integrations.Breadcrumbs({ console: true }),
@@ -57,21 +56,30 @@ const onServiceWorkerUpdate = (registration) => {
console.log("onServiceWorkerUpdate", registration); console.log("onServiceWorkerUpdate", registration);
const btn = ( const btn = (
<Button <Space flex>
type="primary" <Button
onClick={async () => { onClick={async () => {
if (registration && registration.waiting) { window.open("https://imex-online.noticeable.news/", "_blank");
await registration.unregister(); }}
// Makes Workbox call skipWaiting() >
registration.waiting.postMessage({ type: "SKIP_WAITING" }); {i18n.t("general.actions.viewreleasenotes")}
// Once the service worker is unregistered, we can reload the page to let </Button>
// the browser download a fresh copy of our app (invalidating the cache) <Button
window.location.reload(); type="primary"
} onClick={async () => {
}} if (registration && registration.waiting) {
> await registration.unregister();
{i18n.t("general.actions.refresh")} // Makes Workbox call skipWaiting()
</Button> registration.waiting.postMessage({ type: "SKIP_WAITING" });
// Once the service worker is unregistered, we can reload the page to let
// the browser download a fresh copy of our app (invalidating the cache)
window.location.reload();
}
}}
>
{i18n.t("general.actions.refresh")}
</Button>
</Space>
); );
notification.open({ notification.open({
icon: <AlertOutlined />, icon: <AlertOutlined />,

View File

@@ -35,13 +35,14 @@ export function AllJobs({ setBreadcrumbs, setSelectedHeader }) {
offset: page ? (page - 1) * 25 : 0, offset: page ? (page - 1) * 25 : 0,
limit: 25, limit: 25,
...(statusFilters ? { statusList: JSON.parse(statusFilters) } : {}), ...(statusFilters ? { statusList: JSON.parse(statusFilters) } : {}),
order: [ order: [
{ {
[sortcolumn || "created_at"]: sortorder [sortcolumn || "created_at"]:
? sortorder === "descend" sortorder && sortorder !== "false"
? "desc" ? sortorder === "descend"
: "asc" ? "desc"
: "desc", : "asc"
: "desc",
}, },
], ],
}, },

View File

@@ -155,6 +155,13 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
layout="vertical" layout="vertical"
autoComplete={"off"} autoComplete={"off"}
initialValues={{ initialValues={{
tax_tow_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
tax_str_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
tax_paint_mat_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
tax_shop_mat_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
tax_sub_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
tax_lbr_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
tax_levies_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
parts_tax_rates: { parts_tax_rates: {
PAA: { PAA: {
prt_type: "PAA", prt_type: "PAA",
@@ -220,6 +227,30 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
prt_tax_in: true, prt_tax_in: true,
prt_tax_rt: bodyshop.bill_tax_rates.state_tax_rate / 100, prt_tax_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
}, },
PASL: {
prt_type: "PASL",
prt_discp: 0,
prt_mktyp: false,
prt_mkupp: 0,
prt_tax_in: true,
prt_tax_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
},
PAP: {
prt_type: "PAP",
prt_discp: 0,
prt_mktyp: false,
prt_mkupp: 0,
prt_tax_in: true,
prt_tax_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
},
PAO: {
prt_type: "PAO",
prt_discp: 0,
prt_mktyp: false,
prt_mkupp: 0,
prt_tax_in: true,
prt_tax_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
},
}, },
}} }}
> >

View File

@@ -181,7 +181,10 @@ const mapStateToProps = createStructuredSelector({
export function Manage({ match, conflict, bodyshop }) { export function Manage({ match, conflict, bodyshop }) {
const { t } = useTranslation(); const { t } = useTranslation();
useEffect(() => {
const widgetId = "IABVNO4scRKY11XBQkNr";
window.noticeable.render("widget", widgetId);
}, []);
useEffect(() => { useEffect(() => {
document.title = t("titles.app"); document.title = t("titles.app");
}, [t]); }, [t]);
@@ -383,11 +386,22 @@ export function Manage({ match, conflict, bodyshop }) {
<ChatAffixContainer /> <ChatAffixContainer />
<BackTop /> <BackTop />
<Footer> <Footer>
<div style={{ textAlign: "center", margin: "1rem 0rem" }}> <div
<div> style={{
{`ImEX Online V.${process.env.NODE_ENV} - ${ display: "flex",
process.env.REACT_APP_GIT_SHA flexDirection: "column",
} - ${preval`module.exports = new Date().toLocaleString("en-US", {timeZone: "America/Los_Angeles"});`}`} justifyContent: "center",
alignItems: "center",
margin: "1rem 0rem",
}}
>
<div style={{ display: "flex" }}>
<div>
{`ImEX Online ${
process.env.REACT_APP_GIT_SHA
} - ${preval`module.exports = new Date().toLocaleString("en-US", {timeZone: "America/Los_Angeles"});`}`}
</div>
<div id="noticeable-widget" style={{ marginLeft: "1rem" }} />
</div> </div>
<Link to="/about" target="_blank" style={{ color: "#ccc" }}> <Link to="/about" target="_blank" style={{ color: "#ccc" }}>
Disclaimer Disclaimer

View File

@@ -48,3 +48,8 @@ export const setPartnerVersion = (version) => ({
type: ApplicationActionTypes.SET_PARTNER_VERSION, type: ApplicationActionTypes.SET_PARTNER_VERSION,
payload: version, payload: version,
}); });
export const setOnline = (isOnline) => ({
type: ApplicationActionTypes.SET_ONLINE_STATUS,
payload: isOnline,
});

View File

@@ -2,6 +2,7 @@ import ApplicationActionTypes from "./application.types";
const INITIAL_STATE = { const INITIAL_STATE = {
loading: false, loading: false,
online: true,
breadcrumbs: [], breadcrumbs: [],
recentItems: [], recentItems: [],
selectedHeader: "home", selectedHeader: "home",
@@ -21,6 +22,11 @@ const applicationReducer = (state = INITIAL_STATE, action) => {
...state, ...state,
selectedHeader: action.payload, selectedHeader: action.payload,
}; };
case ApplicationActionTypes.SET_ONLINE_STATUS:
return {
...state,
online: action.payload,
};
case ApplicationActionTypes.ADD_RECENT_ITEM: case ApplicationActionTypes.ADD_RECENT_ITEM:
return { return {
...state, ...state,

View File

@@ -40,3 +40,7 @@ export const selectJobReadOnly = createSelector(
[selectApplication], [selectApplication],
(application) => application.jobReadOnly (application) => application.jobReadOnly
); );
export const selectOnline = createSelector(
[selectApplication],
(application) => application.online
);

View File

@@ -9,5 +9,6 @@ const ApplicationActionTypes = {
SET_SELECTED_HEADER: "SET_SELECTED_HEADER", SET_SELECTED_HEADER: "SET_SELECTED_HEADER",
SET_JOB_READONLY: "SET_JOB_READONLY", SET_JOB_READONLY: "SET_JOB_READONLY",
SET_PARTNER_VERSION: "SET_PARTNER_VERSION", SET_PARTNER_VERSION: "SET_PARTNER_VERSION",
SET_ONLINE_STATUS: "SET_ONLINE_STATUS",
}; };
export default ApplicationActionTypes; export default ApplicationActionTypes;

View File

@@ -1,4 +1,6 @@
import Fingerprint2 from "@fingerprintjs/fingerprintjs"; import Fingerprint2 from "@fingerprintjs/fingerprintjs";
import { notification } from "antd";
import i18next from "i18next";
import LogRocket from "logrocket"; import LogRocket from "logrocket";
import { all, call, delay, put, select, takeLatest } from "redux-saga/effects"; import { all, call, delay, put, select, takeLatest } from "redux-saga/effects";
import { import {
@@ -100,6 +102,10 @@ export function* updateUserDetails(userDetails) {
try { try {
yield updateCurrentUser(userDetails.payload); yield updateCurrentUser(userDetails.payload);
yield put(updateUserDetailsSuccess(userDetails.payload)); yield put(updateUserDetailsSuccess(userDetails.payload));
notification.open({
type: "success",
message: i18next.t("profile.successes.updated"),
});
} catch (error) { } catch (error) {
//yield put(signOutFailure(error.message)); //yield put(signOutFailure(error.message));
} }
@@ -158,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);
@@ -214,10 +230,16 @@ 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
); );
if (authRecord[0] && authRecord[0].user.validemail) {
window.$crisp.push(["set", "user:email", [authRecord[0].user.email]]);
}
yield put(setAuthlevel(authRecord[0] ? authRecord[0].authlevel : 0)); yield put(setAuthlevel(authRecord[0] ? authRecord[0].authlevel : 0));
yield put( yield put(
updateUserDetailsSuccess( updateUserDetailsSuccess(

View File

@@ -6,7 +6,7 @@
}, },
"errors": { "errors": {
"deleting": "Error encountered while deleting allocation. {{message}}", "deleting": "Error encountered while deleting allocation. {{message}}",
"saving": "Error while allocating. {{message}}", "saving": "Error while allocating. {{message}}",
"validation": "Please ensure all fields are entered correctly. " "validation": "Please ensure all fields are entered correctly. "
}, },
"fields": { "fields": {
@@ -167,6 +167,7 @@
"exported": "Bill exported successfully." "exported": "Bill exported successfully."
}, },
"validation": { "validation": {
"manualinhouse": "Manual posting to the in house vendor is restricted. ",
"unique_invoice_number": "This invoice number has already been entered for this vendor." "unique_invoice_number": "This invoice number has already been entered for this vendor."
} }
}, },
@@ -218,6 +219,10 @@
"invoice_federal_tax_rate": "Invoices - Federal Tax Rate", "invoice_federal_tax_rate": "Invoices - Federal Tax Rate",
"invoice_local_tax_rate": "Invoices - Local Tax Rate", "invoice_local_tax_rate": "Invoices - Local Tax Rate",
"invoice_state_tax_rate": "Invoices - Provincial/State Tax Rate", "invoice_state_tax_rate": "Invoices - Provincial/State Tax Rate",
"jc_hourly_rates": {
"mapa": "Job Costing - Paint Materials Hourly Cost Rate",
"mash": "Job Costing - Shop Materials Hourly Cost Rate"
},
"lastnumberworkingdays": "Scoreboard - Last Number of Working Days", "lastnumberworkingdays": "Scoreboard - Last Number of Working Days",
"logo_img_path": "Shop Logo", "logo_img_path": "Shop Logo",
"logo_img_path_height": "Logo Image Height", "logo_img_path_height": "Logo Image Height",
@@ -555,7 +560,7 @@
"driverinformation": "Driver's Information", "driverinformation": "Driver's Information",
"findcontract": "Find Contract", "findcontract": "Find Contract",
"findermodal": "Contract Finder", "findermodal": "Contract Finder",
"noteconvertedfrom": "R.O. created from converted Courtesy Car Contract {{agreementnumber}}.", "noteconvertedfrom": "R.O. created from converted Courtesy Car Contract {{agreementnumber}}.",
"populatefromjob": "Populate from Job", "populatefromjob": "Populate from Job",
"rates": "Contract Rates", "rates": "Contract Rates",
"time": "Time", "time": "Time",
@@ -673,7 +678,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"
@@ -683,7 +688,8 @@
"deleting_cloudinary": "Error deleting document from storage. {{message}}", "deleting_cloudinary": "Error deleting document from storage. {{message}}",
"getpresignurl": "Error obtaining presigned URL for document. {{message}}", "getpresignurl": "Error obtaining presigned URL for document. {{message}}",
"insert": "Unable to upload file. {{message}}", "insert": "Unable to upload file. {{message}}",
"nodocuments": "There are no documents." "nodocuments": "There are no documents.",
"updating": "Error updating document. {{error}}"
}, },
"labels": { "labels": {
"confirmdelete": "Are you sure you want to delete these documents. This CANNOT be undone.", "confirmdelete": "Are you sure you want to delete these documents. This CANNOT be undone.",
@@ -696,10 +702,12 @@
"upload": "Upload", "upload": "Upload",
"upload_limitexceeded": "Uploading all selected documents will exceed the job storage limit for your shop. ", "upload_limitexceeded": "Uploading all selected documents will exceed the job storage limit for your shop. ",
"upload_limitexceeded_title": "Unable to upload document(s)", "upload_limitexceeded_title": "Unable to upload document(s)",
"uploading": "Uploading...",
"usage": "of job storage used. ({{used}} / {{total}})" "usage": "of job storage used. ({{used}} / {{total}})"
}, },
"successes": { "successes": {
"delete": "Document(s) deleted successfully.", "delete": "Document(s) deleted successfully.",
"edituploaded": "Edited document uploaded successfully. Please close this window and refresh the documents list.",
"insert": "Uploaded document successfully. ", "insert": "Uploaded document successfully. ",
"updated": "Document updated successfully. " "updated": "Document updated successfully. "
} }
@@ -762,6 +770,11 @@
"unique_employee_number": "You must enter a unique employee number." "unique_employee_number": "You must enter a unique employee number."
} }
}, },
"exportlogs": {
"fields": {
"createdat": "Created At"
}
},
"general": { "general": {
"actions": { "actions": {
"add": "Add", "add": "Add",
@@ -785,7 +798,11 @@
"selectall": "Select All", "selectall": "Select All",
"submit": "Submit", "submit": "Submit",
"submitticket": "Submit a Support Ticket", "submitticket": "Submit a Support Ticket",
"view": "View" "view": "View",
"viewreleasenotes": "See What's Changed"
},
"errors": {
"notfound": "No record was found."
}, },
"itemtypes": { "itemtypes": {
"contract": "CC Contract", "contract": "CC Contract",
@@ -820,6 +837,8 @@
"monday": "Monday", "monday": "Monday",
"na": "N/A", "na": "N/A",
"no": "No", "no": "No",
"nointernet": "It looks like you're not connected to the internet.",
"nointernet_sub": "Please check your connection and try again. ",
"none": "None", "none": "None",
"out": "Out", "out": "Out",
"password": "Password", "password": "Password",
@@ -950,6 +969,7 @@
}, },
"profitcenter_labor": "Profit Center: Labor", "profitcenter_labor": "Profit Center: Labor",
"profitcenter_part": "Profit Center: Part", "profitcenter_part": "Profit Center: Part",
"prt_dsmk_p": "Line Markup %",
"status": "Status", "status": "Status",
"total": "Total", "total": "Total",
"unq_seq": "Seq #" "unq_seq": "Seq #"
@@ -1122,6 +1142,7 @@
"ownr_ea": "Email", "ownr_ea": "Email",
"ownr_ph1": "Phone 1", "ownr_ph1": "Phone 1",
"paa": "Aftermarket", "paa": "Aftermarket",
"pac": "Rechromed",
"pae": "Existing", "pae": "Existing",
"pag": "Glass", "pag": "Glass",
"pal": "LKQ", "pal": "LKQ",
@@ -1226,6 +1247,8 @@
"rate": "PVRT Rate" "rate": "PVRT Rate"
}, },
"ca_gst_all_if_null": "If the job is marked as a \"GST Registrant\" and this value is set to $0, the customer will be responsible for paying all of the GST by default. ", "ca_gst_all_if_null": "If the job is marked as a \"GST Registrant\" and this value is set to $0, the customer will be responsible for paying all of the GST by default. ",
"calc_repair_days": "Calculated Repair Days",
"calc_repair_days_tt": "This is the approximate number of days required to complete the repair according to the target touch time in your shop configuration (current set to {{target_touchtime}}).",
"cards": { "cards": {
"customer": "Customer Information", "customer": "Customer Information",
"damage": "Area of Damage", "damage": "Area of Damage",
@@ -1279,7 +1302,7 @@
"gppercent": "% G.P.", "gppercent": "% G.P.",
"hrs_claimed": "Hours Claimed", "hrs_claimed": "Hours Claimed",
"hrs_total": "Hours Total", "hrs_total": "Hours Total",
"importnote": "The job was initially imported on {{date}} at {{time}}.", "importnote": "The job was initially imported.",
"inproduction": "In Production", "inproduction": "In Production",
"intakechecklist": "Intake Checklist", "intakechecklist": "Intake Checklist",
"job": "Job Details", "job": "Job Details",
@@ -1314,6 +1337,7 @@
"partstotal": "This is the total of all parts and sublet amounts on the vehicle (some of these may require an in-house invoice).<br/>\nItems such as shop and paint materials, labor online lines, etc. are not included in this total.", "partstotal": "This is the total of all parts and sublet amounts on the vehicle (some of these may require an in-house invoice).<br/>\nItems such as shop and paint materials, labor online lines, etc. are not included in this total.",
"totalreturns": "The total amount of returns created for this job." "totalreturns": "The total amount of returns created for this job."
}, },
"prt_dsmk_total": "Line Item Markup",
"rates": "Rates", "rates": "Rates",
"rates_subtotal": "All Rates Subtotal", "rates_subtotal": "All Rates Subtotal",
"reconciliation": { "reconciliation": {
@@ -1339,19 +1363,19 @@
"state_tax_amt": "Provincial/State Taxes", "state_tax_amt": "Provincial/State Taxes",
"subletstotal": "Sublets Total", "subletstotal": "Sublets Total",
"subtotal": "Subtotal", "subtotal": "Subtotal",
"supplementnote": "The job had a supplement imported on {{date}} at {{time}}.", "supplementnote": "The job had a supplement imported.",
"suspense": "Suspense", "suspense": "Suspense",
"total_cost": "Total Cost", "total_cost": "Total Cost",
"total_cust_payable": "Total Customer Amount Payable", "total_cust_payable": "Total Customer Amount Payable",
"total_repairs": "Total Repairs", "total_repairs": "Total Repairs",
"total_sales": "Total Sales", "total_sales": "Total Sales",
"totals": "Totals", "totals": "Totals",
"unvoidnote": "This job was unvoided by {{email}}.", "unvoidnote": "This job was unvoided.",
"vehicle_info": "Vehicle", "vehicle_info": "Vehicle",
"vehicleassociation": "Vehicle Association", "vehicleassociation": "Vehicle Association",
"viewallocations": "View Allocations", "viewallocations": "View Allocations",
"voidjob": "Are you sure you want to void this job? This cannot be easily undone. ", "voidjob": "Are you sure you want to void this job? This cannot be easily undone. ",
"voidnote": "This repair order was voided on {{date}} at {{time}}." "voidnote": "This job was voided."
}, },
"successes": { "successes": {
"addedtoproduction": "Job added to production board.", "addedtoproduction": "Job added to production board.",
@@ -1398,6 +1422,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",
@@ -1580,6 +1605,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"
@@ -1662,6 +1688,8 @@
"state": "Province/State" "state": "Province/State"
}, },
"labels": { "labels": {
"noneselected": "No phone book entry selected. ",
"onenamerequired": "At least one name related field is required.",
"vendorcategory": "Vendor" "vendorcategory": "Vendor"
}, },
"successes": { "successes": {
@@ -1730,7 +1758,10 @@
"payment_receipt": "Payment Receipt", "payment_receipt": "Payment Receipt",
"payment_request": "Payment Request", "payment_request": "Payment Request",
"payments_by_job": "Job Payments", "payments_by_job": "Job Payments",
"purchases_by_ro_detail": "Purchases - Detail",
"purchases_by_ro_summary": "Purchases - Summary",
"qc_sheet": "Quality Control Sheet", "qc_sheet": "Quality Control Sheet",
"ro_totals": "RO Totals",
"ro_with_description": "RO Summary with Descriptions", "ro_with_description": "RO Summary with Descriptions",
"supplement_request": "Supplement Request", "supplement_request": "Supplement Request",
"thank_you_ro": "Thank You Letter", "thank_you_ro": "Thank You Letter",
@@ -1813,6 +1844,9 @@
}, },
"labels": { "labels": {
"activeshop": "Active Shop" "activeshop": "Active Shop"
},
"successes": {
"updated": "Profile updated successfully."
} }
}, },
"reportcenter": { "reportcenter": {
@@ -1828,6 +1862,7 @@
"objects": { "objects": {
"appointments": "Appointments", "appointments": "Appointments",
"bills": "Bills", "bills": "Bills",
"exportlogs": "Export Logs",
"jobs": "Jobs", "jobs": "Jobs",
"payments": "Payments", "payments": "Payments",
"timetickets": "Timetickets" "timetickets": "Timetickets"
@@ -1835,27 +1870,41 @@
"vendor": "Vendor" "vendor": "Vendor"
}, },
"templates": { "templates": {
"attendance_detail": "Attendance (All Employees)",
"attendance_employee": "Employee Attendance", "attendance_employee": "Employee Attendance",
"attendance_summary": "Attendance Summary (All Employees)",
"credits_not_received_date": "Credits not Received by Date", "credits_not_received_date": "Credits not Received by Date",
"estimator_detail": "Jobs by Estimator (Detail)", "estimator_detail": "Jobs by Estimator (Detail)",
"estimator_summary": "Jobs by Estimator (Summary)", "estimator_summary": "Jobs by Estimator (Summary)",
"export_payables": "Export Log - Payables",
"export_payments": "Export Log - Payments",
"export_receivables": "Export Log - Receivables",
"gsr_by_delivery_date": "Gross Sales by Delivery Date",
"gsr_by_estimator": "Gross Sales by Estimator", "gsr_by_estimator": "Gross Sales by Estimator",
"gsr_by_exported_date": "Gross Sales by Export Date",
"gsr_by_ins_co": "Gross Sales by Insurance Company'",
"gsr_by_make": "Gross Sales by Vehicle Make", "gsr_by_make": "Gross Sales by Vehicle Make",
"gsr_by_referral": "Gross Sales by Referral Source",
"gsr_by_ro": "Gross Sales by RO",
"gsr_labor_only": "Gross Sales - Labor Only", "gsr_labor_only": "Gross Sales - Labor Only",
"hours_sold_detail_closed": "Hours Sold Detail - Closed", "hours_sold_detail_closed": "Hours Sold Detail - Closed",
"hours_sold_detail_closed_source": "Hours Sold Detail - Closed by Source", "hours_sold_detail_closed_ins_co": "Hours Sold Detail - Closed by Source",
"hours_sold_detail_open": "Hours Sold Detail - Open", "hours_sold_detail_open": "Hours Sold Detail - Open",
"hours_sold_detail_open_source": "Hours Sold Detail - Open by Source", "hours_sold_detail_open_ins_co": "Hours Sold Detail - Open by Source",
"hours_sold_summary_closed": "Hours Sold Summary - Closed", "hours_sold_summary_closed": "Hours Sold Summary - Closed",
"hours_sold_summary_closed_source": "Hours Sold Summary - Closed by Source", "hours_sold_summary_closed_ins_co": "Hours Sold Summary - Closed by Source",
"hours_sold_summary_open": "Hours Sold Summary - Open", "hours_sold_summary_open": "Hours Sold Summary - Open",
"hours_sold_summary_open_source": "Hours Sold Summary - Open by Source", "hours_sold_summary_open_ins_co": "Hours Sold Summary - Open by Source",
"job_costing_ro_date_detail": "Job Costing by RO - Detail", "job_costing_ro_date_detail": "Job Costing by RO - Detail",
"job_costing_ro_date_summary": "Job Costing by RO - Summary", "job_costing_ro_date_summary": "Job Costing by RO - Summary",
"job_costing_ro_estimator": "Job Costing by Estimator", "job_costing_ro_estimator": "Job Costing by Estimator",
"job_costing_ro_source": "Job Costing by RO Source", "job_costing_ro_ins_co": "Job Costing by RO Source",
"open_orders": "Open Orders by Date", "open_orders": "Open Orders by Date",
"payments_by_date_type": "Payments by Date Range", "open_orders_estimator": "Open Orders by Estimator",
"open_orders_ins_co": "Open Orders by Insurance Company",
"parts_backorder": "Backordered Parts",
"payments_by_date": "Payments by Date",
"payments_by_date_type": "Payments by Date and Type",
"purchases_by_cost_center_detail": "Purchases by Cost Center (Detail)", "purchases_by_cost_center_detail": "Purchases by Cost Center (Detail)",
"purchases_by_cost_center_summary": "Purchases by Cost Center (Summary)", "purchases_by_cost_center_summary": "Purchases by Cost Center (Summary)",
"purchases_by_date_range_detail": "Purchases by Date - Detail", "purchases_by_date_range_detail": "Purchases by Date - Detail",
@@ -1865,11 +1914,15 @@
"purchases_grouped_by_vendor_detailed": "Purchases Grouped by Vendor - Detailed", "purchases_grouped_by_vendor_detailed": "Purchases Grouped by Vendor - Detailed",
"purchases_grouped_by_vendor_summary": "Purchases Grouped by Vendor - Summary", "purchases_grouped_by_vendor_summary": "Purchases Grouped by Vendor - Summary",
"schedule": "Appointment Schedule", "schedule": "Appointment Schedule",
"supplement_ratio_source": "Supplement Ratio by Source", "supplement_ratio_ins_co": "Supplement Ratio by Source",
"thank_you_date": "Thank You Letters",
"timetickets": "Time Tickets", "timetickets": "Time Tickets",
"timetickets_employee": "Employee Time Tickets", "timetickets_employee": "Employee Time Tickets",
"timetickets_summary": "Time Tickets Summary", "timetickets_summary": "Time Tickets Summary",
"void_ros": "Void ROs" "unclaimed_hrs": "Unclaimed Hours",
"void_ros": "Void ROs",
"work_in_progress_labour": "Work in Progress - Labor",
"work_in_progress_payables": "Work in Progress - Payables"
} }
}, },
"scoreboard": { "scoreboard": {

View File

@@ -167,6 +167,7 @@
"exported": "" "exported": ""
}, },
"validation": { "validation": {
"manualinhouse": "",
"unique_invoice_number": "" "unique_invoice_number": ""
} }
}, },
@@ -218,6 +219,10 @@
"invoice_federal_tax_rate": "", "invoice_federal_tax_rate": "",
"invoice_local_tax_rate": "", "invoice_local_tax_rate": "",
"invoice_state_tax_rate": "", "invoice_state_tax_rate": "",
"jc_hourly_rates": {
"mapa": "",
"mash": ""
},
"lastnumberworkingdays": "", "lastnumberworkingdays": "",
"logo_img_path": "", "logo_img_path": "",
"logo_img_path_height": "", "logo_img_path_height": "",
@@ -683,7 +688,8 @@
"deleting_cloudinary": "", "deleting_cloudinary": "",
"getpresignurl": "Error al obtener la URL prescrita para el documento. {{message}}", "getpresignurl": "Error al obtener la URL prescrita para el documento. {{message}}",
"insert": "Incapaz de cargar el archivo. {{message}}", "insert": "Incapaz de cargar el archivo. {{message}}",
"nodocuments": "No hay documentos" "nodocuments": "No hay documentos",
"updating": ""
}, },
"labels": { "labels": {
"confirmdelete": "", "confirmdelete": "",
@@ -696,10 +702,12 @@
"upload": "Subir", "upload": "Subir",
"upload_limitexceeded": "", "upload_limitexceeded": "",
"upload_limitexceeded_title": "", "upload_limitexceeded_title": "",
"uploading": "",
"usage": "" "usage": ""
}, },
"successes": { "successes": {
"delete": "Documento eliminado con éxito.", "delete": "Documento eliminado con éxito.",
"edituploaded": "",
"insert": "Documento cargado con éxito.", "insert": "Documento cargado con éxito.",
"updated": "" "updated": ""
} }
@@ -762,6 +770,11 @@
"unique_employee_number": "" "unique_employee_number": ""
} }
}, },
"exportlogs": {
"fields": {
"createdat": ""
}
},
"general": { "general": {
"actions": { "actions": {
"add": "", "add": "",
@@ -785,7 +798,11 @@
"selectall": "", "selectall": "",
"submit": "", "submit": "",
"submitticket": "", "submitticket": "",
"view": "" "view": "",
"viewreleasenotes": ""
},
"errors": {
"notfound": ""
}, },
"itemtypes": { "itemtypes": {
"contract": "", "contract": "",
@@ -820,6 +837,8 @@
"monday": "", "monday": "",
"na": "N / A", "na": "N / A",
"no": "", "no": "",
"nointernet": "",
"nointernet_sub": "",
"none": "", "none": "",
"out": "Afuera", "out": "Afuera",
"password": "", "password": "",
@@ -950,6 +969,7 @@
}, },
"profitcenter_labor": "", "profitcenter_labor": "",
"profitcenter_part": "", "profitcenter_part": "",
"prt_dsmk_p": "",
"status": "Estado", "status": "Estado",
"total": "", "total": "",
"unq_seq": "Seq #" "unq_seq": "Seq #"
@@ -1122,6 +1142,7 @@
"ownr_ea": "Email", "ownr_ea": "Email",
"ownr_ph1": "Teléfono 1", "ownr_ph1": "Teléfono 1",
"paa": "", "paa": "",
"pac": "",
"pae": "", "pae": "",
"pag": "", "pag": "",
"pal": "", "pal": "",
@@ -1226,6 +1247,8 @@
"rate": "" "rate": ""
}, },
"ca_gst_all_if_null": "", "ca_gst_all_if_null": "",
"calc_repair_days": "",
"calc_repair_days_tt": "",
"cards": { "cards": {
"customer": "Información al cliente", "customer": "Información al cliente",
"damage": "Área de Daño", "damage": "Área de Daño",
@@ -1314,6 +1337,7 @@
"partstotal": "", "partstotal": "",
"totalreturns": "" "totalreturns": ""
}, },
"prt_dsmk_total": "",
"rates": "Tarifas", "rates": "Tarifas",
"rates_subtotal": "", "rates_subtotal": "",
"reconciliation": { "reconciliation": {
@@ -1398,6 +1422,7 @@
"entertimeticket": "", "entertimeticket": "",
"export": "", "export": "",
"export-logs": "", "export-logs": "",
"help": "",
"home": "Casa", "home": "Casa",
"jobs": "Trabajos", "jobs": "Trabajos",
"owners": "propietarios", "owners": "propietarios",
@@ -1580,6 +1605,7 @@
"oem_partno": "", "oem_partno": "",
"order_date": "", "order_date": "",
"order_number": "", "order_number": "",
"orderedby": "",
"quantity": "", "quantity": "",
"return": "", "return": "",
"status": "" "status": ""
@@ -1662,6 +1688,8 @@
"state": "" "state": ""
}, },
"labels": { "labels": {
"noneselected": "",
"onenamerequired": "",
"vendorcategory": "" "vendorcategory": ""
}, },
"successes": { "successes": {
@@ -1730,7 +1758,10 @@
"payment_receipt": "", "payment_receipt": "",
"payment_request": "", "payment_request": "",
"payments_by_job": "", "payments_by_job": "",
"purchases_by_ro_detail": "",
"purchases_by_ro_summary": "",
"qc_sheet": "", "qc_sheet": "",
"ro_totals": "",
"ro_with_description": "", "ro_with_description": "",
"supplement_request": "", "supplement_request": "",
"thank_you_ro": "", "thank_you_ro": "",
@@ -1813,6 +1844,9 @@
}, },
"labels": { "labels": {
"activeshop": "" "activeshop": ""
},
"successes": {
"updated": ""
} }
}, },
"reportcenter": { "reportcenter": {
@@ -1828,6 +1862,7 @@
"objects": { "objects": {
"appointments": "", "appointments": "",
"bills": "", "bills": "",
"exportlogs": "",
"jobs": "", "jobs": "",
"payments": "", "payments": "",
"timetickets": "" "timetickets": ""
@@ -1835,26 +1870,40 @@
"vendor": "" "vendor": ""
}, },
"templates": { "templates": {
"attendance_detail": "",
"attendance_employee": "", "attendance_employee": "",
"attendance_summary": "",
"credits_not_received_date": "", "credits_not_received_date": "",
"estimator_detail": "", "estimator_detail": "",
"estimator_summary": "", "estimator_summary": "",
"export_payables": "",
"export_payments": "",
"export_receivables": "",
"gsr_by_delivery_date": "",
"gsr_by_estimator": "", "gsr_by_estimator": "",
"gsr_by_exported_date": "",
"gsr_by_ins_co": "",
"gsr_by_make": "", "gsr_by_make": "",
"gsr_by_referral": "",
"gsr_by_ro": "",
"gsr_labor_only": "", "gsr_labor_only": "",
"hours_sold_detail_closed": "", "hours_sold_detail_closed": "",
"hours_sold_detail_closed_source": "", "hours_sold_detail_closed_ins_co": "",
"hours_sold_detail_open": "", "hours_sold_detail_open": "",
"hours_sold_detail_open_source": "", "hours_sold_detail_open_ins_co": "",
"hours_sold_summary_closed": "", "hours_sold_summary_closed": "",
"hours_sold_summary_closed_source": "", "hours_sold_summary_closed_ins_co": "",
"hours_sold_summary_open": "", "hours_sold_summary_open": "",
"hours_sold_summary_open_source": "", "hours_sold_summary_open_ins_co": "",
"job_costing_ro_date_detail": "", "job_costing_ro_date_detail": "",
"job_costing_ro_date_summary": "", "job_costing_ro_date_summary": "",
"job_costing_ro_estimator": "", "job_costing_ro_estimator": "",
"job_costing_ro_source": "", "job_costing_ro_ins_co": "",
"open_orders": "", "open_orders": "",
"open_orders_estimator": "",
"open_orders_ins_co": "",
"parts_backorder": "",
"payments_by_date": "",
"payments_by_date_type": "", "payments_by_date_type": "",
"purchases_by_cost_center_detail": "", "purchases_by_cost_center_detail": "",
"purchases_by_cost_center_summary": "", "purchases_by_cost_center_summary": "",
@@ -1865,11 +1914,15 @@
"purchases_grouped_by_vendor_detailed": "", "purchases_grouped_by_vendor_detailed": "",
"purchases_grouped_by_vendor_summary": "", "purchases_grouped_by_vendor_summary": "",
"schedule": "", "schedule": "",
"supplement_ratio_source": "", "supplement_ratio_ins_co": "",
"thank_you_date": "",
"timetickets": "", "timetickets": "",
"timetickets_employee": "", "timetickets_employee": "",
"timetickets_summary": "", "timetickets_summary": "",
"void_ros": "" "unclaimed_hrs": "",
"void_ros": "",
"work_in_progress_labour": "",
"work_in_progress_payables": ""
} }
}, },
"scoreboard": { "scoreboard": {

View File

@@ -167,6 +167,7 @@
"exported": "" "exported": ""
}, },
"validation": { "validation": {
"manualinhouse": "",
"unique_invoice_number": "" "unique_invoice_number": ""
} }
}, },
@@ -218,6 +219,10 @@
"invoice_federal_tax_rate": "", "invoice_federal_tax_rate": "",
"invoice_local_tax_rate": "", "invoice_local_tax_rate": "",
"invoice_state_tax_rate": "", "invoice_state_tax_rate": "",
"jc_hourly_rates": {
"mapa": "",
"mash": ""
},
"lastnumberworkingdays": "", "lastnumberworkingdays": "",
"logo_img_path": "", "logo_img_path": "",
"logo_img_path_height": "", "logo_img_path_height": "",
@@ -683,7 +688,8 @@
"deleting_cloudinary": "", "deleting_cloudinary": "",
"getpresignurl": "Erreur lors de l'obtention de l'URL présignée pour le document. {{message}}", "getpresignurl": "Erreur lors de l'obtention de l'URL présignée pour le document. {{message}}",
"insert": "Incapable de télécharger le fichier. {{message}}", "insert": "Incapable de télécharger le fichier. {{message}}",
"nodocuments": "Il n'y a pas de documents." "nodocuments": "Il n'y a pas de documents.",
"updating": ""
}, },
"labels": { "labels": {
"confirmdelete": "", "confirmdelete": "",
@@ -696,10 +702,12 @@
"upload": "Télécharger", "upload": "Télécharger",
"upload_limitexceeded": "", "upload_limitexceeded": "",
"upload_limitexceeded_title": "", "upload_limitexceeded_title": "",
"uploading": "",
"usage": "" "usage": ""
}, },
"successes": { "successes": {
"delete": "Le document a bien été supprimé.", "delete": "Le document a bien été supprimé.",
"edituploaded": "",
"insert": "Document téléchargé avec succès.", "insert": "Document téléchargé avec succès.",
"updated": "" "updated": ""
} }
@@ -762,6 +770,11 @@
"unique_employee_number": "" "unique_employee_number": ""
} }
}, },
"exportlogs": {
"fields": {
"createdat": ""
}
},
"general": { "general": {
"actions": { "actions": {
"add": "", "add": "",
@@ -785,7 +798,11 @@
"selectall": "", "selectall": "",
"submit": "", "submit": "",
"submitticket": "", "submitticket": "",
"view": "" "view": "",
"viewreleasenotes": ""
},
"errors": {
"notfound": ""
}, },
"itemtypes": { "itemtypes": {
"contract": "", "contract": "",
@@ -820,6 +837,8 @@
"monday": "", "monday": "",
"na": "N / A", "na": "N / A",
"no": "", "no": "",
"nointernet": "",
"nointernet_sub": "",
"none": "", "none": "",
"out": "En dehors", "out": "En dehors",
"password": "", "password": "",
@@ -950,6 +969,7 @@
}, },
"profitcenter_labor": "", "profitcenter_labor": "",
"profitcenter_part": "", "profitcenter_part": "",
"prt_dsmk_p": "",
"status": "Statut", "status": "Statut",
"total": "", "total": "",
"unq_seq": "Seq #" "unq_seq": "Seq #"
@@ -1122,6 +1142,7 @@
"ownr_ea": "Email", "ownr_ea": "Email",
"ownr_ph1": "Téléphone 1", "ownr_ph1": "Téléphone 1",
"paa": "", "paa": "",
"pac": "",
"pae": "", "pae": "",
"pag": "", "pag": "",
"pal": "", "pal": "",
@@ -1226,6 +1247,8 @@
"rate": "" "rate": ""
}, },
"ca_gst_all_if_null": "", "ca_gst_all_if_null": "",
"calc_repair_days": "",
"calc_repair_days_tt": "",
"cards": { "cards": {
"customer": "Informations client", "customer": "Informations client",
"damage": "Zone de dommages", "damage": "Zone de dommages",
@@ -1314,6 +1337,7 @@
"partstotal": "", "partstotal": "",
"totalreturns": "" "totalreturns": ""
}, },
"prt_dsmk_total": "",
"rates": "Les taux", "rates": "Les taux",
"rates_subtotal": "", "rates_subtotal": "",
"reconciliation": { "reconciliation": {
@@ -1398,6 +1422,7 @@
"entertimeticket": "", "entertimeticket": "",
"export": "", "export": "",
"export-logs": "", "export-logs": "",
"help": "",
"home": "Accueil", "home": "Accueil",
"jobs": "Emplois", "jobs": "Emplois",
"owners": "Propriétaires", "owners": "Propriétaires",
@@ -1580,6 +1605,7 @@
"oem_partno": "", "oem_partno": "",
"order_date": "", "order_date": "",
"order_number": "", "order_number": "",
"orderedby": "",
"quantity": "", "quantity": "",
"return": "", "return": "",
"status": "" "status": ""
@@ -1662,6 +1688,8 @@
"state": "" "state": ""
}, },
"labels": { "labels": {
"noneselected": "",
"onenamerequired": "",
"vendorcategory": "" "vendorcategory": ""
}, },
"successes": { "successes": {
@@ -1730,7 +1758,10 @@
"payment_receipt": "", "payment_receipt": "",
"payment_request": "", "payment_request": "",
"payments_by_job": "", "payments_by_job": "",
"purchases_by_ro_detail": "",
"purchases_by_ro_summary": "",
"qc_sheet": "", "qc_sheet": "",
"ro_totals": "",
"ro_with_description": "", "ro_with_description": "",
"supplement_request": "", "supplement_request": "",
"thank_you_ro": "", "thank_you_ro": "",
@@ -1813,6 +1844,9 @@
}, },
"labels": { "labels": {
"activeshop": "" "activeshop": ""
},
"successes": {
"updated": ""
} }
}, },
"reportcenter": { "reportcenter": {
@@ -1828,6 +1862,7 @@
"objects": { "objects": {
"appointments": "", "appointments": "",
"bills": "", "bills": "",
"exportlogs": "",
"jobs": "", "jobs": "",
"payments": "", "payments": "",
"timetickets": "" "timetickets": ""
@@ -1835,26 +1870,40 @@
"vendor": "" "vendor": ""
}, },
"templates": { "templates": {
"attendance_detail": "",
"attendance_employee": "", "attendance_employee": "",
"attendance_summary": "",
"credits_not_received_date": "", "credits_not_received_date": "",
"estimator_detail": "", "estimator_detail": "",
"estimator_summary": "", "estimator_summary": "",
"export_payables": "",
"export_payments": "",
"export_receivables": "",
"gsr_by_delivery_date": "",
"gsr_by_estimator": "", "gsr_by_estimator": "",
"gsr_by_exported_date": "",
"gsr_by_ins_co": "",
"gsr_by_make": "", "gsr_by_make": "",
"gsr_by_referral": "",
"gsr_by_ro": "",
"gsr_labor_only": "", "gsr_labor_only": "",
"hours_sold_detail_closed": "", "hours_sold_detail_closed": "",
"hours_sold_detail_closed_source": "", "hours_sold_detail_closed_ins_co": "",
"hours_sold_detail_open": "", "hours_sold_detail_open": "",
"hours_sold_detail_open_source": "", "hours_sold_detail_open_ins_co": "",
"hours_sold_summary_closed": "", "hours_sold_summary_closed": "",
"hours_sold_summary_closed_source": "", "hours_sold_summary_closed_ins_co": "",
"hours_sold_summary_open": "", "hours_sold_summary_open": "",
"hours_sold_summary_open_source": "", "hours_sold_summary_open_ins_co": "",
"job_costing_ro_date_detail": "", "job_costing_ro_date_detail": "",
"job_costing_ro_date_summary": "", "job_costing_ro_date_summary": "",
"job_costing_ro_estimator": "", "job_costing_ro_estimator": "",
"job_costing_ro_source": "", "job_costing_ro_ins_co": "",
"open_orders": "", "open_orders": "",
"open_orders_estimator": "",
"open_orders_ins_co": "",
"parts_backorder": "",
"payments_by_date": "",
"payments_by_date_type": "", "payments_by_date_type": "",
"purchases_by_cost_center_detail": "", "purchases_by_cost_center_detail": "",
"purchases_by_cost_center_summary": "", "purchases_by_cost_center_summary": "",
@@ -1865,11 +1914,15 @@
"purchases_grouped_by_vendor_detailed": "", "purchases_grouped_by_vendor_detailed": "",
"purchases_grouped_by_vendor_summary": "", "purchases_grouped_by_vendor_summary": "",
"schedule": "", "schedule": "",
"supplement_ratio_source": "", "supplement_ratio_ins_co": "",
"thank_you_date": "",
"timetickets": "", "timetickets": "",
"timetickets_employee": "", "timetickets_employee": "",
"timetickets_summary": "", "timetickets_summary": "",
"void_ros": "" "unclaimed_hrs": "",
"void_ros": "",
"work_in_progress_labour": "",
"work_in_progress_payables": ""
} }
}, },
"scoreboard": { "scoreboard": {

View File

@@ -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_detail: {
title: i18n.t("reportcenter.templates.attendance_detail"),
subject: i18n.t("reportcenter.templates.attendance_detail"),
key: "attendance_detail",
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_co"
), ),
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"

View File

@@ -5696,6 +5696,11 @@ exenv@^1.2.2:
resolved "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz" resolved "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz"
integrity sha1-KueOhdmJQVhnCwPUe+wfA72Ru50= integrity sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=
exifr@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/exifr/-/exifr-7.0.0.tgz#065d54639f2999ac901dd8475db99b4d3f31f11e"
integrity sha512-5JRtopcolAfsX4YGcvTkldXyFDC+VPV40DxhSdRvrDr4OJQHtyccNoYl4aRzYBnVVpNueMmenS0KhzLiyNSfEQ==
exit@^0.1.2: exit@^0.1.2:
version "0.1.2" version "0.1.2"
resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz"
@@ -8297,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"

View File

@@ -1,4 +1,3 @@
Must set the environment variables using: Must set the environment variables using:
firebase functions:config:set auth.graphql_endpoint="https://bodyshop-dev-db.herokuapp.com/v1/graphql" auth.hasura_secret_admin_key="Dev-BodyShopAppBySnaptSoftware!" firebase functions:config:set auth.graphql_endpoint="https://bodyshop-dev-db.herokuapp.com/v1/graphql" auth.hasura_secret_admin_key="Dev-BodyShopApp!"

View File

@@ -1,2 +1,2 @@
endpoint: https://bodyshop-dev-db.herokuapp.com endpoint: https://bodyshop-dev-db.herokuapp.com
admin_secret: Dev-BodyShopAppBySnaptSoftware! admin_secret: Dev-BodyShopApp!

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."documents" DROP COLUMN "takenat";
type: run_sql

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."documents" ADD COLUMN "takenat" timestamptz NULL;
type: run_sql

View File

@@ -0,0 +1,46 @@
- args:
role: user
table:
name: documents
schema: public
type: drop_insert_permission
- args:
permission:
check:
_or:
- job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
- bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- billid
- bodyshopid
- created_at
- extension
- id
- jobid
- key
- name
- size
- type
- updated_at
- uploaded_by
set: {}
role: user
table:
name: documents
schema: public
type: create_insert_permission

View File

@@ -0,0 +1,47 @@
- args:
role: user
table:
name: documents
schema: public
type: drop_insert_permission
- args:
permission:
check:
_or:
- job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
- bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- billid
- bodyshopid
- created_at
- extension
- id
- jobid
- key
- name
- size
- takenat
- type
- updated_at
- uploaded_by
set: {}
role: user
table:
name: documents
schema: public
type: create_insert_permission

View File

@@ -0,0 +1,47 @@
- args:
role: user
table:
name: documents
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: true
columns:
- billid
- bodyshopid
- created_at
- extension
- id
- jobid
- key
- name
- size
- type
- updated_at
- uploaded_by
computed_fields: []
filter:
_or:
- job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
- bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
role: user
table:
name: documents
schema: public
type: create_select_permission

View File

@@ -0,0 +1,48 @@
- args:
role: user
table:
name: documents
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: true
columns:
- billid
- bodyshopid
- created_at
- extension
- id
- jobid
- key
- name
- size
- takenat
- type
- updated_at
- uploaded_by
computed_fields: []
filter:
_or:
- job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
- bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
role: user
table:
name: documents
schema: public
type: create_select_permission

View File

@@ -0,0 +1,6 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."bodyshops" ADD CONSTRAINT "bodyshops_autohouseid_key"
UNIQUE ("autohouseid");
type: run_sql

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."bodyshops" DROP CONSTRAINT "bodyshops_autohouseid_key";
type: run_sql

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."bodyshops" DROP COLUMN "jc_hourly_rates";
type: run_sql

View File

@@ -0,0 +1,6 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."bodyshops" ADD COLUMN "jc_hourly_rates" jsonb NULL
DEFAULT jsonb_build_object();
type: run_sql

View File

@@ -0,0 +1,83 @@
- args:
role: user
table:
name: bodyshops
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- accountingconfig
- address1
- address2
- appt_alt_transport
- appt_colors
- appt_length
- bill_tax_rates
- city
- country
- created_at
- default_adjustment_rate
- deliverchecklist
- email
- enforce_class
- enforce_referral
- federal_tax_id
- id
- imexshopid
- inhousevendorid
- insurance_vendor_id
- intakechecklist
- jobsizelimit
- logo_img_path
- md_categories
- md_ccc_rates
- md_classes
- md_hour_split
- md_ins_cos
- md_labor_rates
- md_messaging_presets
- md_notes_presets
- md_order_statuses
- md_parts_locations
- md_payment_types
- md_rbac
- md_referral_sources
- md_responsibility_centers
- md_ro_statuses
- messagingservicesid
- phone
- prodtargethrs
- production_config
- region_config
- schedule_end_time
- schedule_start_time
- scoreboard_target
- shopname
- shoprates
- speedprint
- ssbuckets
- state
- state_tax_id
- stripe_acct_id
- sub_status
- target_touchtime
- template_header
- textid
- updated_at
- use_fippa
- website
- workingdays
- zip_post
computed_fields: []
filter:
associations:
user:
authid:
_eq: X-Hasura-User-Id
role: user
table:
name: bodyshops
schema: public
type: create_select_permission

View File

@@ -0,0 +1,84 @@
- args:
role: user
table:
name: bodyshops
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- accountingconfig
- address1
- address2
- appt_alt_transport
- appt_colors
- appt_length
- bill_tax_rates
- city
- country
- created_at
- default_adjustment_rate
- deliverchecklist
- email
- enforce_class
- enforce_referral
- federal_tax_id
- id
- imexshopid
- inhousevendorid
- insurance_vendor_id
- intakechecklist
- jc_hourly_rates
- jobsizelimit
- logo_img_path
- md_categories
- md_ccc_rates
- md_classes
- md_hour_split
- md_ins_cos
- md_labor_rates
- md_messaging_presets
- md_notes_presets
- md_order_statuses
- md_parts_locations
- md_payment_types
- md_rbac
- md_referral_sources
- md_responsibility_centers
- md_ro_statuses
- messagingservicesid
- phone
- prodtargethrs
- production_config
- region_config
- schedule_end_time
- schedule_start_time
- scoreboard_target
- shopname
- shoprates
- speedprint
- ssbuckets
- state
- state_tax_id
- stripe_acct_id
- sub_status
- target_touchtime
- template_header
- textid
- updated_at
- use_fippa
- website
- workingdays
- zip_post
computed_fields: []
filter:
associations:
user:
authid:
_eq: X-Hasura-User-Id
role: user
table:
name: bodyshops
schema: public
type: create_select_permission

View File

@@ -0,0 +1,77 @@
- args:
role: user
table:
name: bodyshops
schema: public
type: drop_update_permission
- args:
permission:
columns:
- accountingconfig
- address1
- address2
- appt_alt_transport
- appt_colors
- appt_length
- bill_tax_rates
- city
- country
- created_at
- default_adjustment_rate
- deliverchecklist
- email
- enforce_class
- enforce_referral
- federal_tax_id
- id
- inhousevendorid
- insurance_vendor_id
- intakechecklist
- logo_img_path
- md_categories
- md_ccc_rates
- md_classes
- md_hour_split
- md_ins_cos
- md_labor_rates
- md_messaging_presets
- md_notes_presets
- md_order_statuses
- md_parts_locations
- md_payment_types
- md_rbac
- md_referral_sources
- md_responsibility_centers
- md_ro_statuses
- phone
- prodtargethrs
- production_config
- schedule_end_time
- schedule_start_time
- scoreboard_target
- shopname
- shoprates
- speedprint
- ssbuckets
- state
- state_tax_id
- target_touchtime
- updated_at
- use_fippa
- website
- workingdays
- zip_post
filter:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
set: {}
role: user
table:
name: bodyshops
schema: public
type: create_update_permission

View File

@@ -0,0 +1,78 @@
- args:
role: user
table:
name: bodyshops
schema: public
type: drop_update_permission
- args:
permission:
columns:
- accountingconfig
- address1
- address2
- appt_alt_transport
- appt_colors
- appt_length
- bill_tax_rates
- city
- country
- created_at
- default_adjustment_rate
- deliverchecklist
- email
- enforce_class
- enforce_referral
- federal_tax_id
- id
- inhousevendorid
- insurance_vendor_id
- intakechecklist
- jc_hourly_rates
- logo_img_path
- md_categories
- md_ccc_rates
- md_classes
- md_hour_split
- md_ins_cos
- md_labor_rates
- md_messaging_presets
- md_notes_presets
- md_order_statuses
- md_parts_locations
- md_payment_types
- md_rbac
- md_referral_sources
- md_responsibility_centers
- md_ro_statuses
- phone
- prodtargethrs
- production_config
- schedule_end_time
- schedule_start_time
- scoreboard_target
- shopname
- shoprates
- speedprint
- ssbuckets
- state
- state_tax_id
- target_touchtime
- updated_at
- use_fippa
- website
- workingdays
- zip_post
filter:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
set: {}
role: user
table:
name: bodyshops
schema: public
type: create_update_permission

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE ONLY "public"."joblines" ALTER COLUMN "prt_dsmk_p" DROP DEFAULT;
type: run_sql

View File

@@ -0,0 +1,6 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE ONLY "public"."joblines" ALTER COLUMN "prt_dsmk_p" SET DEFAULT
0;
type: run_sql

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: "update joblines \nset prt_dsmk_p = 0 where joblines.prt_dsmk_p is null;"
type: run_sql

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."conversations" DROP COLUMN "archived";
type: run_sql

View File

@@ -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

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."parts_orders" DROP COLUMN "orderedby";
type: run_sql

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."parts_orders" ADD COLUMN "orderedby" text NULL;
type: run_sql

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

Some files were not shown because too many files have changed in this diff Show More