Compare commits

..

2 Commits

Author SHA1 Message Date
Dave Richer
ee8cbe33c4 Pagination Fix 2023-12-04 14:28:44 -05:00
Dave Richer
92c8b54f85 Merged in release/2023-12-01 (pull request #1103)
Reversion

Approved-by: Allan Carr
2023-12-04 16:50:13 +00:00
160 changed files with 2443 additions and 16366 deletions

View File

@@ -68,120 +68,6 @@ jobs:
from: build from: build
to: "s3://imex-online-production/" to: "s3://imex-online-production/"
- jira/notify - jira/notify
rome-api-deploy:
docker:
- image: "cimg/base:stable"
steps:
- checkout
- eb/setup
- run:
command: |
eb init romeonline-productionapi -r us-east-2 -p "Node.js 18 running on 64bit Amazon Linux 2"
eb status --verbose
eb deploy
eb status
- jira/notify
rome-hasura-migrate:
docker:
- image: cimg/node:16.15.0
parameters:
secret:
type: string
default: $HASURA_PROD_SECRET
working_directory: ~/repo/hasura
steps:
- checkout:
path: ~/repo
- run:
name: Execute migration
command: |
npm install hasura-cli -g
hasura migrate apply --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
hasura metadata apply --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
hasura metadata reload --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
rome-app-build:
docker:
- image: cimg/node:16.15.0
working_directory: ~/repo/client
steps:
- checkout:
path: ~/repo
- restore_cache:
name: Restore Yarn Package Cache
keys:
- yarn-packages-{{ checksum "yarn.lock" }}
- run:
name: Install Dependencies
command: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn
- save_cache:
name: Save Yarn Package Cache
key: yarn-packages-{{ checksum "yarn.lock" }}
paths:
- ~/.cache/yarn
- run: yarn run build
- aws-s3/sync:
from: build
to: "s3://rome-online-production/"
- jira/notify
test-rome-hasura-migrate:
docker:
- image: cimg/node:16.15.0
parameters:
secret:
type: string
default: $HASURA_ROME_TEST_SECRET
working_directory: ~/repo/hasura
steps:
- checkout:
path: ~/repo
- run:
name: Execute migration
command: |
npm install hasura-cli -g
echo ${HASURA_TEST_SECRET}
hasura migrate apply --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
hasura metadata apply --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
hasura metadata reload --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
test-rome-app-build:
docker:
- image: cimg/node:16.15.0
working_directory: ~/repo/client
steps:
- checkout:
path: ~/repo
- restore_cache:
name: Restore Yarn Package Cache
keys:
- yarn-packages-{{ checksum "yarn.lock" }}
- run:
name: Install Dependencies
command: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn
- save_cache:
name: Save Yarn Package Cache
key: yarn-packages-{{ checksum "yarn.lock" }}
paths:
- ~/.cache/yarn
- run: yarn run build:test
- aws-s3/sync:
from: build
to: "s3://rome-online-test/"
- jira/notify
test-hasura-migrate: test-hasura-migrate:
docker: docker:
@@ -279,19 +165,6 @@ workflows:
filters: filters:
branches: branches:
only: master only: master
- rome-api-deploy:
filters:
branches:
only: rome/master
- rome-app-build:
filters:
branches:
only: rome/master
- rome-hasura-migrate:
secret: ${HASURA_PROD_SECRET}
filters:
branches:
only: rome/master
- test-app-build: - test-app-build:
filters: filters:
branches: branches:
@@ -301,15 +174,6 @@ workflows:
filters: filters:
branches: branches:
only: test only: test
- test-rome-app-build:
filters:
branches:
only: rome/test
- test-rome-hasura-migrate:
secret: ${HASURA_ROME_TEST_SECRET}
filters:
branches:
only: rome/test
#- admin-app-build: #- admin-app-build:
#filters: #filters:
#branches: #branches:

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,13 @@
REACT_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql REACT_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
REACT_APP_GA_CODE=231099835 REACT_APP_GA_CODE=231099835
REACT_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"} REACT_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"}
REACT_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/io-test REACT_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/io-test
REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/io-test REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/io-test
REACT_APP_CLOUDINARY_API_KEY=957865933348715 REACT_APP_CLOUDINARY_API_KEY=957865933348715
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250 REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo' REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BG3tzU7L2BXlGZ_3VLK4PNaRceoEXEnmHfxcVbRMF5o5g05ejslhVPki9kBM9cBBT-08Ad9kN3HSpS6JmrWD6h4'
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
REACT_APP_AXIOS_BASE_API_URL=http://localhost:4000 REACT_APP_AXIOS_BASE_API_URL=http://localhost:4000
REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
REACT_APP_COUNTRY=USA

View File

@@ -1,13 +1,14 @@
REACT_APP_GRAPHQL_ENDPOINT=https://db.romeonline.io/v1/graphql GENERATE_SOURCEMAP=false
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.romeonline.io/v1/graphql REACT_APP_GRAPHQL_ENDPOINT=https://db.imex.online/v1/graphql
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.imex.online/v1/graphql
REACT_APP_GA_CODE=231103507 REACT_APP_GA_CODE=231103507
REACT_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"} REACT_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDSezy-jGJreo7ulgpLdlpOwAOrgcaEkhU","authDomain":"imex-prod.firebaseapp.com","databaseURL":"https://imex-prod.firebaseio.com","projectId":"imex-prod","storageBucket":"imex-prod.appspot.com","messagingSenderId":"253497221485","appId":"1:253497221485:web:3c81c483b94db84b227a64","measurementId":"G-NTWBKG2L0M"}
REACT_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/bodyshop REACT_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/bodyshop
REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/bodyshop REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/bodyshop
REACT_APP_CLOUDINARY_API_KEY=473322739956866 REACT_APP_CLOUDINARY_API_KEY=473322739956866
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250 REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo' REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BMgZT1NZztW2DsJl8Mg2L04hgY9FzAg6b8fbzgNAfww2VDzH3VE63Ot9EaP_U7KWS2JT-7HPHaw0T_Tw_5vkZc8'
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
REACT_APP_AXIOS_BASE_API_URL=https://api.romeonline.io/ REACT_APP_AXIOS_BASE_API_URL=https://api.imex.online/
REACT_APP_REPORTS_SERVER_URL=https://reports.romeonline.io REACT_APP_REPORTS_SERVER_URL=https://reports.imex.online
REACT_APP_SPLIT_API=et9pjkik6bn67he5evpmpr1agoo7gactphgk REACT_APP_SPLIT_API=et9pjkik6bn67he5evpmpr1agoo7gactphgk

View File

@@ -1,14 +1,14 @@
REACT_APP_GRAPHQL_ENDPOINT=https://db.test.romeonline.io/v1/graphql REACT_APP_GRAPHQL_ENDPOINT=https://db.test.bodyshop.app/v1/graphql
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.test.romeonline.io/v1/graphql REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.test.bodyshop.app/v1/graphql
REACT_APP_GA_CODE=231103507 REACT_APP_GA_CODE=231099835
REACT_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"} REACT_APP_FIREBASE_CONFIG={ "apiKey":"AIzaSyBw7_GTy7GtQyfkIRPVrWHEGKfcqeyXw0c", "authDomain":"imex-test.firebaseapp.com", "projectId":"imex-test", "storageBucket":"imex-test.appspot.com", "messagingSenderId":"991923618608", "appId":"1:991923618608:web:633437569cdad78299bef5", "measurementId":"G-TW0XLZEH18"}
REACT_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/bodyshop REACT_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/bodyshop
REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/bodyshop REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/bodyshop
REACT_APP_CLOUDINARY_API_KEY=473322739956866 REACT_APP_CLOUDINARY_API_KEY=473322739956866
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250 REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo' REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BN2GcDPjipR5MTEosO5dT4CfQ3cmrdBIsI4juoOQrRijn_5aRiHlwj1mlq0W145mOusx6xynEKl_tvYJhpCc9lo'
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
REACT_APP_AXIOS_BASE_API_URL=https://api.test.romeonline.io/ REACT_APP_AXIOS_BASE_API_URL=https://api.test.imex.online/
REACT_APP_REPORTS_SERVER_URL=https://reports.test.romeonline.io REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
REACT_APP_IS_TEST=true REACT_APP_IS_TEST=true
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc

View File

@@ -12,7 +12,7 @@ module.exports = {
authToken: authToken:
"6b45b028a02342db97a9a2f92c0959058665443d379d4a3a876430009e744260", "6b45b028a02342db97a9a2f92c0959058665443d379d4a3a876430009e744260",
org: "snapt-software", org: "snapt-software",
project: "rome-online", project: "imexonline",
release: process.env.REACT_APP_GIT_SHA, release: process.env.REACT_APP_GIT_SHA,
// webpack-specific configuration // webpack-specific configuration
@@ -27,7 +27,7 @@ module.exports = {
lessOptions: { lessOptions: {
modifyVars: { modifyVars: {
...(process.env.NODE_ENV === "development" ...(process.env.NODE_ENV === "development"
? { "@primary-color": "#B22234" } ? { "@primary-color": "#a51d1d" }
: { : {
//"@primary-color": "#1DA57A" //"@primary-color": "#1DA57A"
}), }),

View File

@@ -28,17 +28,6 @@ switch (this.location.hostname) {
// measurementId: "${config.measurementId}", // measurementId: "${config.measurementId}",
}; };
break; break;
case "romeonline.io":
firebaseConfig = {
apiKey: "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE",
authDomain: "rome-prod-1.firebaseapp.com",
projectId: "rome-prod-1",
storageBucket: "rome-prod-1.appspot.com",
messagingSenderId: "147786367145",
appId: "1:147786367145:web:9d4cba68071c3f29a8a9b8",
measurementId: "G-G8Z9DRHTZS",
};
break;
case "imex.online": case "imex.online":
default: default:
firebaseConfig = { firebaseConfig = {

View File

@@ -2,81 +2,23 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/ro-favicon.png" /> <link rel="icon" href="%PUBLIC_URL%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#002366" /> <meta name="theme-color" content="#002366" />
<meta name="description" content="Rome Online" /> <meta name="description" content="ImEX Online" />
<!-- <link rel="apple-touch-icon" href="logo192.png" /> --> <!-- <link rel="apple-touch-icon" href="logo192.png" /> -->
<!--Use the below code snippet to provide real time updates to the live chat plugin without the need of copying and paste each time to your website when changes are made via PBX-->
<call-us-selector phonesystem-url=https://rometech.east.3cx.us:5001 party="LiveChat528346"></call-us-selector>
<!--Incase you don't want real time updates to the live chat plugin when options are changed, use the below code snippet. Please note that each time you change the settings you will need to copy and paste the snippet code to your website-->
<!--<call-us
phonesystem-url=https://rometech.east.3cx.us:5001
style="position:fixed;font-size:16px;line-height:17px;z-index: 99999;right: 20px; bottom: 20px;"
id="wp-live-chat-by-3CX"
minimized="true"
animation-style="noanimation"
party="LiveChat528346"
minimized-style="bubbleright"
allow-call="true"
allow-video="false"
allow-soundnotifications="true"
enable-mute="true"
enable-onmobile="true"
offline-enabled="true"
enable="true"
ignore-queueownership="false"
authentication="both"
show-operator-actual-name="true"
aknowledge-received="true"
gdpr-enabled="false"
message-userinfo-format="name"
message-dateformat="both"
lang="browser"
button-icon-type="default"
greeting-visibility="none"
greeting-offline-visibility="none"
chat-delay="2000"
enable-direct-call="true"
enable-ga="false"
></call-us>-->
<script defer src=https://downloads-global.3cx.com/downloads/livechatandtalk/v1/callus.js id="tcx-callus-js" charset="utf-8"></script>
<link rel="apple-touch-icon" href="logo192.png" /> <link rel="apple-touch-icon" href="logo192.png" />
<script type="text/javascript">
window.$crisp = [];
window.CRISP_WEBSITE_ID = "36724f62-2eb0-4b29-9cdd-9905fb99913e";
(function () {
d = document;
s = d.createElement("script");
s.src = "https://client.crisp.chat/l.js";
s.async = 1;
d.getElementsByTagName("head")[0].appendChild(s);
})();
</script>
<script> <script>
!(function () { !(function () {
"use strict"; "use strict";
@@ -135,7 +77,7 @@ enable-ga="false"
work correctly both with client-side routing and a non-root public URL. work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`. Learn how to configure a non-root public URL by running `npm run build`.
--> -->
<title>Rome Online</title> <title>ImEX Online</title>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>

View File

@@ -1,6 +1,6 @@
{ {
"short_name": "Rome Online", "short_name": "ImEX Online",
"name": "Rome Online", "name": "ImEX Online",
"description": "The ultimate bodyshop management system.", "description": "The ultimate bodyshop management system.",
"icons": [ "icons": [
{ {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 B

View File

@@ -27,12 +27,6 @@ export default function AppContainer() {
//componentSize="small" //componentSize="small"
input={{ autoComplete: "new-password" }} input={{ autoComplete: "new-password" }}
locale={enLocale} locale={enLocale}
theme={{
token: {
colorPrimary: "#326ade",
colorInfo: "#326ade"
},
}}
form={{ form={{
validateMessages: { validateMessages: {
// eslint-disable-next-line no-template-curly-in-string // eslint-disable-next-line no-template-curly-in-string

View File

@@ -77,9 +77,9 @@ export function App({
if (currentUser.authorized && bodyshop) { if (currentUser.authorized && bodyshop) {
client.setAttribute("imexshopid", bodyshop.imexshopid); client.setAttribute("imexshopid", bodyshop.imexshopid);
LogRocket.init("rome-online/rome-online");
if (client.getTreatment("LogRocket_Tracking") === "on") { if (client.getTreatment("LogRocket_Tracking") === "on") {
LogRocket.init("rome-online/rome-online"); console.log("LR Start");
LogRocket.init("gvfvfw/bodyshopapp");
} }
} }
}, [bodyshop, client, currentUser.authorized]); }, [bodyshop, client, currentUser.authorized]);
@@ -109,7 +109,7 @@ export function App({
return ( return (
<Switch> <Switch>
<Suspense fallback={<LoadingSpinner />}> <Suspense fallback={<LoadingSpinner message="ImEX Online" />}>
<ErrorBoundary> <ErrorBoundary>
<Route exact path="/" component={LandingPage} /> <Route exact path="/" component={LandingPage} />
</ErrorBoundary> </ErrorBoundary>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

View File

@@ -1,5 +1,4 @@
import { useApolloClient, useMutation } from "@apollo/client"; import { useApolloClient, useMutation } from "@apollo/client";
import { useTreatments } from "@splitsoftware/splitio-react";
import { Button, Checkbox, Form, Modal, Space, notification } from "antd"; import { Button, Checkbox, Form, Modal, Space, notification } from "antd";
import _ from "lodash"; import _ from "lodash";
import React, { useEffect, useMemo, useState } from "react"; import React, { useEffect, useMemo, useState } from "react";
@@ -64,19 +63,9 @@ function BillEnterModalContainer({
"enter_bill_generate_label", "enter_bill_generate_label",
false false
); );
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
const formValues = useMemo(() => { const formValues = useMemo(() => {
return { return {
...billEnterModal.context.bill, ...billEnterModal.context.bill,
//Added as a part of IO-2436 for capturing parts price changes.
billlines: billEnterModal.context?.bill?.billlines?.map((line) => ({
...line,
original_actual_price: line.actual_price,
})),
jobid: jobid:
(billEnterModal.context.job && billEnterModal.context.job.id) || null, (billEnterModal.context.job && billEnterModal.context.job.id) || null,
federal_tax_rate: federal_tax_rate:
@@ -109,7 +98,6 @@ function BillEnterModalContainer({
} = values; } = values;
let adjustmentsToInsert = {}; let adjustmentsToInsert = {};
let payrollAdjustmentsToInsert = [];
const r1 = await insertBill({ const r1 = await insertBill({
variables: { variables: {
@@ -125,33 +113,14 @@ function BillEnterModalContainer({
lbr_adjustment, lbr_adjustment,
location: lineLocation, location: lineLocation,
part_type, part_type,
create_ppc,
original_actual_price,
...restI ...restI
} = i; } = i;
if (Enhanced_Payroll.treatment === "on") { if (deductedfromlbr) {
if ( adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] =
deductedfromlbr && (adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] || 0) -
true //payroll is on restI.actual_price / lbr_adjustment.rate;
) {
payrollAdjustmentsToInsert.push({
id: i.joblineid,
convertedtolbr: true,
convertedtolbr_data: {
mod_lb_hrs: lbr_adjustment.mod_lb_hrs * -1,
mod_lbr_ty: lbr_adjustment.mod_lbr_ty,
},
});
}
} else {
if (deductedfromlbr) {
adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] =
(adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] || 0) -
restI.actual_price / lbr_adjustment.rate;
}
} }
return { return {
...restI, ...restI,
deductedfromlbr: deductedfromlbr, deductedfromlbr: deductedfromlbr,
@@ -177,20 +146,6 @@ function BillEnterModalContainer({
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID", "GET_JOB_BY_PK"], refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID", "GET_JOB_BY_PK"],
}); });
await Promise.all(
payrollAdjustmentsToInsert.map((li) => {
return updateJobLines({
variables: {
lineId: li.id,
line: {
convertedtolbr: li.convertedtolbr,
convertedtolbr_data: li.convertedtolbr_data,
},
},
});
})
);
const adjKeys = Object.keys(adjustmentsToInsert); const adjKeys = Object.keys(adjustmentsToInsert);
if (adjKeys.length > 0) { if (adjKeys.length > 0) {
//Query the adjustments, merge, and update them. //Query the adjustments, merge, and update them.
@@ -299,14 +254,6 @@ function BillEnterModalContainer({
location: li.location || location, location: li.location || location,
status: status:
bodyshop.md_order_statuses.default_received || "Received*", bodyshop.md_order_statuses.default_received || "Received*",
//Added parts price changes.
...(li.create_ppc &&
li.original_actual_price !== li.actual_price
? {
act_price_before_ppc: li.original_actual_price,
act_price: li.actual_price,
}
: {}),
}, },
}, },
}); });
@@ -375,12 +322,12 @@ function BillEnterModalContainer({
}); });
if (enterAgain) { if (enterAgain) {
// form.resetFields(); form.resetFields();
form.resetFields();
form.setFieldsValue({ form.setFieldsValue({
...formValues, ...formValues,
billlines: [], billlines: [],
}); });
form.resetFields();
} else { } else {
toggleModalVisible(); toggleModalVisible();
} }
@@ -455,9 +402,6 @@ function BillEnterModalContainer({
setEnterAgain(false); setEnterAgain(false);
}} }}
> >
<button onClick={() => console.log(form.getFieldsValue("billlines"))}>
get billlines
</button>
<BillFormContainer <BillFormContainer
form={form} form={form}
disableInvNumber={billEnterModal.context.disableInvNumber} disableInvNumber={billEnterModal.context.disableInvNumber}

View File

@@ -366,15 +366,13 @@ export function BillFormComponent({
)} )}
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow> <LayoutFormRow>
{ <Form.Item
// <Form.Item span={3}
// span={3} label={t("bills.fields.federal_tax_rate")}
// label={t("bills.fields.federal_tax_rate")} name="federal_tax_rate"
// name="federal_tax_rate" >
// > <CurrencyInput min={0} disabled={disabled} />
// <CurrencyInput min={0} disabled={disabled} /> </Form.Item>
// </Form.Item>
}
<Form.Item <Form.Item
span={3} span={3}
label={t("bills.fields.state_tax_rate")} label={t("bills.fields.state_tax_rate")}
@@ -382,15 +380,13 @@ export function BillFormComponent({
> >
<CurrencyInput min={0} disabled={disabled} /> <CurrencyInput min={0} disabled={disabled} />
</Form.Item> </Form.Item>
{ <Form.Item
// <Form.Item span={3}
// span={3} label={t("bills.fields.local_tax_rate")}
// label={t("bills.fields.local_tax_rate")} name="local_tax_rate"
// name="local_tax_rate" >
// > <CurrencyInput min={0} />
// <CurrencyInput min={0} /> </Form.Item>
// </Form.Item>
}
<Form.Item shouldUpdate span={15}> <Form.Item shouldUpdate span={15}>
{() => { {() => {
const values = form.getFieldsValue([ const values = form.getFieldsValue([
@@ -416,25 +412,21 @@ export function BillFormComponent({
value={totals.subtotal.toFormat()} value={totals.subtotal.toFormat()}
precision={2} precision={2}
/> />
{ <Statistic
// <Statistic title={t("bills.labels.federal_tax")}
// title={t("bills.labels.federal_tax")} value={totals.federalTax.toFormat()}
// value={totals.federalTax.toFormat()} precision={2}
// precision={2} />
// />
}
<Statistic <Statistic
title={t("bills.labels.state_tax")} title={t("bills.labels.state_tax")}
value={totals.stateTax.toFormat()} value={totals.stateTax.toFormat()}
precision={2} precision={2}
/> />
{ <Statistic
// <Statistic title={t("bills.labels.local_tax")}
// title={t("bills.labels.local_tax")} value={totals.localTax.toFormat()}
// value={totals.localTax.toFormat()} precision={2}
// precision={2} />
// />
}
<Statistic <Statistic
title={t("bills.labels.entered_total")} title={t("bills.labels.entered_total")}
value={totals.enteredTotal.toFormat()} value={totals.enteredTotal.toFormat()}

View File

@@ -1,16 +1,14 @@
import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons"; import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
import { useTreatments } from "@splitsoftware/splitio-react"; import { useTreatments } from "@splitsoftware/splitio-react";
import { import {
Button, Button, Form,
Checkbox,
Form,
Input, Input,
InputNumber, InputNumber,
Select, Select,
Space, Space,
Switch, Switch,
Table, Table,
Tooltip, Tooltip
} from "antd"; } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -47,13 +45,6 @@ export function BillEnterModalLinesComponent({
{}, {},
bodyshop && bodyshop.imexshopid bodyshop && bodyshop.imexshopid
); );
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
const columns = (remove) => { const columns = (remove) => {
return [ return [
{ {
@@ -102,7 +93,6 @@ export function BillEnterModalLinesComponent({
line_desc: opt.line_desc, line_desc: opt.line_desc,
quantity: opt.part_qty || 1, quantity: opt.part_qty || 1,
actual_price: opt.cost, actual_price: opt.cost,
original_actual_price: opt.cost,
cost_center: opt.part_type cost_center: opt.part_type
? bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid
? opt.part_type !== "PAE" ? opt.part_type !== "PAE"
@@ -229,43 +219,6 @@ export function BillEnterModalLinesComponent({
}} }}
/> />
), ),
additional: (record, index) => (
<Form.Item
dependencies={["billlines", record.name, "actual_price"]}
noStyle
>
{() => {
const billLine = getFieldValue(["billlines", record.name]);
const jobLine = lineData.find(
(line) => line.id === billLine?.joblineid
);
if (
!billEdit &&
billLine &&
jobLine &&
billLine?.actual_price !== jobLine?.act_price
) {
return (
<Space size="small">
<Form.Item
noStyle
label={t("joblines.fields.create_ppc")}
key={`${index}ppc`}
valuePropName="checked"
name={[record.name, "create_ppc"]}
>
<Checkbox />
</Form.Item>
{t("joblines.fields.create_ppc")}
</Space>
);
} else {
return null;
}
}}
</Form.Item>
),
}, },
{ {
title: t("billlines.fields.actual_cost"), title: t("billlines.fields.actual_cost"),
@@ -407,7 +360,7 @@ export function BillEnterModalLinesComponent({
}, },
formInput: (record, index) => <Switch disabled={disabled} />, formInput: (record, index) => <Switch disabled={disabled} />,
additional: (record, index) => ( additional: (record, index) => (
<Form.Item shouldUpdate noStyle style={{ display: "inline-block" }}> <Form.Item shouldUpdate style={{ display: "inline-block" }}>
{() => { {() => {
const price = getFieldValue([ const price = getFieldValue([
"billlines", "billlines",
@@ -422,31 +375,12 @@ export function BillEnterModalLinesComponent({
"rate", "rate",
]); ]);
const billline = getFieldValue(["billlines", record.name]);
const jobline = lineData.find(
(line) => line.id === billline?.joblineid
);
const employeeTeamName = bodyshop.employee_teams.find(
(team) => team.id === jobline?.assigned_team
);
if (getFieldValue(["billlines", record.name, "deductedfromlbr"])) if (getFieldValue(["billlines", record.name, "deductedfromlbr"]))
return ( return (
<div> <div>
<Space>
{t("joblines.fields.assigned_team", {
name: employeeTeamName?.name,
})}
{`${jobline.mod_lb_hrs} units/${t(
`joblines.fields.lbr_types.${jobline.mod_lbr_ty}`
)}`}
</Space>
<Form.Item <Form.Item
label={t("joblines.fields.mod_lbr_ty")} label={t("joblines.fields.mod_lbr_ty")}
key={`${index}modlbrty`} key={`${index}modlbrty`}
initialValue={jobline ? jobline.mod_lbr_ty : null}
rules={[ rules={[
{ {
required: true, required: true,
@@ -500,44 +434,22 @@ export function BillEnterModalLinesComponent({
</Select.Option> </Select.Option>
</Select> </Select>
</Form.Item> </Form.Item>
{Enhanced_Payroll.treatment === "on" ? ( <Form.Item
<Form.Item label={t("jobs.labels.adjustmentrate")}
label={t("billlines.labels.mod_lbr_adjustment")} name={[record.name, "lbr_adjustment", "rate"]}
name={[record.name, "lbr_adjustment", "mod_lb_hrs"]} initialValue={bodyshop.default_adjustment_rate}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
> >
<InputNumber <InputNumber precision={2} min={0.01} />
precision={5} </Form.Item>
min={0.01} {price &&
max={jobline ? jobline.mod_lb_hrs : 0} adjustmentRate &&
/> `${(price / adjustmentRate).toFixed(1)} hrs`}
</Form.Item>
) : (
<Form.Item
label={t("jobs.labels.adjustmentrate")}
name={[record.name, "lbr_adjustment", "rate"]}
initialValue={bodyshop.default_adjustment_rate}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber precision={2} min={0.01} />
</Form.Item>
)}
<Space>
{price &&
adjustmentRate &&
`${(price / adjustmentRate).toFixed(1)} hrs`}
</Space>
</div> </div>
); );
return <></>; return <></>;
@@ -545,21 +457,21 @@ export function BillEnterModalLinesComponent({
</Form.Item> </Form.Item>
), ),
}, },
// { {
// title: t("billlines.fields.federal_tax_applicable"), title: t("billlines.fields.federal_tax_applicable"),
// dataIndex: "applicable_taxes.federal", dataIndex: "applicable_taxes.federal",
// editable: true, editable: true,
// formItemProps: (field) => { formItemProps: (field) => {
// return { return {
// key: `${field.index}fedtax`, key: `${field.index}fedtax`,
// valuePropName: "checked", valuePropName: "checked",
// // initialValue: true, initialValue: true,
// name: [field.name, "applicable_taxes", "federal"], name: [field.name, "applicable_taxes", "federal"],
// }; };
// }, },
// formInput: (record, index) => <Switch disabled={disabled} />, formInput: (record, index) => <Switch disabled={disabled} />,
// }, },
{ {
title: t("billlines.fields.state_tax_applicable"), title: t("billlines.fields.state_tax_applicable"),
dataIndex: "applicable_taxes.state", dataIndex: "applicable_taxes.state",
@@ -574,20 +486,20 @@ export function BillEnterModalLinesComponent({
}, },
formInput: (record, index) => <Switch disabled={disabled} />, formInput: (record, index) => <Switch disabled={disabled} />,
}, },
// { {
// title: t("billlines.fields.local_tax_applicable"), title: t("billlines.fields.local_tax_applicable"),
// dataIndex: "applicable_taxes.local", dataIndex: "applicable_taxes.local",
// editable: true, editable: true,
// formItemProps: (field) => { formItemProps: (field) => {
// return { return {
// key: `${field.index}localtax`, key: `${field.index}localtax`,
// valuePropName: "checked", valuePropName: "checked",
// name: [field.name, "applicable_taxes", "local"], name: [field.name, "applicable_taxes", "local"],
// }; };
// }, },
// formInput: (record, index) => <Switch disabled={disabled} />, formInput: (record, index) => <Switch disabled={disabled} />,
// }, },
{ {
title: t("general.labels.actions"), title: t("general.labels.actions"),
@@ -712,7 +624,7 @@ const EditableCell = ({
if (additional) if (additional)
return ( return (
<td {...restProps}> <td {...restProps}>
<div size="small"> <Space size="small">
<Form.Item <Form.Item
name={dataIndex} name={dataIndex}
labelCol={{ span: 0 }} labelCol={{ span: 0 }}
@@ -721,7 +633,7 @@ const EditableCell = ({
{(formInput && formInput(record, record.name)) || children} {(formInput && formInput(record, record.name)) || children}
</Form.Item> </Form.Item>
{additional && additional(record, record.name)} {additional && additional(record, record.name)}
</div> </Space>
</td> </td>
); );
if (wrapper) if (wrapper)

View File

@@ -19,14 +19,14 @@ export const CalculateBillTotal = (invoice) => {
}).multiply(i.quantity || 1); }).multiply(i.quantity || 1);
subtotal = subtotal.add(itemTotal); subtotal = subtotal.add(itemTotal);
if (i.applicable_taxes?.federal) { if (i.applicable_taxes.federal) {
federalTax = federalTax.add( federalTax = federalTax.add(
itemTotal.percentage(federal_tax_rate || 0) itemTotal.percentage(federal_tax_rate || 0)
); );
} }
if (i.applicable_taxes?.state) if (i.applicable_taxes.state)
stateTax = stateTax.add(itemTotal.percentage(state_tax_rate || 0)); stateTax = stateTax.add(itemTotal.percentage(state_tax_rate || 0));
if (i.applicable_taxes?.local) if (i.applicable_taxes.local)
localTax = localTax.add(itemTotal.percentage(local_tax_rate || 0)); localTax = localTax.add(itemTotal.percentage(local_tax_rate || 0));
} }
}); });

View File

@@ -63,12 +63,6 @@ const BillLineSearchSelect = (
item.oem_partno ? ` - ${item.oem_partno}` : "" item.oem_partno ? ` - ${item.oem_partno}` : ""
}${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim()} }${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim()}
</span> </span>
{item.act_price === 0 && item.mod_lb_hrs > 0 && (
<span style={{ float: "right", paddingleft: "1rem" }}>
{`${item.mod_lb_hrs} units`}
</span>
)}
<span style={{ float: "right", paddingleft: "1rem" }}> <span style={{ float: "right", paddingleft: "1rem" }}>
{item.act_price {item.act_price
? `$${item.act_price && item.act_price.toFixed(2)}` ? `$${item.act_price && item.act_price.toFixed(2)}`

View File

@@ -1,10 +1,10 @@
import { Select, Space, Tag } from "antd"; import { Select, Space, Tag } from "antd";
import React from "react"; import React, { forwardRef } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
const { Option } = Select; const { Option } = Select;
//To be used as a form element only. //To be used as a form element only.
const EmployeeSearchSelect = ({ options, ...props }) => { const EmployeeSearchSelect = ({ options, ...props }, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
@@ -39,4 +39,4 @@ const EmployeeSearchSelect = ({ options, ...props }) => {
</Select> </Select>
); );
}; };
export default EmployeeSearchSelect; export default forwardRef(EmployeeSearchSelect);

View File

@@ -1,33 +0,0 @@
import { useQuery } from "@apollo/client";
import { Select } from "antd";
import React, { forwardRef } from "react";
import { QUERY_TEAMS } from "../../graphql/employee_teams.queries";
import AlertComponent from "../alert/alert.component";
//To be used as a form element only.
const EmployeeTeamSearchSelect = ({ ...props }, ref) => {
const { loading, error, data } = useQuery(QUERY_TEAMS);
if (error) return <AlertComponent message={JSON.stringify(error)} />;
return (
<Select
showSearch
allowClear
loading={loading}
style={{
width: 400,
}}
options={
data
? data.employee_teams.map((e) => ({
value: JSON.stringify(e),
label: e.name,
}))
: []
}
{...props}
/>
);
};
export default forwardRef(EmployeeTeamSearchSelect);

View File

@@ -40,22 +40,22 @@ class ErrorBoundary extends React.Component {
} }
handleErrorSubmit = () => { handleErrorSubmit = () => {
// window.$crisp.push([ window.$crisp.push([
// "do", "do",
// "message:send", "message:send",
// [ [
// "text", "text",
// `I hit the following error: \n\n `I hit the following error: \n\n
// ${this.state.error.message}\n\n ${this.state.error.message}\n\n
// ${this.state.error.stack}\n\n ${this.state.error.stack}\n\n
// URL:${window.location} as ${this.props.currentUser.email} for ${ URL:${window.location} as ${this.props.currentUser.email} for ${
// this.props.bodyshop && this.props.bodyshop.name this.props.bodyshop && this.props.bodyshop.name
// } }
// `, `,
// ], ],
// ]); ]);
// window.$crisp.push(["do", "chat:open"]); window.$crisp.push(["do", "chat:open"]);
// const errorDescription = `**Please add relevant details about what you were doing before you encountered this issue** // const errorDescription = `**Please add relevant details about what you were doing before you encountered this issue**
// ---- // ----

View File

@@ -1,26 +1,9 @@
import Dinero from "dinero.js"; import Dinero from "dinero.js";
import React, { forwardRef } from "react"; import React, { forwardRef } from "react";
import { connect } from "react-redux"; const ReadOnlyFormItem = ({ value, type = "text", onChange }, ref) => {
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
const ReadOnlyFormItem = (
{ bodyshop, value, type = "text", onChange },
ref
) => {
if (!value) return null; if (!value) return null;
switch (type) { switch (type) {
case "employee":
const emp = bodyshop.employees.find((e) => e.id === value);
return `${emp?.first_name} ${emp?.last_name}`;
case "text": case "text":
return <div>{value}</div>; return <div>{value}</div>;
case "currency": case "currency":
@@ -31,8 +14,4 @@ const ReadOnlyFormItem = (
return <div>{value}</div>; return <div>{value}</div>;
} }
}; };
export default forwardRef(ReadOnlyFormItem);
export default connect(
mapStateToProps,
mapDispatchToProps
)(forwardRef(ReadOnlyFormItem));

View File

@@ -268,13 +268,6 @@ function Header({
{t("menus.header.timetickets")} {t("menus.header.timetickets")}
</Link> </Link>
</Menu.Item> </Menu.Item>
{bodyshop?.md_tasks_presets?.use_approvals && (
<Menu.Item key="ttapprovals" icon={<FieldTimeOutlined />}>
<Link to="/manage/ttapprovals">
{t("menus.header.ttapprovals")}
</Link>
</Menu.Item>
)}
<Menu.Item <Menu.Item
key="entertimetickets" key="entertimetickets"
icon={<Icon component={GiPlayerTime} />} icon={<Icon component={GiPlayerTime} />}
@@ -389,12 +382,20 @@ function Header({
<Menu.Item <Menu.Item
key="help" key="help"
onClick={() => { onClick={() => {
window.open("https://rometech.com/", "_blank"); window.open("https://help.imex.online/", "_blank");
}} }}
icon={<Icon component={QuestionCircleFilled} />} icon={<Icon component={QuestionCircleFilled} />}
> >
{t("menus.header.help")} {t("menus.header.help")}
</Menu.Item> </Menu.Item>
<Menu.Item
key="rescue"
onClick={() => {
window.open("https://imexrescue.com/", "_blank");
}}
>
{t("menus.header.rescueme")}
</Menu.Item>
<Menu.Item key="shiftclock"> <Menu.Item key="shiftclock">
<Link to="/manage/shiftclock">{t("menus.header.shiftclock")}</Link> <Link to="/manage/shiftclock">{t("menus.header.shiftclock")}</Link>
</Menu.Item> </Menu.Item>

View File

@@ -87,8 +87,7 @@ export default function JobBillsTotalComponent({
const totalPartsSublet = Dinero(totals.parts.parts.total) const totalPartsSublet = Dinero(totals.parts.parts.total)
.add(Dinero(totals.parts.sublets.total)) .add(Dinero(totals.parts.sublets.total))
.add(Dinero(totals.additional.shipping)) .add(Dinero(totals.additional.shipping))
.add(Dinero(totals.additional.towing)) .add(Dinero(totals.additional.towing));
.add(Dinero(totals.additional.additionalCosts));
const discrepancy = totalPartsSublet.subtract(billTotals); const discrepancy = totalPartsSublet.subtract(billTotals);

View File

@@ -1,52 +1,47 @@
import { Button, notification } from "antd"; import { Button, notification } from "antd";
import Axios from "axios"; import Axios from "axios";
import React, { useState } from "react"; import React, { useState } from "react";
import { useMutation } from "@apollo/client";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
export default function JobCalculateTotals({ job, disabled, refetch }) { import { UPDATE_JOB } from "../../graphql/jobs.queries";
import Dinero from "dinero.js";
export default function JobCalculateTotals({ job, disabled }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [updateJob] = useMutation(UPDATE_JOB);
const handleCalculate = async () => { const handleCalculate = async () => {
try { setLoading(true);
setLoading(true); const newTotals = (
await Axios.post("/job/totals", {
job: job,
})
).data;
await Axios.post("/job/totalsssu", { const result = await updateJob({
id: job.id, refetchQueries: ["GET_JOB_BY_PK"],
}); awaitRefetchQueries: true,
variables: {
if (refetch) refetch(); jobId: job.id,
// const result = await updateJob({ job: {
// refetchQueries: ["GET_JOB_BY_PK"], job_totals: newTotals,
// awaitRefetchQueries: true, clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
// variables: { owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat(
// jobId: job.id, "0.00"
// job: { ),
// job_totals: newTotals, },
// clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"), },
// owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat( });
// "0.00" if (!!!result.errors) {
// ), notification["success"]({ message: t("jobs.successes.updated") });
// }, } else {
// },
// });
// if (!!!result.errors) {
// notification["success"]({ message: t("jobs.successes.updated") });
// } else {
// notification["error"]({
// message: t("jobs.errors.updating", {
// error: JSON.stringify(result.errors),
// }),
// });
// }
} catch (error) {
notification["error"]({ notification["error"]({
message: t("jobs.errors.updating", { message: t("jobs.errors.updating", {
error: JSON.stringify(error), error: JSON.stringify(result.errors),
}), }),
}); });
} finally {
setLoading(false);
} }
setLoading(false);
}; };
return ( return (

View File

@@ -32,9 +32,9 @@ const mapDispatchToProps = (dispatch) => ({
}); });
const span = { const span = {
lg: { span: 24 }, sm: { span: 24 },
xl: { span: 12 }, md: { span: 12 },
xxl: { span: 8 }, lg: { span: 8 },
}; };
export function JobDetailCards({ bodyshop, setPrintCenterContext }) { export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
@@ -137,6 +137,12 @@ export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
data={data ? data.jobs_by_pk : null} data={data ? data.jobs_by_pk : null}
/> />
</Col> </Col>
<Col {...span}>
<JobDetailCardsPartsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col {...span}> <Col {...span}>
<JobDetailCardsNotesComponent <JobDetailCardsNotesComponent
loading={loading} loading={loading}
@@ -157,12 +163,6 @@ export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
data={data ? data.jobs_by_pk : null} data={data ? data.jobs_by_pk : null}
/> />
</Col> </Col>
<Col span={24}>
<JobDetailCardsPartsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
</Row> </Row>
</Card> </Card>
) : null} ) : null}

View File

@@ -1,119 +1,16 @@
import { Table } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import JobLineNotePopup from "../job-line-note-popup/job-line-note-popup.component";
import PartsStatusPie from "../parts-status-pie/parts-status-pie.component"; import PartsStatusPie from "../parts-status-pie/parts-status-pie.component";
import CardTemplate from "./job-detail-cards.template.component"; import CardTemplate from "./job-detail-cards.template.component";
import { connect } from "react-redux"; export default function JobDetailCardsPartsComponent({ loading, data }) {
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { onlyUnique } from "../../utils/arrayHelper";
import { alphaSort } from "../../utils/sorters";
import JobLineLocationPopup from "../job-line-location-popup/job-line-location-popup.component";
import JobLineStatusPopup from "../job-line-status-popup/job-line-status-popup.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
jobRO: selectJobReadOnly,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobDetailCardsPartsComponent);
export function JobDetailCardsPartsComponent({ loading, data, jobRO }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { joblines_status } = data; const { joblines_status } = data;
const columns = [
{
title: t("joblines.fields.line_desc"),
dataIndex: "line_desc",
fixed: "left",
key: "line_desc",
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
onCell: (record) => ({
className: record.manual_line && "job-line-manual",
style: {
...(record.critical ? { boxShadow: " -.5em 0 0 #FFC107" } : {}),
},
}),
width: "30%",
ellipsis: true,
},
{
title: t("joblines.fields.part_type"),
dataIndex: "part_type",
key: "part_type",
width: "15%",
sorter: (a, b) =>
alphaSort(
t(`joblines.fields.part_types.${a.part_type}`),
t(`joblines.fields.part_types.${b.part_type}`)
),
render: (text, record) =>
record.part_type
? t(`joblines.fields.part_types.${record.part_type}`)
: null,
},
{
title: t("joblines.fields.part_qty"),
dataIndex: "part_qty",
key: "part_qty",
width: "10%",
},
{
title: t("joblines.fields.notes"),
dataIndex: "notes",
key: "notes",
render: (text, record) => (
<JobLineNotePopup disabled={jobRO} jobline={record} />
),
},
{
title: t("joblines.fields.location"),
dataIndex: "location",
key: "location",
sorter: (a, b) => alphaSort(a.location, b.location),
render: (text, record) => (
<JobLineLocationPopup jobline={record} disabled={jobRO} />
),
},
{
title: t("joblines.fields.status"),
dataIndex: "status",
key: "status",
sorter: (a, b) => alphaSort(a.status, b.status),
filters:
(data &&
data.joblines
?.map((l) => l.status)
.filter(onlyUnique)
.map((s) => {
return {
text: s || "No Status*",
value: [s],
};
})) ||
[],
onFilter: (value, record) => value.includes(record.status),
render: (text, record) => (
<JobLineStatusPopup jobline={record} disabled={jobRO} />
),
},
];
return ( return (
<div> <div>
<CardTemplate loading={loading} title={t("jobs.labels.cards.parts")}> <CardTemplate loading={loading} title={t("jobs.labels.cards.parts")}>
<PartsStatusPie joblines_status={joblines_status} /> <PartsStatusPie joblines_status={joblines_status} />
<Table
key="id"
columns={columns}
dataSource={data ? data.joblines : []}
/>
</CardTemplate> </CardTemplate>
</div> </div>
); );

View File

@@ -8,18 +8,7 @@ import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter"; import { DateFormatter } from "../../utils/DateFormatter";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import { connect } from "react-redux"; export default function JobLinesExpander({ jobline, jobid }) {
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)(JobLinesExpander);
export function JobLinesExpander({ jobline, jobid, bodyshop }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { loading, error, data } = useQuery(GET_JOB_LINE_ORDERS, { const { loading, error, data } = useQuery(GET_JOB_LINE_ORDERS, {
fetchPolicy: "network-only", fetchPolicy: "network-only",
@@ -34,7 +23,7 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
return ( return (
<Row> <Row>
<Col md={24} lg={8}> <Col md={24} lg={12}>
<Typography.Title level={4}> <Typography.Title level={4}>
{t("parts_orders.labels.parts_orders")} {t("parts_orders.labels.parts_orders")}
</Typography.Title> </Typography.Title>
@@ -60,7 +49,7 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
)} )}
</Timeline> </Timeline>
</Col> </Col>
<Col md={24} lg={8}> <Col md={24} lg={12}>
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title> <Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
<Timeline> <Timeline>
{data.billlines.length > 0 ? ( {data.billlines.length > 0 ? (
@@ -82,7 +71,7 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
</Col> </Col>
<Col span={4}> <Col span={4}>
<span> <span>
{`${t("billlines.fields.actual_cost")}: `} {`${t("billlines.fields.actual_cost")}: `}
<CurrencyFormatter>{line.actual_cost}</CurrencyFormatter> <CurrencyFormatter>{line.actual_cost}</CurrencyFormatter>
</span> </span>
</Col> </Col>
@@ -100,37 +89,6 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
)} )}
</Timeline> </Timeline>
</Col> </Col>
<Col md={24} lg={8}>
<Typography.Title level={4}>
{t("parts_dispatch.labels.parts_dispatch")}
</Typography.Title>
<Timeline>
{data.parts_dispatch_lines.length > 0 ? (
data.parts_dispatch_lines.map((line) => (
<Timeline.Item key={line.id}>
<Space split={<Divider type="vertical" />} wrap>
<Link to={`/manage/jobs/${jobid}?partsorderid=${line.id}`}>
{line.parts_dispatch.number}
</Link>
{
bodyshop.employees.find(
(e) => e.id === line.parts_dispatch.employeeid
)?.first_name
}
<Space>
{t("parts_dispatch_lines.fields.accepted_at")}
<DateFormatter>{line.accepted_at}</DateFormatter>
</Space>
</Space>
</Timeline.Item>
))
) : (
<Timeline.Item>
{t("parts_orders.labels.notyetordered")}
</Timeline.Item>
)}
</Timeline>
</Col>
</Row> </Row>
); );
} }

View File

@@ -1,97 +0,0 @@
import { useMutation } from "@apollo/client";
import { Button, Form, notification, Popover, Tooltip } from "antd";
import { t } from "i18next";
import React, { useState } from "react";
import { UPDATE_LINE_PPC } from "../../graphql/jobs-lines.queries";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
import JobLineConvertToLabor from "../job-line-convert-to-labor/job-line-convert-to-labor.component";
import axios from "axios";
export default function JobLinesPartPriceChange({ job, line, refetch }) {
const [loading, setLoading] = useState(false);
const [updatePartPrice] = useMutation(UPDATE_LINE_PPC);
const handleFinish = async (values) => {
try {
setLoading(true);
const result = await updatePartPrice({
variables: {
id: line.id,
jobline: {
act_price_before_ppc: line.act_price_before_ppc
? line.act_price_before_ppc
: line.act_price,
act_price: values.act_price,
},
},
});
await axios.post("/job/totalsssu", {
id: job.id,
});
if (result.errors) {
notification.open({
type: "error",
message: t("joblines.errors.saving", {
error: JSON.stringify(result.errors),
}),
});
if (refetch) refetch();
} else {
notification.open({
type: "success",
message: t("joblines.successes.saved"),
});
}
} catch (error) {
notification.open({
type: "error",
message: t("joblines.errors.saving", { error: JSON.stringify(error) }),
});
} finally {
setLoading(false);
}
};
const popcontent = (
<Form layout="vertical" onFinish={handleFinish} initialValues={{ act_price: line.act_price }}>
<Form.Item
name="act_price"
label={t("jobs.labels.act_price_ppc")}
rules={[{ required: true }]}
>
<CurrencyFormItemComponent />
</Form.Item>
<Button loading={loading} htmlType="primary">
{t("general.actions.save")}
</Button>
</Form>
);
return (
<JobLineConvertToLabor jobline={line} job={job}>
<Popover trigger="click" disabled={line.manual_line} content={popcontent}>
<CurrencyFormatter>
{line.db_ref === "900510" || line.db_ref === "900511"
? line.prt_dsmk_m
: line.act_price}
</CurrencyFormatter>
{line.prt_dsmk_p && line.prt_dsmk_p !== 0 ? (
<span style={{ marginLeft: ".2rem" }}>{`(${line.prt_dsmk_p}%)`}</span>
) : (
<></>
)}
{line.act_price_before_ppc && line.act_price_before_ppc !== 0 ? (
<Tooltip title={t("jobs.labels.ppc")}>
<span style={{ marginLeft: ".2rem", color: "tomato" }}>
(
<CurrencyFormatter>{line.act_price_before_ppc}</CurrencyFormatter>
)
</span>
</Tooltip>
) : (
<></>
)}
</Popover>
</JobLineConvertToLabor>
);
}

View File

@@ -1,12 +1,12 @@
import { import {
DeleteFilled, DeleteFilled,
EditFilled,
FilterFilled, FilterFilled,
HomeOutlined,
MinusCircleTwoTone,
PlusCircleTwoTone,
SyncOutlined, SyncOutlined,
WarningFilled, WarningFilled,
EditFilled,
PlusCircleTwoTone,
MinusCircleTwoTone,
HomeOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { import {
@@ -29,6 +29,7 @@ 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 { selectTechnician } from "../../redux/tech/tech.selectors";
import { onlyUnique } from "../../utils/arrayHelper"; import { onlyUnique } from "../../utils/arrayHelper";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
import JobLineLocationPopup from "../job-line-location-popup/job-line-location-popup.component"; import JobLineLocationPopup from "../job-line-location-popup/job-line-location-popup.component";
import JobLineNotePopup from "../job-line-note-popup/job-line-note-popup.component"; import JobLineNotePopup from "../job-line-note-popup/job-line-note-popup.component";
@@ -37,18 +38,13 @@ import JobLinesBillRefernece from "../job-lines-bill-reference/job-lines-bill-re
// import AllocationsAssignmentContainer from "../allocations-assignment/allocations-assignment.container"; // import AllocationsAssignmentContainer from "../allocations-assignment/allocations-assignment.container";
// import AllocationsBulkAssignmentContainer from "../allocations-bulk-assignment/allocations-bulk-assignment.container"; // import AllocationsBulkAssignmentContainer from "../allocations-bulk-assignment/allocations-bulk-assignment.container";
// import AllocationsEmployeeLabelContainer from "../allocations-employee-label/allocations-employee-label.container"; // import AllocationsEmployeeLabelContainer from "../allocations-employee-label/allocations-employee-label.container";
import _ from "lodash";
import moment from "moment";
import { selectBodyshop } from "../../redux/user/user.selectors";
import JobCreateIOU from "../job-create-iou/job-create-iou.component";
import JobSendPartPriceChangeComponent from "../job-send-parts-price-change/job-send-parts-price-change.component";
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container"; import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
import _ from "lodash";
import JobCreateIOU from "../job-create-iou/job-create-iou.component";
import JobLinesExpander from "./job-lines-expander.component"; import JobLinesExpander from "./job-lines-expander.component";
import JobLinesPartPriceChange from "./job-lines-part-price-change.component"; import { selectBodyshop } from "../../redux/user/user.selectors";
import JoblineTeamAssignment from "../job-line-team-assignment/job-line-team-assignmnent.component"; import moment from "moment";
import JobLineDispatchButton from "../job-line-dispatch-button/job-line-dispatch-button.component"; import JobLineConvertToLabor from "../job-line-convert-to-labor/job-line-convert-to-labor.component";
import JobLineBulkAssignComponent from "../job-line-bulk-assign/job-line-bulk-assign.component";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -80,16 +76,8 @@ export function JobLinesComponent({
setBillEnterContext, setBillEnterContext,
}) { }) {
const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK); const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK);
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
const [selectedLines, setSelectedLines] = useState([]); const [selectedLines, setSelectedLines] = useState([]);
console.log(
"🚀 ~ file: job-lines.component.jsx:89 ~ selectedLines:",
selectedLines
);
const [state, setState] = useState({ const [state, setState] = useState({
sortedInfo: {}, sortedInfo: {},
filteredInfo: {}, filteredInfo: {},
@@ -133,21 +121,10 @@ export function JobLinesComponent({
sortOrder: sortOrder:
state.sortedInfo.columnKey === "oem_partno" && state.sortedInfo.order, state.sortedInfo.columnKey === "oem_partno" && state.sortedInfo.order,
ellipsis: true, ellipsis: true,
onCell: (record) => ({ render: (text, record) =>
className: record.manual_line && "job-line-manual", `${record.oem_partno || ""} ${
style: { record.alt_partno ? `(${record.alt_partno})` : ""
...(record.parts_dispatch_lines[0]?.accepted_at }`.trim(),
? { boxShadow: " -.5em 0 0 #FFC107" }
: {}),
},
}),
render: (text, record) => (
<span class="ant-table-cell-content">
{`${record.oem_partno || ""} ${
record.alt_partno ? `(${record.alt_partno})` : ""
}`.trim()}
</span>
),
}, },
{ {
title: t("joblines.fields.op_code_desc"), title: t("joblines.fields.op_code_desc"),
@@ -243,7 +220,20 @@ 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) => (
<JobLinesPartPriceChange line={record} job={job} refetch={refetch} /> <JobLineConvertToLabor jobline={record} job={job}>
<CurrencyFormatter>
{record.db_ref === "900510" || record.db_ref === "900511"
? record.prt_dsmk_m
: record.act_price}
</CurrencyFormatter>
{record.prt_dsmk_p && record.prt_dsmk_p !== 0 ? (
<span
style={{ marginLeft: ".2rem" }}
>{`(${record.prt_dsmk_p}%)`}</span>
) : (
<></>
)}
</JobLineConvertToLabor>
), ),
}, },
{ {
@@ -296,23 +286,6 @@ export function JobLinesComponent({
state.sortedInfo.columnKey === "line_ind" && state.sortedInfo.order, state.sortedInfo.columnKey === "line_ind" && state.sortedInfo.order,
responsive: ["md"], responsive: ["md"],
}, },
...(Enhanced_Payroll.treatment === "on"
? [
{
title: t("joblines.fields.assigned_team"),
dataIndex: "assigned_team",
key: "assigned_team",
render: (text, record) => (
<JoblineTeamAssignment
disabled={jobRO}
jobline={record}
jobId={job.id}
/>
),
},
]
: []),
{ {
title: t("joblines.fields.notes"), title: t("joblines.fields.notes"),
dataIndex: "notes", dataIndex: "notes",
@@ -431,11 +404,7 @@ export function JobLinesComponent({
setSelectedLines((selectedLines) => setSelectedLines((selectedLines) =>
_.uniq([ _.uniq([
...selectedLines, ...selectedLines,
...jobLines.filter( ...jobLines.filter((item) => markedTypes.includes(item.part_type)),
(item) =>
markedTypes.includes(item.part_type) ||
markedTypes.includes(item.mod_lbr_ty)
),
]) ])
); );
} }
@@ -448,21 +417,6 @@ export function JobLinesComponent({
<Menu.Item key="PAL">{t("joblines.fields.part_types.PAL")}</Menu.Item> <Menu.Item key="PAL">{t("joblines.fields.part_types.PAL")}</Menu.Item>
<Menu.Item key="PAS">{t("joblines.fields.part_types.PAS")}</Menu.Item> <Menu.Item key="PAS">{t("joblines.fields.part_types.PAS")}</Menu.Item>
<Menu.Divider /> <Menu.Divider />
<Menu.Item key="LAA">{t("joblines.fields.lbr_types.LAA")}</Menu.Item>
<Menu.Item key="LAB">{t("joblines.fields.lbr_types.LAB")}</Menu.Item>
<Menu.Item key="LAD">{t("joblines.fields.lbr_types.LAD")}</Menu.Item>
<Menu.Item key="LAE">{t("joblines.fields.lbr_types.LAE")}</Menu.Item>
<Menu.Item key="LAF">{t("joblines.fields.lbr_types.LAF")}</Menu.Item>
<Menu.Item key="LAG">{t("joblines.fields.lbr_types.LAG")}</Menu.Item>
<Menu.Item key="LAM">{t("joblines.fields.lbr_types.LAM")}</Menu.Item>
<Menu.Item key="LAR">{t("joblines.fields.lbr_types.LAR")}</Menu.Item>
<Menu.Item key="LAS">{t("joblines.fields.lbr_types.LAS")}</Menu.Item>
<Menu.Item key="LAU">{t("joblines.fields.lbr_types.LAU")}</Menu.Item>
<Menu.Item key="LA1">{t("joblines.fields.lbr_types.LA1")}</Menu.Item>
<Menu.Item key="LA2">{t("joblines.fields.lbr_types.LA2")}</Menu.Item>
<Menu.Item key="LA3">{t("joblines.fields.lbr_types.LA3")}</Menu.Item>
<Menu.Item key="LA4">{t("joblines.fields.lbr_types.LA2")}</Menu.Item>
<Menu.Divider />
<Menu.Item key="clear">{t("general.labels.clear")}</Menu.Item> <Menu.Item key="clear">{t("general.labels.clear")}</Menu.Item>
</Menu> </Menu>
); );
@@ -486,18 +440,6 @@ export function JobLinesComponent({
</Space> </Space>
</Tag> </Tag>
)} )}
<JobLineDispatchButton
selectedLines={selectedLines}
setSelectedLines={setSelectedLines}
job={job}
/>
{Enhanced_Payroll.treatment === "on" && (
<JobLineBulkAssignComponent
selectedLines={selectedLines}
setSelectedLines={setSelectedLines}
job={job}
/>
)}
<Button <Button
disabled={ disabled={
(job && !job.converted) || (job && !job.converted) ||
@@ -506,6 +448,15 @@ export function JobLinesComponent({
technician technician
} }
onClick={() => { onClick={() => {
// setPartsOrderContext({
// actions: { refetch: refetch },
// context: {
// jobId: job.id,
// job: job,
// linesToOrder: selectedLines,
// },
// });
setBillEnterContext({ setBillEnterContext({
actions: { refetch: refetch }, actions: { refetch: refetch },
context: { context: {
@@ -612,9 +563,6 @@ export function JobLinesComponent({
> >
{t("joblines.actions.new")} {t("joblines.actions.new")}
</Button> </Button>
{bodyshop.region_config.toLowerCase().startsWith("us") && (
<JobSendPartPriceChangeComponent job={job} />
)}
<JobCreateIOU job={job} selectedJobLines={selectedLines} /> <JobCreateIOU job={job} selectedJobLines={selectedLines} />
<Input.Search <Input.Search
placeholder={t("general.labels.search")} placeholder={t("general.labels.search")}

View File

@@ -1,149 +0,0 @@
import React, { useState } from "react";
import { useMutation } from "@apollo/client";
import { Button, Form, Popover, Select, Space, notification } from "antd";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { UPDATE_LINE_BULK_ASSIGN } from "../../graphql/jobs-lines.queries";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
jobRO: selectJobReadOnly,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export default connect(mapStateToProps, mapDispatchToProps)(JoblineBulkAssign);
export function JoblineBulkAssign({
setSelectedLines,
selectedLines,
insertAuditTrail,
bodyshop,
jobRO,
job,
currentUser,
}) {
const [visible, setVisible] = useState(false);
const [loading, setLoading] = useState(false);
const [form] = Form.useForm();
const { t } = useTranslation();
const [assignLines] = useMutation(UPDATE_LINE_BULK_ASSIGN);
const handleConvert = async (values) => {
try {
setLoading(true);
const result = await assignLines({
variables: {
jobline: {
assigned_team: values.assigned_team,
},
ids: selectedLines.map((l) => l.id),
},
});
if (result.errors) {
notification.open({
type: "error",
message: t("parts_dispatch.errors.creating", {
error: JSON.stringify(result.errors),
}),
});
} else {
//Insert the audit trail here.
const teamName = bodyshop.employee_teams.find(
(et) => et.id === values.assigned_team
)?.name;
const hours = selectedLines.reduce(
(acc, val) => (acc += val.mod_lb_hrs),
0
);
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.assignedlinehours(
teamName,
hours.toFixed(1)
),
});
setSelectedLines([]);
setVisible(false);
}
} catch (error) {
notification.open({
type: "error",
message: t("parts_dispatch.errors.creating", {
error: error,
}),
});
} finally {
setLoading(false);
}
};
const popMenu = (
<div>
<Form layout="vertical" form={form} onFinish={handleConvert}>
<Form.Item
name={"assigned_team"}
label={t("joblines.fields.assigned_team")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select
showSearch
style={{ width: 200 }}
optionFilterProp="children"
filterOption={(input, option) =>
option.props.children
.toLowerCase()
.indexOf(input.toLowerCase()) >= 0
}
>
{bodyshop.employee_teams.map((team) => (
<Select.Option value={team.id} key={team.id} name={team.name}>
{team.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Space wrap>
<Button type="danger" onClick={() => form.submit()} loading={loading}>
{t("general.actions.save")}
</Button>
<Button onClick={() => setVisible(false)}>
{t("general.actions.cancel")}
</Button>
</Space>
</Form>
</div>
);
return (
<Popover open={visible} content={popMenu}>
<Button
disabled={selectedLines.length === 0 || jobRO}
loading={loading}
onClick={() => setVisible(true)}
>
{t("joblines.actions.assign_team", { count: selectedLines.length })}
</Button>
</Popover>
);
}

View File

@@ -1,167 +0,0 @@
import React, { useState } from "react";
import { useMutation } from "@apollo/client";
import { Button, Form, Popover, Select, Space, notification } from "antd";
import moment from "moment";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { INSERT_PARTS_DISPATCH } from "../../graphql/parts-dispatch.queries";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
jobRO: selectJobReadOnly,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobLineDispatchButton);
export function JobLineDispatchButton({
setSelectedLines,
selectedLines,
bodyshop,
jobRO,
job,
currentUser,
}) {
const [visible, setVisible] = useState(false);
const [loading, setLoading] = useState(false);
const [form] = Form.useForm();
const Templates = TemplateList("job_special", {
ro_number: job.ro_number,
});
const { t } = useTranslation();
const [dispatchLines] = useMutation(INSERT_PARTS_DISPATCH);
const handleConvert = async (values) => {
try {
setLoading(true);
//THIS HAS NOT YET BEEN TESTED. START BY FINISHING THIS FUNCTION.
const result = await dispatchLines({
variables: {
partsDispatch: {
dispatched_at: moment(),
employeeid: values.employeeid,
jobid: job.id,
dispatched_by: currentUser.email,
parts_dispatch_lines: {
data: selectedLines.map((l) => ({
joblineid: l.id,
quantity: l.part_qty,
})),
},
},
//joblineids: selectedLines.map((l) => l.id),
},
});
if (result.errors) {
notification.open({
type: "error",
message: t("parts_dispatch.errors.creating", {
error: result.errors,
}),
});
} else {
setSelectedLines([]);
await GenerateDocument(
{
name: Templates.parts_dispatch.key,
variables: {
id: result.data.insert_parts_dispatch_one.id,
},
},
{},
"p"
);
}
setVisible(false);
} catch (error) {
notification.open({
type: "error",
message: t("parts_dispatch.errors.creating", {
error: error,
}),
});
} finally {
setLoading(false);
}
};
const popMenu = (
<div>
<Form layout="vertical" form={form} onFinish={handleConvert}>
<Form.Item
name={"employeeid"}
label={t("timetickets.fields.employee")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select
showSearch
style={{ width: 200 }}
optionFilterProp="children"
filterOption={(input, option) =>
option.props.children
.toLowerCase()
.indexOf(input.toLowerCase()) >= 0
}
>
{bodyshop.employees
.filter((emp) => emp.active)
.map((emp) => (
<Select.Option
value={emp.id}
key={emp.id}
name={`${emp.first_name} ${emp.last_name}`}
>
{`${emp.first_name} ${emp.last_name}`}
</Select.Option>
))}
</Select>
</Form.Item>
<Space wrap>
<Button
type="danger"
onClick={() => form.submit()}
loading={loading}
disabled={selectedLines.length === 0}
>
{t("general.actions.save")}
</Button>
<Button onClick={() => setVisible(false)}>
{t("general.actions.cancel")}
</Button>
</Space>
</Form>
</div>
);
return (
<Popover open={visible} content={popMenu}>
<Button
disabled={selectedLines.length === 0 || jobRO}
loading={loading}
onClick={() => setVisible(true)}
>
{t("joblines.actions.dispatchparts", { count: selectedLines.length })}
</Button>
</Popover>
);
}

View File

@@ -1,5 +1,5 @@
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { notification, Select, Space } from "antd"; import { notification, Select } from "antd";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -77,10 +77,7 @@ export function JobLineLocationPopup({ bodyshop, jobline, disabled }) {
style={{ width: "100%", minHeight: "2rem", cursor: "pointer" }} style={{ width: "100%", minHeight: "2rem", cursor: "pointer" }}
onClick={() => !disabled && setEditing(true)} onClick={() => !disabled && setEditing(true)}
> >
<Space wrap> {jobline.location}
{jobline.location}
{jobline.parts_dispatch_lines?.length > 0 && "-Disp"}
</Space>
</div> </div>
); );
} }

View File

@@ -1,115 +0,0 @@
import { notification, Select } from "antd";
import React, { useEffect, useState } from "react";
import { useMutation } from "@apollo/client";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export function JoblineTeamAssignment({
bodyshop,
jobline,
disabled,
jobId,
insertAuditTrail,
}) {
const [editing, setEditing] = useState(false);
const [loading, setLoading] = useState(false);
const [assignedTeam, setAssignedTeam] = useState(jobline.assigned_team);
const [updateJob] = useMutation(UPDATE_JOB_LINE);
const { t } = useTranslation();
useEffect(() => {
if (editing) setAssignedTeam(jobline.assigned_team);
}, [editing, jobline.assigned_team]);
const handleChange = (e) => {
setAssignedTeam(e);
};
const handleSave = async (e) => {
setLoading(true);
const result = await updateJob({
variables: {
lineId: jobline.id,
line: { assigned_team: assignedTeam },
},
});
if (!!!result.errors) {
notification["success"]({ message: t("joblines.successes.saved") });
//insert the audit trail here.
const teamName = bodyshop.employee_teams.find(
(et) => et.id === assignedTeam
)?.name;
insertAuditTrail({
jobid: jobId,
operation: AuditTrailMapping.assignedlinehours(
teamName,
jobline.mod_lb_hrs
),
});
} else {
notification["error"]({
message: t("joblines.errors.saving", {
error: JSON.stringify(result.errors),
}),
});
}
setLoading(false);
setEditing(false);
};
if (editing)
return (
<div>
<LoadingSpinner loading={loading}>
<Select
autoFocus
allowClear
dropdownMatchSelectWidth={100}
value={assignedTeam}
onSelect={handleChange}
onBlur={handleSave}
onClear={() => handleChange(null)}
>
{Object.values(bodyshop.employee_teams).map((s, idx) => (
<Select.Option key={idx} value={s.id}>
{s.name}
</Select.Option>
))}
</Select>
</LoadingSpinner>
</div>
);
const team = bodyshop.employee_teams.find(
(tm) => tm.id === jobline.assigned_team
);
return (
<div
style={{ width: "100%", minHeight: "1rem", cursor: "pointer" }}
onClick={() => !disabled && setEditing(true)}
>
{team?.name}
</div>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(JoblineTeamAssignment);

View File

@@ -1,18 +0,0 @@
import { Alert } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
export default function JobProfileDataWarning({ job }) {
const { t } = useTranslation();
let missingProfileInfo =
Object.keys(job.cieca_pft).length === 0 ||
Object.keys(job.cieca_pfl).length === 0 ||
Object.keys(job.materials).length === 0;
if (missingProfileInfo)
return (
<Alert type="error" message={t("jobs.labels.missingprofileinfo")}></Alert>
);
return null;
}

View File

@@ -19,8 +19,7 @@ export default function JobReconciliationModalComponent({ job, bills }) {
const jobLineData = job.joblines.filter( const jobLineData = job.joblines.filter(
(j) => (j) =>
(j.part_type !== "PAE" && j.act_price !== 0 && j.part_qty !== 0) || (j.part_type !== null && j.part_type !== "PAE") ||
j.misc_amt !== 0 ||
(j.line_desc && (j.line_desc &&
j.line_desc.toLowerCase().includes("towing") && j.line_desc.toLowerCase().includes("towing") &&
j.lbr_op === "OP13") || j.lbr_op === "OP13") ||

View File

@@ -1,31 +0,0 @@
import { Button, notification } from "antd";
import axios from "axios";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
export default function JobSendPartPriceChangeComponent({ job }) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const handleClick = async () => {
setLoading(true);
try {
const ppcData = await axios.post("/job/ppc", { jobid: job.id });
await axios.post("http://localhost:1337/ppc/", ppcData.data);
} catch (error) {
notification.open({
type: "error",
message: t("jobs.errors.partspricechange", {
error: JSON.stringify(error),
}),
});
} finally {
setLoading(false);
}
};
return (
<Button onClick={handleClick} loading={loading}>
{t("jobs.actions.sendpartspricechange")}
</Button>
);
}

View File

@@ -67,8 +67,7 @@ export function JobsTotalsTableComponent({ jobRO, currentUser, job }) {
<JobTotalsTableTotals job={job} /> <JobTotalsTableTotals job={job} />
</Card> </Card>
</Col> </Col>
{(currentUser.email.includes("@imex.") || {currentUser.email.includes("@imex.") && (
currentUser.email.includes("@rome.")) && (
<Col span={24}> <Col span={24}>
<Card title="DEVELOPMENT USE ONLY"> <Card title="DEVELOPMENT USE ONLY">
<JobCalculateTotals job={job} disabled={jobRO} /> <JobCalculateTotals job={job} disabled={jobRO} />

View File

@@ -123,10 +123,11 @@ export default function JobTotalsTableLabor({ job }) {
<Space> <Space>
{t("jobs.labels.mapa")} {t("jobs.labels.mapa")}
{job.materials && {job.materials &&
job.materials.MAPA && job.materials.mapa &&
job.materials.MAPA.cal_maxdlr !== undefined && job.materials.mapa.cal_maxdlr &&
job.materials.mapa.cal_maxdlr > 0 &&
t("jobs.labels.threshhold", { t("jobs.labels.threshhold", {
amount: job.materials.MAPA.cal_maxdlr, amount: job.materials.mapa.cal_maxdlr,
})} })}
</Space> </Space>
</Table.Summary.Cell> </Table.Summary.Cell>
@@ -147,10 +148,11 @@ export default function JobTotalsTableLabor({ job }) {
<Space wrap> <Space wrap>
{t("jobs.labels.mash")} {t("jobs.labels.mash")}
{job.materials && {job.materials &&
job.materials.MASH && job.materials.mash &&
job.materials.MASH.cal_maxdlr !== undefined && job.materials.mash.cal_maxdlr &&
job.materials.mash.cal_maxdlr > 0 &&
t("jobs.labels.threshhold", { t("jobs.labels.threshhold", {
amount: job.materials.MASH.cal_maxdlr, amount: job.materials.mash.cal_maxdlr,
})} })}
</Space> </Space>
</Table.Summary.Cell> </Table.Summary.Cell>

View File

@@ -11,22 +11,6 @@ export default function JobTotalsTableParts({ job }) {
filteredInfo: {}, filteredInfo: {},
}); });
const insuranceAdjustments = useMemo(() => {
if (!job.job_totals) return [];
if (!job.job_totals?.parts?.adjustments) return [];
const adjs = [];
Object.keys(job.job_totals?.parts?.adjustments).forEach((key) => {
if (Dinero(job.job_totals?.parts?.adjustments[key]).getAmount() !== 0) {
adjs.push({
id: key,
amount: Dinero(job.job_totals.parts.adjustments[key]),
});
}
});
return adjs;
}, [job.job_totals]);
const data = useMemo(() => { const data = useMemo(() => {
return Object.keys(job.job_totals.parts.parts.list) return Object.keys(job.job_totals.parts.parts.list)
.filter( .filter(
@@ -90,11 +74,11 @@ export default function JobTotalsTableParts({ job }) {
<Table.Summary.Cell> <Table.Summary.Cell>
{t("jobs.labels.prt_dsmk_total")} {t("jobs.labels.prt_dsmk_total")}
</Table.Summary.Cell> </Table.Summary.Cell>
<Table.Summary.Cell align="right"> <Table.Summary.Cell align="right">
{Dinero(job.job_totals.parts.parts.prt_dsmk_total).toFormat()} {Dinero(job.job_totals.parts.parts.prt_dsmk_total).toFormat()}
</Table.Summary.Cell> </Table.Summary.Cell>
</Table.Summary.Row> </Table.Summary.Row>
<Table.Summary.Row> <Table.Summary.Row>
<Table.Summary.Cell> <Table.Summary.Cell>
<strong>{t("jobs.labels.partstotal")}</strong> <strong>{t("jobs.labels.partstotal")}</strong>
@@ -106,24 +90,6 @@ export default function JobTotalsTableParts({ job }) {
</strong> </strong>
</Table.Summary.Cell> </Table.Summary.Cell>
</Table.Summary.Row> </Table.Summary.Row>
{insuranceAdjustments.length > 0 && (
<Table.Summary.Row>
<Table.Summary.Cell colSpan={24}>
{t("jobs.labels.profileadjustments")}
</Table.Summary.Cell>
</Table.Summary.Row>
)}
{insuranceAdjustments.map((adj, idx) => (
<Table.Summary.Row key={idx}>
<Table.Summary.Cell>
{t(`jobs.fields.${adj.id.toLowerCase()}`)}
</Table.Summary.Cell>
<Table.Summary.Cell align="right">
{adj.amount.toFormat()}
</Table.Summary.Cell>
</Table.Summary.Row>
))}
</> </>
)} )}
/> />

View File

@@ -28,109 +28,26 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
total: job.job_totals.totals.subtotal, total: job.job_totals.totals.subtotal,
bold: true, bold: true,
}, },
{
...(job.job_totals.totals.us_sales_tax_breakdown key: t("jobs.labels.local_tax_amt"),
total: job.job_totals.totals.local_tax,
},
{
key: t("jobs.labels.state_tax_amt"),
total: job.job_totals.totals.state_tax,
},
...(bodyshop.region_config === "CA_BC"
? [ ? [
{ {
key: `${ key: t("jobs.fields.ca_bc_pvrt"),
bodyshop.md_responsibility_centers.taxes.tax_ty1?.tax_type1 || total: job.job_totals.additional.pvrt,
"T1"
} - ${[
job.cieca_pft.ty1_rate1,
job.cieca_pft.ty1_rate2,
job.cieca_pft.ty1_rate3,
job.cieca_pft.ty1_rate4,
job.cieca_pft.ty1_rate5,
]
.filter((i) => i > 0)
.join(", ")}%`,
total: job.job_totals.totals.us_sales_tax_breakdown.ty1Tax,
}, },
{ ]
key: `${ : []),
bodyshop.md_responsibility_centers.taxes.tax_ty2?.tax_type2 || {
"T2" key: t("jobs.labels.federal_tax_amt"),
} - ${[ total: job.job_totals.totals.federal_tax,
job.cieca_pft.ty2_rate1, },
job.cieca_pft.ty2_rate2,
job.cieca_pft.ty2_rate3,
job.cieca_pft.ty2_rate4,
job.cieca_pft.ty2_rate5,
]
.filter((i) => i > 0)
.join(", ")}%`,
total: job.job_totals.totals.us_sales_tax_breakdown.ty2Tax,
},
{
key: `${
bodyshop.md_responsibility_centers.taxes.tax_ty3?.tax_type3 ||
"T3"
} - ${[
job.cieca_pft.ty3_rate1,
job.cieca_pft.ty3_rate2,
job.cieca_pft.ty3_rate3,
job.cieca_pft.ty3_rate4,
job.cieca_pft.ty3_rate5,
]
.filter((i) => i > 0)
.join(", ")}%`,
total: job.job_totals.totals.us_sales_tax_breakdown.ty3Tax,
},
{
key: `${
bodyshop.md_responsibility_centers.taxes.tax_ty4?.tax_type4 ||
"T4"
} - ${[
job.cieca_pft.ty4_rate1,
job.cieca_pft.ty4_rate2,
job.cieca_pft.ty4_rate3,
job.cieca_pft.ty4_rate4,
job.cieca_pft.ty4_rate5,
]
.filter((i) => i > 0)
.join(", ")}%`,
total: job.job_totals.totals.us_sales_tax_breakdown.ty4Tax,
},
{
key: `${
bodyshop.md_responsibility_centers.taxes.tax_ty5?.tax_type5 ||
"TT"
} - ${[
job.cieca_pft.ty5_rate1,
job.cieca_pft.ty5_rate2,
job.cieca_pft.ty5_rate3,
job.cieca_pft.ty5_rate4,
job.cieca_pft.ty5_rate5,
]
.filter((i) => i > 0)
.join(", ")}%`,
total: job.job_totals.totals.us_sales_tax_breakdown.ty5Tax,
},
{
key: t("jobs.labels.total_sales_tax"),
bold: true,
total: Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty1Tax)
.add(
Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty2Tax)
)
.add(
Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty3Tax)
)
.add(
Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty4Tax)
)
.add(
Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty5Tax)
).toJSON(),
},
].filter((item) => item.total.amount !== 0)
: [
{
key: t("jobs.labels.state_tax_amt"),
total: job.job_totals.totals.state_tax,
},
]),
{ {
key: t("jobs.labels.total_repairs"), key: t("jobs.labels.total_repairs"),
total: job.job_totals.totals.total_repairs, total: job.job_totals.totals.total_repairs,
@@ -140,10 +57,10 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
key: t("jobs.fields.ded_amt"), key: t("jobs.fields.ded_amt"),
total: job.job_totals.totals.custPayable.deductible, total: job.job_totals.totals.custPayable.deductible,
}, },
// { {
// key: t("jobs.fields.federal_tax_payable"), key: t("jobs.fields.federal_tax_payable"),
// total: job.job_totals.totals.custPayable.federal_tax, total: job.job_totals.totals.custPayable.federal_tax,
// }, },
{ {
key: t("jobs.fields.other_amount_payable"), key: t("jobs.fields.other_amount_payable"),
total: job.job_totals.totals.custPayable.other_customer_amount, total: job.job_totals.totals.custPayable.other_customer_amount,
@@ -164,7 +81,7 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
bold: true, bold: true,
}, },
]; ];
}, [job.job_totals, job.cieca_pft, t, bodyshop.md_responsibility_centers]); }, [job.job_totals, t, bodyshop.region_config]);
const columns = [ const columns = [
{ {

View File

@@ -25,7 +25,7 @@ export const GetSupplementDelta = async (client, jobId, newLines) => {
//Found a relevant matching line. Add it to lines to update. //Found a relevant matching line. Add it to lines to update.
linesToUpdate.push({ linesToUpdate.push({
id: existingLines[matchingIndex].id, id: existingLines[matchingIndex].id,
newData: { ...newLine, removed: false, act_price_before_ppc: null }, newData: { ...newLine, removed: false },
}); });
//Splice out item we found for performance. //Splice out item we found for performance.

View File

@@ -6,9 +6,9 @@ import {
useQuery, useQuery,
} from "@apollo/client"; } from "@apollo/client";
import { useTreatments } from "@splitsoftware/splitio-react"; import { useTreatments } from "@splitsoftware/splitio-react";
import { Button, Col, Row, notification } from "antd"; import { Col, notification, Row } from "antd";
import Axios from "axios"; import Axios from "axios";
import _ from "lodash"; import Dinero from "dinero.js";
import moment from "moment"; import moment from "moment";
import queryString from "query-string"; import queryString from "query-string";
import React, { useCallback, useEffect, useState } from "react"; import React, { useCallback, useEffect, useState } from "react";
@@ -30,6 +30,7 @@ import {
selectBodyshop, selectBodyshop,
selectCurrentUser, selectCurrentUser,
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import confirmDialog from "../../utils/asyncConfirm";
import AuditTrailMapping from "../../utils/AuditTrailMappings"; import AuditTrailMapping from "../../utils/AuditTrailMappings";
import CriticalPartsScan from "../../utils/criticalPartsScan"; import CriticalPartsScan from "../../utils/criticalPartsScan";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
@@ -91,15 +92,14 @@ export function JobsAvailableContainer({
const modalSearchState = useState(""); const modalSearchState = useState("");
//Import Scenario //Import Scenario
const onOwnerFindModalOk = async (lazyData) => { const onOwnerFindModalOk = async () => {
logImEXEvent("job_import_new"); logImEXEvent("job_import_new");
setOwnerModalVisible(false); setOwnerModalVisible(false);
setInsertLoading(true); setInsertLoading(true);
const estData = replaceEmpty(
lazyData?.available_jobs_by_pk || estDataRaw.data.available_jobs_by_pk const estData = replaceEmpty(estDataRaw.data.available_jobs_by_pk);
);
if (!(estData && estData.est_data)) { if (!(estData && estData.est_data)) {
//We don't have the right data. Error! //We don't have the right data. Error!
@@ -109,21 +109,17 @@ export function JobsAvailableContainer({
}); });
return; return;
} }
// if (process.env.REACT_APP_COUNTRY === "USA") {
//Massage the CCC file set to remove duplicate UNQ_SEQ.
await ResolveCCCLineIssues(estData.est_data, bodyshop);
// } else {
//IO-539 Check for Parts Rate on PAL for SGI use case. //IO-539 Check for Parts Rate on PAL for SGI use case.
await CheckTaxRates(estData.est_data, bodyshop); await CheckTaxRates(estData.est_data, bodyshop);
// }
// const newTotals = ( const newTotals = (
// await Axios.post("/job/totals", { await Axios.post("/job/totals", {
// job: { job: {
// ...estData.est_data, ...estData.est_data,
// joblines: estData.est_data.joblines.data, joblines: estData.est_data.joblines.data,
// }, },
// }) })
// ).data; ).data;
let existingVehicles; let existingVehicles;
if (estData.est_data.v_vin) { if (estData.est_data.v_vin) {
@@ -138,9 +134,9 @@ export function JobsAvailableContainer({
const newJob = { const newJob = {
...estData.est_data, ...estData.est_data,
// clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"), clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
// owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"), owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"),
// job_totals: newTotals, job_totals: newTotals,
date_open: moment(), date_open: moment(),
status: bodyshop.md_ro_statuses.default_imported, status: bodyshop.md_ro_statuses.default_imported,
notes: { notes: {
@@ -164,23 +160,17 @@ export function JobsAvailableContainer({
delete newJob.vehicle; delete newJob.vehicle;
} }
if (typeof newJob.kmin === "string") {
newJob.kmin = null;
}
try { try {
const r = await insertNewJob({ const r = await insertNewJob({
variables: { variables: {
job: newJob, job: newJob,
}, },
}); });
await Axios.post("/job/totalsssu", {
id: r.data.insert_jobs.returning[0].id,
});
if (CriticalPartsScanning.treatment === "on") { if (CriticalPartsScanning.treatment === "on") {
CriticalPartsScan(r.data.insert_jobs.returning[0].id); CriticalPartsScan(r.data.insert_jobs.returning[0].id);
} }
notification["success"]({ notification["success"]({
message: t("jobs.successes.created"), message: t("jobs.successes.created"),
onClick: () => { onClick: () => {
@@ -194,7 +184,7 @@ export function JobsAvailableContainer({
operation: AuditTrailMapping.jobimported(), operation: AuditTrailMapping.jobimported(),
}); });
await deleteJob({ deleteJob({
variables: { id: estData.id }, variables: { id: estData.id },
}).then((r) => { }).then((r) => {
refetch(); refetch();
@@ -202,16 +192,16 @@ export function JobsAvailableContainer({
}); });
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle); setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
} catch (r) { } catch (err) {
//error while inserting //error while inserting
notification["error"]({ notification["error"]({
message: t("jobs.errors.creating", { error: r.message }), message: t("jobs.errors.creating", { error: err.message }),
}); });
refetch(); refetch().catch(e => {console.error(`Something went wrong in jobs available table container - ${err.message || ''}`)});
setInsertLoading(false); setInsertLoading(false);
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle); setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
} }
}; };
//Supplement scenario //Supplement scenario
@@ -234,7 +224,6 @@ export function JobsAvailableContainer({
let supp = replaceEmpty({ ...estData.est_data }); let supp = replaceEmpty({ ...estData.est_data });
//IO-539 Check for Parts Rate on PAL for SGI use case. //IO-539 Check for Parts Rate on PAL for SGI use case.
await CheckTaxRates(supp, bodyshop); await CheckTaxRates(supp, bodyshop);
await ResolveCCCLineIssues(supp, bodyshop);
delete supp.owner; delete supp.owner;
delete supp.vehicle; delete supp.vehicle;
@@ -410,25 +399,6 @@ export function JobsAvailableContainer({
partsQueueToggle={partsQueueToggle} partsQueueToggle={partsQueueToggle}
setPartsQueueToggle={setPartsQueueToggle} setPartsQueueToggle={setPartsQueueToggle}
/> />
{currentUser.email.includes("@rome.") ||
currentUser.email.includes("@imex.") ? (
<Button
onClick={async () => {
for (const record of data.available_jobs) {
//Query the data
console.log("Start Job", record.id);
const { data } = await loadEstData({
variables: { id: record.id },
});
console.log("Query has been awaited and is complete");
await onOwnerFindModalOk(data);
}
}}
>
Add all jobs as new.
</Button>
) : null}
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
<Col span={24}> <Col span={24}>
<JobsAvailableTableComponent <JobsAvailableTableComponent
@@ -460,158 +430,115 @@ function replaceEmpty(someObj, replaceValue = null) {
} }
async function CheckTaxRates(estData, bodyshop) { async function CheckTaxRates(estData, bodyshop) {
// //LKQ Check //LKQ Check
// if ( if (
// !estData.parts_tax_rates?.PAL || !estData.parts_tax_rates?.PAL ||
// estData.parts_tax_rates?.PAL?.prt_tax_rt === null || estData.parts_tax_rates?.PAL?.prt_tax_rt === null ||
// estData.parts_tax_rates?.PAL?.prt_tax_rt === 0 estData.parts_tax_rates?.PAL?.prt_tax_rt === 0
// ) { ) {
// const res = await confirmDialog( const res = await confirmDialog(
// `Rome Online has detected that there is a missing tax rate for LKQ parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.` `ImEX Online has detected that there is a missing tax rate for LKQ parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
// ); );
// if (res) { if (res) {
// if (!estData.parts_tax_rates.PAL) { if (!estData.parts_tax_rates.PAL) {
// estData.parts_tax_rates.PAL = { estData.parts_tax_rates.PAL = {
// prt_discp: 0, prt_discp: 0,
// prt_mktyp: true, prt_mktyp: true,
// prt_mkupp: 0, prt_mkupp: 0,
// prt_type: "PAL", prt_type: "PAL",
// }; };
// } }
// estData.parts_tax_rates.PAL.prt_tax_rt = estData.parts_tax_rates.PAL.prt_tax_rt =
// bodyshop.bill_tax_rates.state_tax_rate / 100; bodyshop.bill_tax_rates.state_tax_rate / 100;
// estData.parts_tax_rates.PAL.prt_tax_in = true; estData.parts_tax_rates.PAL.prt_tax_in = true;
// } }
// } }
// //PAC Check //PAC Check
// if ( if (
// !estData.parts_tax_rates?.PAC || !estData.parts_tax_rates?.PAC ||
// estData.parts_tax_rates?.PAC?.prt_tax_rt === null || estData.parts_tax_rates?.PAC?.prt_tax_rt === null ||
// estData.parts_tax_rates?.PAC?.prt_tax_rt === 0 estData.parts_tax_rates?.PAC?.prt_tax_rt === 0
// ) { ) {
// const res = await confirmDialog( const res = await confirmDialog(
// `Rome Online has detected that there is a missing tax rate for rechromed parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.` `ImEX Online has detected that there is a missing tax rate for rechromed parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
// ); );
// if (res) { if (res) {
// if (!estData.parts_tax_rates.PAC) { if (!estData.parts_tax_rates.PAC) {
// estData.parts_tax_rates.PAC = { estData.parts_tax_rates.PAC = {
// prt_discp: 0, prt_discp: 0,
// prt_mktyp: true, prt_mktyp: true,
// prt_mkupp: 0, prt_mkupp: 0,
// prt_type: "PAC", prt_type: "PAC",
// }; };
// } }
// estData.parts_tax_rates.PAC.prt_tax_rt = estData.parts_tax_rates.PAC.prt_tax_rt =
// bodyshop.bill_tax_rates.state_tax_rate / 100; bodyshop.bill_tax_rates.state_tax_rate / 100;
// estData.parts_tax_rates.PAC.prt_tax_in = true; estData.parts_tax_rates.PAC.prt_tax_in = true;
// } }
// } }
//PAM Check //PAM Check
if (!estData.parts_tax_rates?.PAM) { if (
estData.parts_tax_rates.PAM = estData.parts_tax_rates.PAC; !estData.parts_tax_rates?.PAM ||
estData.parts_tax_rates?.PAM?.prt_tax_rt === null ||
estData.parts_tax_rates?.PAM?.prt_tax_rt === 0
) {
const res = await confirmDialog(
`ImEX Online has detected that there is a missing tax rate for remanufactured parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
);
if (res) {
if (!estData.parts_tax_rates.PAM) {
estData.parts_tax_rates.PAM = {
prt_discp: 0,
prt_mktyp: true,
prt_mkupp: 0,
prt_type: "PAM",
};
}
estData.parts_tax_rates.PAM.prt_tax_rt =
bodyshop.bill_tax_rates.state_tax_rate / 100;
estData.parts_tax_rates.PAM.prt_tax_in = true;
}
} }
// //PAM Check if (
// if ( !estData.parts_tax_rates?.PAR ||
// !estData.parts_tax_rates?.PAM || estData.parts_tax_rates?.PAR?.prt_tax_rt === null ||
// estData.parts_tax_rates?.PAM?.prt_tax_rt === null || estData.parts_tax_rates?.PAR?.prt_tax_rt === 0
// estData.parts_tax_rates?.PAM?.prt_tax_rt === 0 ) {
// ) { const res = await confirmDialog(
// const res = await confirmDialog( `ImEX Online has detected that there is a missing tax rate for recored parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
// `Rome Online has detected that there is a missing tax rate for remanufactured parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.` );
// ); if (res) {
// if (res) { if (!estData.parts_tax_rates.PAR) {
// if (!estData.parts_tax_rates.PAM) { estData.parts_tax_rates.PAR = {
// estData.parts_tax_rates.PAM = { prt_discp: 0,
// prt_discp: 0, prt_mktyp: true,
// prt_mktyp: true, prt_mkupp: 0,
// prt_mkupp: 0, prt_type: "PAR",
// prt_type: "PAM", };
// }; }
// } estData.parts_tax_rates.PAR.prt_tax_rt =
// estData.parts_tax_rates.PAM.prt_tax_rt = bodyshop.bill_tax_rates.state_tax_rate / 100;
// bodyshop.bill_tax_rates.state_tax_rate / 100; estData.parts_tax_rates.PAR.prt_tax_in = true;
// estData.parts_tax_rates.PAM.prt_tax_in = true; }
// } }
// }
// if (
// !estData.parts_tax_rates?.PAR ||
// estData.parts_tax_rates?.PAR?.prt_tax_rt === null ||
// estData.parts_tax_rates?.PAR?.prt_tax_rt === 0
// ) {
// const res = await confirmDialog(
// `Rome Online has detected that there is a missing tax rate for recored parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
// );
// if (res) {
// if (!estData.parts_tax_rates.PAR) {
// estData.parts_tax_rates.PAR = {
// prt_discp: 0,
// prt_mktyp: true,
// prt_mkupp: 0,
// prt_type: "PAR",
// };
// }
// estData.parts_tax_rates.PAR.prt_tax_rt =
// bodyshop.bill_tax_rates.state_tax_rate / 100;
// estData.parts_tax_rates.PAR.prt_tax_in = true;
// }
// }
//IO-1387 If a sublet line is NOT R&R, use the labor tax. If it is, use the sublet tax rate. //IO-1387 If a sublet line is NOT R&R, use the labor tax. If it is, use the sublet tax rate.
//Currently limited to SK shops only. //Currently limited to SK shops only.
if (bodyshop.region_config === "CA_SK") { //if (bodyshop.region_config === "CA_SK") {
estData.joblines.data.forEach((jl, index) => { estData.joblines.data.forEach((jl, index) => {
if ( if (
(jl.part_type === "PASL" || jl.part_type === "PAS") && (jl.part_type === "PASL" || jl.part_type === "PAS") &&
jl.lbr_op !== "OP11" jl.lbr_op !== "OP11"
) { ) {
estData.joblines.data[index].tax_part = jl.lbr_tax; estData.joblines.data[index].tax_part = jl.lbr_tax;
} }
//Set markup lines and tax lines as taxable. //Set markup lines and tax lines as taxable.
//900510 is a mark up. 900510 is a discount. //900510 is a mark up. 900510 is a discount.
if (jl.db_ref === "900510") { if (jl.db_ref === "900510") {
estData.joblines.data[index].tax_part = true; estData.joblines.data[index].tax_part = true;
}
});
}
}
async function ResolveCCCLineIssues(estData, bodyshop) {
//Find all misc amounts, populate them to the act price.
//TODO Ensure that this doesnt get violated
//This needs to be done before cleansing unq_seq since some misc prices could move over.
estData.joblines.data.forEach((line) => {
if (line.misc_amt && line.misc_amt !== 0) {
line.act_price = line.act_price + line.misc_amt;
line.tax_part = !!line.misc_tax;
} }
}); });
//}
//Generate the list of duplicated UNQ_SEQ that will feed into the next section to scrub the lines.
const unqSeqHash = _.groupBy(estData.joblines.data, "unq_seq");
const duplicatedUnqSeq = Object.keys(unqSeqHash).filter(
(key) => unqSeqHash[key].length > 1
);
duplicatedUnqSeq.forEach((unq_seq) => {
//Keys are strings, convert to int.
const int_unq_seq = parseInt(unq_seq);
//When line splitting, the first line is always the non-refinish line. We will keep it as is.
//We will cleanse the second line, which is always the next line.
const nonRefLineIndex = estData.joblines.data.findIndex(
(line) => line.unq_seq === int_unq_seq
);
estData.joblines.data[nonRefLineIndex + 1] = {
...estData.joblines.data[nonRefLineIndex + 1],
part_type: null,
act_price: 0,
db_price: 0,
prt_dsmk_p: 0,
prt_dsmk_m: 0,
};
});
} }

View File

@@ -36,8 +36,6 @@ export function JobsCloseAutoAllocate({ bodyshop, joblines, form, disabled }) {
ret.profitcenter_part = defaults.profits["MAPA"]; ret.profitcenter_part = defaults.profits["MAPA"];
} else if (lineDesc.includes("ats amount")) { } else if (lineDesc.includes("ats amount")) {
ret.profitcenter_part = defaults.profits["ATS"]; ret.profitcenter_part = defaults.profits["ATS"];
} else if (jl.act_price > 0) {
ret.profitcenter_part = defaults.profits["PAO"];
} else { } else {
ret.profitcenter_part = null; ret.profitcenter_part = null;
} }

View File

@@ -1,4 +1,4 @@
import { Collapse, Form, Input, Select, Switch } from "antd"; import { Collapse, Form, Input, InputNumber, Select, Switch } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -12,12 +12,6 @@ import FormItemPhone, {
} from "../form-items-formatted/phone-form-item.component"; } from "../form-items-formatted/phone-form-item.component";
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component"; import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
import JobsDetailRatesParts from "../jobs-detail-rates/jobs-detail-rates.parts.component"; import JobsDetailRatesParts from "../jobs-detail-rates/jobs-detail-rates.parts.component";
import JobsDetailRatesLabor from "../jobs-detail-rates/jobs-detail-rates.labor.component";
import JobsDetailRatesMaterials from "../jobs-detail-rates/jobs-detail-rates.materials.component";
import JobsDetailRatesOther from "../jobs-detail-rates/jobs-detail-rates.other.component";
import JobsDetailRatesTaxes from "../jobs-detail-rates/jobs-detail-rates.taxes.component";
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component"; import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
@@ -264,28 +258,26 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
<CurrencyInput /> <CurrencyInput />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
{ <LayoutFormRow>
// <LayoutFormRow> <Form.Item
// <Form.Item label={t("jobs.fields.federal_tax_rate")}
// label={t("jobs.fields.federal_tax_rate")} name="federal_tax_rate"
// name="federal_tax_rate" >
// > <InputNumber min={0} max={1} precision={2} />
// <InputNumber min={0} max={1} precision={2} /> </Form.Item>
// </Form.Item> <Form.Item
// <Form.Item label={t("jobs.fields.state_tax_rate")}
// label={t("jobs.fields.state_tax_rate")} name="state_tax_rate"
// name="state_tax_rate" >
// > <InputNumber min={0} max={1} precision={2} />
// <InputNumber min={0} max={1} precision={2} /> </Form.Item>
// </Form.Item> <Form.Item
// <Form.Item label={t("jobs.fields.local_tax_rate")}
// label={t("jobs.fields.local_tax_rate")} name="local_tax_rate"
// name="local_tax_rate" >
// > <InputNumber min={0} max={1} precision={2} />
// <InputNumber min={0} max={1} precision={2} /> </Form.Item>
// </Form.Item> </LayoutFormRow>
// </LayoutFormRow>
}
<LayoutFormRow> <LayoutFormRow>
<Form.Item label={t("jobs.fields.rate_lab")} name="rate_lab"> <Form.Item label={t("jobs.fields.rate_lab")} name="rate_lab">
<CurrencyInput /> <CurrencyInput />
@@ -364,10 +356,6 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
required={selected && true} required={selected && true}
form={form} form={form}
/> />
<JobsDetailRatesLabor form={form} />
<JobsDetailRatesMaterials form={form} />
<JobsDetailRatesOther form={form} />
<JobsDetailRatesTaxes form={form} />
</div> </div>
); );
} }

View File

@@ -50,8 +50,6 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(setModalContext({ context: context, modal: "jobCosting" })), dispatch(setModalContext({ context: context, modal: "jobCosting" })),
setTimeTicketContext: (context) => setTimeTicketContext: (context) =>
dispatch(setModalContext({ context: context, modal: "timeTicket" })), dispatch(setModalContext({ context: context, modal: "timeTicket" })),
setTimeTicketTaskContext: (context) =>
dispatch(setModalContext({ context: context, modal: "timeTicketTask" })),
setCardPaymentContext: (context) => setCardPaymentContext: (context) =>
dispatch(setModalContext({ context: context, modal: "cardPayment" })), dispatch(setModalContext({ context: context, modal: "cardPayment" })),
insertAuditTrail: ({ jobid, operation }) => insertAuditTrail: ({ jobid, operation }) =>
@@ -69,7 +67,6 @@ export function JobsDetailHeaderActions({
setJobCostingContext, setJobCostingContext,
jobRO, jobRO,
setTimeTicketContext, setTimeTicketContext,
setTimeTicketTaskContext,
setCardPaymentContext, setCardPaymentContext,
insertAuditTrail, insertAuditTrail,
}) { }) {
@@ -271,24 +268,6 @@ export function JobsDetailHeaderActions({
> >
{t("timetickets.actions.enter")} {t("timetickets.actions.enter")}
</Menu.Item> </Menu.Item>
{bodyshop.md_tasks_presets.enable_tasks && (
<Menu.Item
key="claimtimetickettasks"
disabled={
!job.converted ||
(!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced)
}
onClick={() => {
setTimeTicketTaskContext({
actions: {},
context: { jobid: job.id },
});
}}
>
{t("timetickets.actions.claimtasks")}
</Menu.Item>
)}
<Menu.Item <Menu.Item
key="enterpayments" key="enterpayments"
disabled={!job.converted} disabled={!job.converted}

View File

@@ -5,13 +5,9 @@ import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors"; import { selectJobReadOnly } from "../../redux/application/application.selectors";
import LaborAllocationsTableComponent from "../labor-allocations-table/labor-allocations-table.component"; import LaborAllocationsTableComponent from "../labor-allocations-table/labor-allocations-table.component";
import TimeTicketList from "../time-ticket-list/time-ticket-list.component"; import TimeTicketList from "../time-ticket-list/time-ticket-list.component";
import PayrollLaborAllocationsTable from "../labor-allocations-table/labor-allocations-table.payroll.component";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly, jobRO: selectJobReadOnly,
bodyshop: selectBodyshop,
}); });
export default connect(mapStateToProps, null)(JobsDetailLaborContainer); export default connect(mapStateToProps, null)(JobsDetailLaborContainer);
@@ -52,7 +48,6 @@ const adjSpan = {
}; };
export function JobsDetailLaborContainer({ export function JobsDetailLaborContainer({
bodyshop,
jobRO, jobRO,
job, job,
jobId, jobId,
@@ -63,12 +58,6 @@ export function JobsDetailLaborContainer({
techConsole, techConsole,
adjustments, adjustments,
}) { }) {
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
return ( return (
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
<Col {...ticketSpan}> <Col {...ticketSpan}>
@@ -81,28 +70,14 @@ export function JobsDetailLaborContainer({
jobId={jobId} jobId={jobId}
/> />
</Col> </Col>
<Col {...adjSpan}>
{Enhanced_Payroll.treatment === "on" ? ( <LaborAllocationsTableComponent
<Col {...adjSpan}> jobId={jobId}
<PayrollLaborAllocationsTable joblines={joblines}
jobId={jobId} timetickets={timetickets}
joblines={joblines} adjustments={adjustments}
timetickets={timetickets} />
refetch={refetch} </Col>
adjustments={adjustments}
/>
</Col>
) : (
<Col {...adjSpan}>
<LaborAllocationsTableComponent
jobId={jobId}
joblines={joblines}
timetickets={timetickets}
refetch={refetch}
adjustments={adjustments}
/>
</Col>
)}
</Row> </Row>
); );
} }

View File

@@ -6,14 +6,12 @@ import BillsListTable from "../bills-list-table/bills-list-table.component";
import JobBillsTotal from "../job-bills-total/job-bills-total.component"; import JobBillsTotal from "../job-bills-total/job-bills-total.component";
import PartsOrderListTableComponent from "../parts-order-list-table/parts-order-list-table.component"; import PartsOrderListTableComponent from "../parts-order-list-table/parts-order-list-table.component";
import PartsOrderModal from "../parts-order-modal/parts-order-modal.container"; import PartsOrderModal from "../parts-order-modal/parts-order-modal.container";
import PartsDispatchTable from "../parts-dispatch-table/parts-dispatch-table.component";
export default function JobsDetailPliComponent({ export default function JobsDetailPliComponent({
job, job,
billsQuery, billsQuery,
handleBillOnRowClick, handleBillOnRowClick,
handlePartsOrderOnRowClick, handlePartsOrderOnRowClick,
handlePartsDispatchOnRowClick,
}) { }) {
return ( return (
<div> <div>
@@ -45,13 +43,6 @@ export default function JobsDetailPliComponent({
billsQuery={billsQuery} billsQuery={billsQuery}
/> />
</Col> </Col>
<Col span={24}>
<PartsDispatchTable
job={job}
handleOnRowClick={handlePartsDispatchOnRowClick}
billsQuery={billsQuery}
/>
</Col>
</Row> </Row>
</div> </div>
); );

View File

@@ -39,24 +39,12 @@ export default function JobsDetailPliContainer({ job }) {
} }
}; };
const handlePartsDispatchOnRowClick = (record) => {
if (record) {
if (record.id) {
search.partsdispatchid = record.id;
history.push({ search: queryString.stringify(search) });
}
} else {
delete search.partsdispatchid;
history.push({ search: queryString.stringify(search) });
}
};
return ( return (
<JobsDetailPliComponent <JobsDetailPliComponent
job={job} job={job}
billsQuery={billsQuery} billsQuery={billsQuery}
handleBillOnRowClick={handleBillOnRowClick} handleBillOnRowClick={handleBillOnRowClick}
handlePartsOrderOnRowClick={handlePartsOrderOnRowClick} handlePartsOrderOnRowClick={handlePartsOrderOnRowClick}
handlePartsDispatchOnRowClick={handlePartsDispatchOnRowClick}
/> />
); );
} }

View File

@@ -1,21 +1,25 @@
import { Divider, Form, Input, Select, Space, Switch, Tooltip } from "antd"; import {
Divider,
Form,
Input,
InputNumber,
Select,
Space,
Switch,
Tooltip,
} from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors"; import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import CABCpvrtCalculator from "../ca-bc-pvrt-calculator/ca-bc-pvrt-calculator.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component"; import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component"; import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component"; import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
import FormRow from "../layout-form-row/layout-form-row.component"; import FormRow from "../layout-form-row/layout-form-row.component";
import JobsDetailRatesLabor from "./jobs-detail-rates.labor.component";
import JobsDetailRatesMaterials from "./jobs-detail-rates.materials.component";
import JobsDetailRatesOther from "./jobs-detail-rates.other.component";
import JobsDetailRatesParts from "./jobs-detail-rates.parts.component"; import JobsDetailRatesParts from "./jobs-detail-rates.parts.component";
import JobsDetailRatesTaxes from "./jobs-detail-rates.taxes.component";
import JobsDetailRatesProfileOVerride from "./jobs-detail-rates.profile-override.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly, jobRO: selectJobReadOnly,
@@ -80,7 +84,14 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
> >
<CurrencyInput disabled={jobRO || bodyshop.cdk_dealerid} /> <CurrencyInput disabled={jobRO || bodyshop.cdk_dealerid} />
</Form.Item> </Form.Item>
{bodyshop.region_config === "CA_BC" && (
<Space align="center">
<Form.Item label={t("jobs.fields.ca_bc_pvrt")} name="ca_bc_pvrt">
<CurrencyInput disabled={jobRO} min={0} />
</Form.Item>
<CABCpvrtCalculator form={form} disabled={jobRO} />
</Space>
)}
<Form.Item <Form.Item
label={t("jobs.fields.auto_add_ats")} label={t("jobs.fields.auto_add_ats")}
name="auto_add_ats" name="auto_add_ats"
@@ -109,7 +120,41 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
}} }}
</Form.Item> </Form.Item>
</FormRow> </FormRow>
<FormRow>
<Form.Item
label={t("jobs.fields.federal_tax_rate")}
name="federal_tax_rate"
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.state_tax_rate")}
name="state_tax_rate"
>
<InputNumber
min={0}
max={1}
precision={2}
disabled={jobRO}
autoComplete="new-password"
/>
</Form.Item>
<Form.Item
label={t("jobs.fields.local_tax_rate")}
name="local_tax_rate"
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
{bodyshop.region_config.toLowerCase().startsWith("ca") && (
<Form.Item
label={t("jobs.fields.ca_gst_registrant")}
name="ca_gst_registrant"
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
)}
</FormRow>
<Divider <Divider
orientation="left" orientation="left"
type="horizontal" type="horizontal"
@@ -197,15 +242,7 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
<CurrencyInput min={0} disabled={jobRO} /> <CurrencyInput min={0} disabled={jobRO} />
</Form.Item> </Form.Item>
</FormRow> </FormRow>
<Divider orientation="left">Tax Profile</Divider>
<JobsDetailRatesProfileOVerride form={form} />
<JobsDetailRatesParts form={form} /> <JobsDetailRatesParts form={form} />
<JobsDetailRatesLabor form={form} />
<JobsDetailRatesMaterials form={form} />
<JobsDetailRatesOther form={form} />
<JobsDetailRatesTaxes form={form} />
</div> </div>
); );
} }

View File

@@ -1,427 +0,0 @@
import { Collapse, Form, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
});
export function JobsDetailRatesLabor({
jobRO,
expanded,
required = true,
form,
}) {
const { t } = useTranslation();
return (
<Collapse defaultActiveKey={expanded && "rates"}>
<Collapse.Panel
forceRender
header={t("jobs.labels.cieca_pfl")}
key="cieca_pfl"
>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAB")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAB", "lbr_tax_in"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAB", "lbr_tx_in1"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
name={["cieca_pfl", "LAB", "lbr_tx_in2"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
name={["cieca_pfl", "LAB", "lbr_tx_in3"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
name={["cieca_pfl", "LAB", "lbr_tx_in4"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
name={["cieca_pfl", "LAB", "lbr_tx_in5"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAD")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAD", "lbr_tax_in"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAD", "lbr_tx_in1"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
name={["cieca_pfl", "LAD", "lbr_tx_in2"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
name={["cieca_pfl", "LAD", "lbr_tx_in3"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
name={["cieca_pfl", "LAD", "lbr_tx_in4"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
name={["cieca_pfl", "LAD", "lbr_tx_in5"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAE")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAE", "lbr_tax_in"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAE", "lbr_tx_in1"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
name={["cieca_pfl", "LAE", "lbr_tx_in2"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
name={["cieca_pfl", "LAE", "lbr_tx_in3"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
name={["cieca_pfl", "LAE", "lbr_tx_in4"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
name={["cieca_pfl", "LAE", "lbr_tx_in5"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAF")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAF", "lbr_tax_in"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAF", "lbr_tx_in1"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
name={["cieca_pfl", "LAF", "lbr_tx_in2"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
name={["cieca_pfl", "LAF", "lbr_tx_in3"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
name={["cieca_pfl", "LAF", "lbr_tx_in4"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
name={["cieca_pfl", "LAF", "lbr_tx_in5"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAG")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAG", "lbr_tax_in"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAG", "lbr_tx_in1"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
name={["cieca_pfl", "LAG", "lbr_tx_in2"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
name={["cieca_pfl", "LAG", "lbr_tx_in3"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
name={["cieca_pfl", "LAG", "lbr_tx_in4"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
name={["cieca_pfl", "LAG", "lbr_tx_in5"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAM")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAM", "lbr_tax_in"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAM", "lbr_tx_in1"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
name={["cieca_pfl", "LAM", "lbr_tx_in2"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
name={["cieca_pfl", "LAM", "lbr_tx_in3"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
name={["cieca_pfl", "LAM", "lbr_tx_in4"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
name={["cieca_pfl", "LAM", "lbr_tx_in5"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAR")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAR", "lbr_tax_in"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAR", "lbr_tx_in1"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
name={["cieca_pfl", "LAR", "lbr_tx_in2"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
name={["cieca_pfl", "LAR", "lbr_tx_in3"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
name={["cieca_pfl", "LAR", "lbr_tx_in4"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
name={["cieca_pfl", "LAR", "lbr_tx_in5"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAS")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAS", "lbr_tax_in"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAS", "lbr_tx_in1"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
name={["cieca_pfl", "LAS", "lbr_tx_in2"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
name={["cieca_pfl", "LAS", "lbr_tx_in3"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
name={["cieca_pfl", "LAS", "lbr_tx_in4"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
name={["cieca_pfl", "LAS", "lbr_tx_in5"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAU")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAU", "lbr_tax_in"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAU", "lbr_tx_in1"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
name={["cieca_pfl", "LAU", "lbr_tx_in2"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
name={["cieca_pfl", "LAU", "lbr_tx_in3"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
name={["cieca_pfl", "LAU", "lbr_tx_in4"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
name={["cieca_pfl", "LAU", "lbr_tx_in5"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
</Collapse.Panel>
</Collapse>
);
}
export default connect(mapStateToProps, null)(JobsDetailRatesLabor);

View File

@@ -1,145 +0,0 @@
import { Collapse, Form, Input, InputNumber, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
});
export function JobsDetailRatesMaterials({
jobRO,
expanded,
required = true,
form,
}) {
const { t } = useTranslation();
return (
<Collapse defaultActiveKey={expanded && "rates"}>
<Collapse.Panel
forceRender
header={t("jobs.fields.materials.materials")}
key="materials"
>
<LayoutFormRow header={t("jobs.fields.materials.MAPA")}>
<Form.Item
label={t("jobs.fields.materials.cal_maxdlr")}
name={["materials", "MAPA", "cal_maxdlr"]}
>
<InputNumber min={0} precision={2} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.cal_opcode")}
name={["materials", "MAPA", "cal_opcode"]}
>
<Input disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.tax_ind")}
name={["materials", "MAPA", "tax_ind"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in1")}
name={["materials", "MAPA", "mat_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in2")}
name={["materials", "MAPA", "mat_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in3")}
name={["materials", "MAPA", "mat_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in4")}
name={["materials", "MAPA", "mat_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in5")}
name={["materials", "MAPA", "mat_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("jobs.fields.materials.MASH")}>
<Form.Item
label={t("jobs.fields.materials.cal_maxdlr")}
name={["materials", "MASH", "cal_maxdlr"]}
>
<InputNumber min={0} precision={2} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.cal_opcode")}
name={["materials", "MASH", "cal_opcode"]}
>
<Input disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.tax_ind")}
name={["materials", "MASH", "tax_ind"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in1")}
name={["materials", "MASH", "mat_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in2")}
name={["materials", "MASH", "mat_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in3")}
name={["materials", "MASH", "mat_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in4")}
name={["materials", "MASH", "mat_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in5")}
name={["materials", "MASH", "mat_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
</Collapse.Panel>
</Collapse>
);
}
export default connect(mapStateToProps, null)(JobsDetailRatesMaterials);

View File

@@ -1,104 +0,0 @@
import { Collapse, Form, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
});
export function JobsDetailRatesOther({
jobRO,
expanded,
required = true,
form,
}) {
const { t } = useTranslation();
return (
<Collapse defaultActiveKey={expanded && "rates"}>
<Collapse.Panel
forceRender
header={t("jobs.labels.cieca_pfo")}
key="cieca_pfo"
>
<LayoutFormRow noDivider>
<Form.Item
label={t("jobs.fields.cieca_pfo.tow_t_in1")}
name={["cieca_pfo", "tow_t_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfo.tow_t_in2")}
name={["cieca_pfo", "tow_t_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfo.tow_t_in3")}
name={["cieca_pfo", "tow_t_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfo.tow_t_in4")}
name={["cieca_pfo", "tow_t_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfo.tow_t_in5")}
name={["cieca_pfo", "tow_t_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfo.stor_t_in1")}
name={["cieca_pfo", "stor_t_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfo.stor_t_in2")}
name={["cieca_pfo", "stor_t_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfo.stor_t_in3")}
name={["cieca_pfo", "stor_t_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfo.stor_t_in4")}
name={["cieca_pfo", "stor_t_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfo.stor_t_in5")}
name={["cieca_pfo", "stor_t_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
</Collapse.Panel>
</Collapse>
);
}
export default connect(mapStateToProps, null)(JobsDetailRatesOther);

View File

@@ -29,7 +29,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_discp")} label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAA", "prt_discp"]} name={["parts_tax_rates", "PAA", "prt_discp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")} label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -42,7 +42,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")} label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAA", "prt_mkupp"]} name={["parts_tax_rates", "PAA", "prt_mkupp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")} label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -68,58 +68,18 @@ export function JobsDetailRatesParts({
}, },
]} ]}
> >
<InputNumber <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item> </Form.Item>
); );
}} }}
</Form.Item> </Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAA", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAA", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAA", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAA", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAA", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAC")}> <LayoutFormRow header={t("joblines.fields.part_types.PAC")}>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")} label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAC", "prt_discp"]} name={["parts_tax_rates", "PAC", "prt_discp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")} label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -132,7 +92,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")} label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAC", "prt_mkupp"]} name={["parts_tax_rates", "PAC", "prt_mkupp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")} label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -158,58 +118,18 @@ export function JobsDetailRatesParts({
}, },
]} ]}
> >
<InputNumber <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item> </Form.Item>
); );
}} }}
</Form.Item> </Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAC", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAC", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAC", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAC", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAC", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAL")}> <LayoutFormRow header={t("joblines.fields.part_types.PAL")}>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")} label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAL", "prt_discp"]} name={["parts_tax_rates", "PAL", "prt_discp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")} label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -222,7 +142,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")} label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAL", "prt_mkupp"]} name={["parts_tax_rates", "PAL", "prt_mkupp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")} label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -248,58 +168,18 @@ export function JobsDetailRatesParts({
}, },
]} ]}
> >
<InputNumber <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item> </Form.Item>
); );
}} }}
</Form.Item> </Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAL", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAL", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAL", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAL", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAL", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAG")}> <LayoutFormRow header={t("joblines.fields.part_types.PAG")}>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")} label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAG", "prt_discp"]} name={["parts_tax_rates", "PAG", "prt_discp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")} label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -312,7 +192,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")} label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAG", "prt_mkupp"]} name={["parts_tax_rates", "PAG", "prt_mkupp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")} label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -338,58 +218,18 @@ export function JobsDetailRatesParts({
}, },
]} ]}
> >
<InputNumber <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item> </Form.Item>
); );
}} }}
</Form.Item> </Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAG", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAG", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAG", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAG", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAG", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAM")}> <LayoutFormRow header={t("joblines.fields.part_types.PAM")}>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")} label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAM", "prt_discp"]} name={["parts_tax_rates", "PAM", "prt_discp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")} label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -402,7 +242,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")} label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAM", "prt_mkupp"]} name={["parts_tax_rates", "PAM", "prt_mkupp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")} label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -428,58 +268,18 @@ export function JobsDetailRatesParts({
}, },
]} ]}
> >
<InputNumber <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item> </Form.Item>
); );
}} }}
</Form.Item> </Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAM", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAM", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAM", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAM", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAM", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAN")}> <LayoutFormRow header={t("joblines.fields.part_types.PAN")}>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")} label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAN", "prt_discp"]} name={["parts_tax_rates", "PAN", "prt_discp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")} label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -492,7 +292,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")} label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAN", "prt_mkupp"]} name={["parts_tax_rates", "PAN", "prt_mkupp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")} label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -518,58 +318,18 @@ export function JobsDetailRatesParts({
}, },
]} ]}
> >
<InputNumber <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item> </Form.Item>
); );
}} }}
</Form.Item> </Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAN", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAN", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAN", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAN", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAN", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAO")}> <LayoutFormRow header={t("joblines.fields.part_types.PAO")}>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")} label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAO", "prt_discp"]} name={["parts_tax_rates", "PAO", "prt_discp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")} label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -582,7 +342,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")} label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAO", "prt_mkupp"]} name={["parts_tax_rates", "PAO", "prt_mkupp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")} label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -608,58 +368,18 @@ export function JobsDetailRatesParts({
}, },
]} ]}
> >
<InputNumber <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item> </Form.Item>
); );
}} }}
</Form.Item> </Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAO", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAO", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAO", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAO", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAO", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAP")}> <LayoutFormRow header={t("joblines.fields.part_types.PAP")}>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")} label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAP", "prt_discp"]} name={["parts_tax_rates", "PAP", "prt_discp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")} label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -672,7 +392,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")} label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAP", "prt_mkupp"]} name={["parts_tax_rates", "PAP", "prt_mkupp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")} label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -698,58 +418,18 @@ export function JobsDetailRatesParts({
}, },
]} ]}
> >
<InputNumber <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item> </Form.Item>
); );
}} }}
</Form.Item> </Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAP", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAP", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAP", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAP", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAP", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAR")}> <LayoutFormRow header={t("joblines.fields.part_types.PAR")}>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")} label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAR", "prt_discp"]} name={["parts_tax_rates", "PAR", "prt_discp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")} label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -762,7 +442,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")} label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAR", "prt_mkupp"]} name={["parts_tax_rates", "PAR", "prt_mkupp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")} label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -788,58 +468,18 @@ export function JobsDetailRatesParts({
}, },
]} ]}
> >
<InputNumber <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item> </Form.Item>
); );
}} }}
</Form.Item> </Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAR", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAR", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAR", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAR", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAR", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAS")}> <LayoutFormRow header={t("joblines.fields.part_types.PAS")}>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")} label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAS", "prt_discp"]} name={["parts_tax_rates", "PAS", "prt_discp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")} label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -852,7 +492,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")} label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAS", "prt_mkupp"]} name={["parts_tax_rates", "PAS", "prt_mkupp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")} label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -878,58 +518,18 @@ export function JobsDetailRatesParts({
}, },
]} ]}
> >
<InputNumber <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item> </Form.Item>
); );
}} }}
</Form.Item> </Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAS", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAS", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAS", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAS", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAS", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PASL")}> <LayoutFormRow header={t("joblines.fields.part_types.PASL")}>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")} label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PASL", "prt_discp"]} name={["parts_tax_rates", "PASL", "prt_discp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")} label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -942,7 +542,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")} label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PASL", "prt_mkupp"]} name={["parts_tax_rates", "PASL", "prt_mkupp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")} label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -968,58 +568,18 @@ export function JobsDetailRatesParts({
}, },
]} ]}
> >
<InputNumber <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item> </Form.Item>
); );
}} }}
</Form.Item> </Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PASL", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PASL", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PASL", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PASL", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PASL", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.CCDR")}> <LayoutFormRow header={t("joblines.fields.part_types.CCDR")}>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")} label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "CCDR", "prt_discp"]} name={["parts_tax_rates", "CCDR", "prt_discp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")} label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -1032,7 +592,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")} label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "CCDR", "prt_mkupp"]} name={["parts_tax_rates", "CCDR", "prt_mkupp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")} label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -1045,7 +605,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")} label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
name={["parts_tax_rates", "CCDR", "prt_tax_rt"]} name={["parts_tax_rates", "CCDR", "prt_tax_rt"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.CCF")}> <LayoutFormRow header={t("joblines.fields.part_types.CCF")}>
@@ -1053,7 +613,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_discp")} label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "CCF", "prt_discp"]} name={["parts_tax_rates", "CCF", "prt_discp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")} label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -1066,7 +626,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")} label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "CCF", "prt_mkupp"]} name={["parts_tax_rates", "CCF", "prt_mkupp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")} label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -1079,7 +639,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")} label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
name={["parts_tax_rates", "CCF", "prt_tax_rt"]} name={["parts_tax_rates", "CCF", "prt_tax_rt"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.CCM")}> <LayoutFormRow header={t("joblines.fields.part_types.CCM")}>
@@ -1087,7 +647,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_discp")} label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "CCM", "prt_discp"]} name={["parts_tax_rates", "CCM", "prt_discp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")} label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -1100,7 +660,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")} label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "CCM", "prt_mkupp"]} name={["parts_tax_rates", "CCM", "prt_mkupp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")} label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -1113,7 +673,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")} label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
name={["parts_tax_rates", "CCM", "prt_tax_rt"]} name={["parts_tax_rates", "CCM", "prt_tax_rt"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.CCC")}> <LayoutFormRow header={t("joblines.fields.part_types.CCC")}>
@@ -1121,7 +681,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_discp")} label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "CCC", "prt_discp"]} name={["parts_tax_rates", "CCC", "prt_discp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")} label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -1134,7 +694,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")} label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "CCC", "prt_mkupp"]} name={["parts_tax_rates", "CCC", "prt_mkupp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")} label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -1147,7 +707,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")} label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
name={["parts_tax_rates", "CCC", "prt_tax_rt"]} name={["parts_tax_rates", "CCC", "prt_tax_rt"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.CCD")}> <LayoutFormRow header={t("joblines.fields.part_types.CCD")}>
@@ -1155,7 +715,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_discp")} label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "CCD", "prt_discp"]} name={["parts_tax_rates", "CCD", "prt_discp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")} label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -1168,7 +728,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")} label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "CCD", "prt_mkupp"]} name={["parts_tax_rates", "CCD", "prt_mkupp"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")} label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -1181,39 +741,39 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")} label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
name={["parts_tax_rates", "CCD", "prt_tax_rt"]} name={["parts_tax_rates", "CCD", "prt_tax_rt"]}
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow> <LayoutFormRow>
<Form.Item label={t("jobs.fields.tax_tow_rt")} name="tax_tow_rt"> <Form.Item label={t("jobs.fields.tax_tow_rt")} name="tax_tow_rt">
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item label={t("jobs.fields.tax_str_rt")} name="tax_str_rt"> <Form.Item label={t("jobs.fields.tax_str_rt")} name="tax_str_rt">
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.tax_paint_mat_rt")} label={t("jobs.fields.tax_paint_mat_rt")}
name="tax_paint_mat_rt" name="tax_paint_mat_rt"
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.tax_shop_mat_rt")} label={t("jobs.fields.tax_shop_mat_rt")}
name="tax_shop_mat_rt" name="tax_shop_mat_rt"
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item label={t("jobs.fields.tax_sub_rt")} name="tax_sub_rt"> <Form.Item label={t("jobs.fields.tax_sub_rt")} name="tax_sub_rt">
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item label={t("jobs.fields.tax_lbr_rt")} name="tax_lbr_rt"> <Form.Item label={t("jobs.fields.tax_lbr_rt")} name="tax_lbr_rt">
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.tax_levies_rt")} label={t("jobs.fields.tax_levies_rt")}
name="tax_levies_rt" name="tax_levies_rt"
> >
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
</Collapse.Panel> </Collapse.Panel>

View File

@@ -1,42 +0,0 @@
import { Button, Popconfirm } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
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
)(JobsDetailRatesProfileOVerride);
export function JobsDetailRatesProfileOVerride({ bodyshop, form }) {
const { t } = useTranslation();
return (
<Popconfirm
onConfirm={() => {
form.setFieldsValue({
cieca_pft: {
...bodyshop.md_responsibility_centers.taxes.tax_ty1,
...bodyshop.md_responsibility_centers.taxes.tax_ty2,
...bodyshop.md_responsibility_centers.taxes.tax_ty3,
...bodyshop.md_responsibility_centers.taxes.tax_ty4,
...bodyshop.md_responsibility_centers.taxes.tax_ty5,
},
materials: bodyshop.md_responsibility_centers.cieca_pfm,
cieca_pfl: bodyshop.md_responsibility_centers.cieca_pfl,
parts_tax_rates: bodyshop.md_responsibility_centers.parts_tax_rates,
});
}}
title={t("jobs.actions.taxprofileoverride_confirm")}
>
<Button type="link">{t("jobs.actions.taxprofileoverride")}</Button>
</Popconfirm>
);
}

View File

@@ -1,155 +0,0 @@
import { Collapse, Divider, Form, Input, InputNumber, Space } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
bodyshop: selectBodyshop,
});
export function JobsDetailRatesTaxes({
jobRO,
expanded,
bodyshop,
required = true,
form,
}) {
const { t } = useTranslation();
const formItems = [];
for (let tyCounter = 1; tyCounter <= 5; tyCounter++) {
const section = [];
section.push(
TaxFormItems({
typeNum: tyCounter,
rootElements: true,
bodyshop,
jobRO,
})
);
for (let iterator = 1; iterator <= 5; iterator++) {
section.push(
TaxFormItems({
typeNum: tyCounter,
typeNumIterator: iterator,
rootElements: false,
jobRO,
})
);
}
formItems.push(Space({ children: section, wrap: true }));
formItems.push(<Divider />);
}
return (
<Collapse defaultActiveKey={expanded && "rates"}>
<Collapse.Panel
forceRender
header={t("jobs.labels.cieca_pft")}
key="cieca_pft"
>
{formItems}
</Collapse.Panel>
</Collapse>
);
}
export default connect(mapStateToProps, null)(JobsDetailRatesTaxes);
function TaxFormItems({
typeNum,
typeNumIterator,
rootElements,
bodyshopjobRO,
jobRO,
}) {
const { t } = useTranslation();
if (rootElements)
return (
<>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_tax_type", {
typeNum,
typeNumIterator,
})}
// rules={[
// {
// required: true,
// //message: t("general.validation.required"),
// },
// ]}
name={["cieca_pft", `tax_type${typeNum}`]}
>
<Input disabled={jobRO} />
</Form.Item>
</>
);
return (
<>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_tax_tier", {
typeNum,
typeNumIterator,
})}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["cieca_pft", `ty${typeNum}_tier${typeNumIterator}`]}
>
<InputNumber precision={0} min={0} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_tax_thres", {
typeNum,
typeNumIterator,
})}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["cieca_pft", `ty${typeNum}_thres${typeNumIterator}`]}
>
<InputNumber min={0} precision={2} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_tax_rate", {
typeNum,
typeNumIterator,
})}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["cieca_pft", `ty${typeNum}_rate${typeNumIterator}`]}
>
<InputNumber min={0} precision={2} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_tax_sur", {
typeNum,
typeNumIterator,
})}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["cieca_pft", `ty${typeNum}_sur${typeNumIterator}`]}
>
<InputNumber min={0} precision={2} disabled={jobRO} />
</Form.Item>
</>
);
}

View File

@@ -1,105 +1,117 @@
import { import {BranchesOutlined, ExclamationCircleFilled, PauseCircleOutlined, SyncOutlined,} from "@ant-design/icons";
SyncOutlined, import {useQuery} from "@apollo/client";
ExclamationCircleFilled, import {Button, Card, Grid, Input, Space, Table, Tooltip, Typography} from "antd";
PauseCircleOutlined,
BranchesOutlined,
} from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd";
import queryString from "query-string"; import queryString from "query-string";
import React, { useState } from "react"; import React, {useEffect, useState} from "react";
import { useTranslation } from "react-i18next"; import {useTranslation} from "react-i18next";
import { connect } from "react-redux"; import {connect} from "react-redux";
import { Link, useHistory, useLocation } from "react-router-dom"; import {Link, useHistory, useLocation} from "react-router-dom";
import { createStructuredSelector } from "reselect"; import {createStructuredSelector} from "reselect";
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries"; import {QUERY_ALL_ACTIVE_JOBS_PAGINATED} from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors"; import {selectBodyshop} from "../../redux/user/user.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 AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import ChatOpenButton from "../chat-open-button/chat-open-button.component"; import ChatOpenButton from "../chat-open-button/chat-open-button.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import {flattenDeep} from "lodash";
import { pageLimit } from '../../utils/config';
import axios from "axios";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
export function JobsList({ bodyshop }) { const mapDispatchToProps = () => ({
const searchParams = queryString.parse(useLocation().search); });
const { selected } = searchParams;
export function JobsList({bodyshop,}) {
const search = queryString.parse(useLocation().search);
const [openSearchResults, setOpenSearchResults] = useState([]);
const [searchLoading, setSearchLoading] = useState(false);
const {page, selected, sortorder, sortcolumn,statusFilters} = search;
const selectedBreakpoint = Object.entries(Grid.useBreakpoint()) const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1]) .filter((screen) => !!screen[1])
.slice(-1)[0]; .slice(-1)[0];
const { loading, error, data, refetch } = useQuery(QUERY_ALL_ACTIVE_JOBS, {
const {loading, error, data, refetch} = useQuery(QUERY_ALL_ACTIVE_JOBS_PAGINATED, {
variables: { variables: {
statuses: bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"], offset: page ? (page - 1) * pageLimit : 0,
limit: pageLimit,
statusFilters: statusFilters ? JSON.parse(statusFilters) : bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
order: [
{
[sortcolumn || "ro_number"]:
sortorder && sortorder !== "false"
? (sortorder === "descend"
? "desc"
: "asc")
: "desc",
},
],
}, },
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
}); });
const total = data?.jobs_aggregate?.aggregate?.count || 0;
const [state, setState] = useState({ const [state, setState] = useState({
sortedInfo: {}, filteredInfo: {text: ""},
filteredInfo: { text: "" },
}); });
const { t } = useTranslation(); const {t} = useTranslation();
const history = useHistory(); const history = useHistory();
const [searchText, setSearchText] = useState("");
if (error) return <AlertComponent message={error.message} type="error" />; const jobs = data?.jobs || [];
const jobs = data
? searchText === ""
? data.jobs
: data.jobs.filter(
(j) =>
(j.ro_number || "")
.toString()
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.ownr_co_nm || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.comments || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.ownr_fn || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.ownr_ln || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
(j.plate_no || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.v_model_desc || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.est_ct_fn || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.est_ct_ln || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.v_make_desc || "")
.toLowerCase()
.includes(searchText.toLowerCase())
)
: [];
const handleTableChange = (pagination, filters, sorter) => { const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); search.page = pagination.current;
search.sortcolumn = sorter.column && sorter.column.key;
search.sortorder = sorter.order;
if (filters.status) {
search.statusFilters = JSON.stringify(flattenDeep(filters.status));
} else {
delete search.statusFilters;
}
history.push({search: queryString.stringify(search)});
}; };
useEffect(() => {
if (search.search && search.search.trim() !== "") {
searchJobs().catch(e => {
console.error('Something went wrong searching for jobs in the job-list component', e);
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (error) return <AlertComponent message={error.message} type="error"/>;
async function searchJobs(value) {
try {
setSearchLoading(true);
const searchData = await axios.post("/search", {
search: value || search.search,
index: "jobs",
});
setOpenSearchResults(searchData.data.hits.hits.map((s) => s._source));
} catch (error) {
console.log("Error while fetching search results", error);
} finally {
setSearchLoading(false);
}
}
const handleOnRowClick = (record) => { const handleOnRowClick = (record) => {
if (record) { if (record) {
if (record.id) { if (record.id) {
history.push({ history.push({
search: queryString.stringify({ search: queryString.stringify({
...searchParams, ...search,
selected: record.id, selected: record.id,
}), }),
}); });
@@ -112,11 +124,9 @@ export function JobsList({ bodyshop }) {
title: t("jobs.fields.ro_number"), title: t("jobs.fields.ro_number"),
dataIndex: "ro_number", dataIndex: "ro_number",
key: "ro_number", key: "ro_number",
sorter: (a, b) => sorter: true,
parseInt((a.ro_number || "0").replace(/\D/g, "")) -
parseInt((b.ro_number || "0").replace(/\D/g, "")),
sortOrder: sortOrder:
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order, sortcolumn === "ro_number" && sortorder,
render: (text, record) => ( render: (text, record) => (
<Link <Link
@@ -126,14 +136,14 @@ export function JobsList({ bodyshop }) {
<Space> <Space>
{record.ro_number || t("general.labels.na")} {record.ro_number || t("general.labels.na")}
{record.production_vars && record.production_vars.alert ? ( {record.production_vars && record.production_vars.alert ? (
<ExclamationCircleFilled className="production-alert" /> <ExclamationCircleFilled className="production-alert"/>
) : null} ) : null}
{record.suspended && ( {record.suspended && (
<PauseCircleOutlined style={{ color: "orangered" }} /> <PauseCircleOutlined style={{color: "orangered"}}/>
)} )}
{record.iouparent && ( {record.iouparent && (
<Tooltip title={t("jobs.labels.iou")}> <Tooltip title={t("jobs.labels.iou")}>
<BranchesOutlined style={{ color: "orangered" }} /> <BranchesOutlined style={{color: "orangered"}}/>
</Tooltip> </Tooltip>
)} )}
</Space> </Space>
@@ -145,22 +155,20 @@ export function JobsList({ bodyshop }) {
dataIndex: "owner", dataIndex: "owner",
key: "owner", key: "owner",
ellipsis: true, ellipsis: true,
responsive: ["md"], responsive: ["md"],
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln), sorter: false,
sortOrder: sortOrder: sortcolumn === "owner" && sortorder,
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
render: (text, record) => { render: (text, record) => {
return record.ownerid ? ( return record.ownerid ? (
<Link <Link
to={"/manage/owners/" + record.ownerid} to={"/manage/owners/" + record.ownerid}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
<OwnerNameDisplay ownerObject={record} /> <OwnerNameDisplay ownerObject={record}/>
</Link> </Link>
) : ( ) : (
<span> <span>
<OwnerNameDisplay ownerObject={record} /> <OwnerNameDisplay ownerObject={record}/>
</span> </span>
); );
}, },
@@ -172,7 +180,7 @@ export function JobsList({ bodyshop }) {
ellipsis: true, ellipsis: true,
responsive: ["md"], responsive: ["md"],
render: (text, record) => ( render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph1} jobid={record.id} /> <ChatOpenButton phone={record.ownr_ph1} jobid={record.id}/>
), ),
}, },
{ {
@@ -182,7 +190,7 @@ export function JobsList({ bodyshop }) {
ellipsis: true, ellipsis: true,
responsive: ["md"], responsive: ["md"],
render: (text, record) => ( render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph2} jobid={record.id} /> <ChatOpenButton phone={record.ownr_ph2} jobid={record.id}/>
), ),
}, },
@@ -191,25 +199,17 @@ export function JobsList({ bodyshop }) {
dataIndex: "status", dataIndex: "status",
key: "status", key: "status",
ellipsis: true, ellipsis: true,
sorter: true,
sorter: (a, b) => alphaSort(a.status, b.status),
sortOrder: sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order, sortcolumn === "status" && sortorder,
filters: filters:bodyshop.md_ro_statuses.statuses.map((s) => {
(jobs && return { text: s, value: [s] };
jobs }),
.map((j) => j.status)
.filter(onlyUnique)
.map((s) => {
return {
text: s || "No Status*",
value: [s],
};
})) ||
[],
onFilter: (value, record) => value.includes(record.status), onFilter: (value, record) => value.includes(record.status),
render: (text, record) => {
return record.status || t("general.labels.na");
},
}, },
{ {
title: t("jobs.fields.vehicle"), title: t("jobs.fields.vehicle"),
dataIndex: "vehicle", dataIndex: "vehicle",
@@ -237,11 +237,10 @@ export function JobsList({ bodyshop }) {
dataIndex: "plate_no", dataIndex: "plate_no",
key: "plate_no", key: "plate_no",
ellipsis: true, ellipsis: true,
responsive: ["md"], responsive: ["md"],
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no), sorter: true,
sortOrder: sortOrder:
state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order, sortcolumn === "plate_no" && sortorder,
}, },
{ {
title: t("jobs.fields.clm_no"), title: t("jobs.fields.clm_no"),
@@ -249,9 +248,9 @@ export function JobsList({ bodyshop }) {
key: "clm_no", key: "clm_no",
ellipsis: true, ellipsis: true,
responsive: ["md"], responsive: ["md"],
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no), sorter: true,
sortOrder: sortOrder:
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order, sortcolumn === "clm_no" && sortorder,
render: (text, record) => render: (text, record) =>
`${record.clm_no || ""}${ `${record.clm_no || ""}${
record.po_number ? ` (PO: ${record.po_number})` : "" record.po_number ? ` (PO: ${record.po_number})` : ""
@@ -262,19 +261,20 @@ export function JobsList({ bodyshop }) {
dataIndex: "ins_co_nm", dataIndex: "ins_co_nm",
key: "ins_co_nm", key: "ins_co_nm",
ellipsis: true, ellipsis: true,
filters: // TODO: Restore Filters?
(jobs && // filters:
jobs // (jobs &&
.map((j) => j.ins_co_nm) // jobs
.filter(onlyUnique) // .map((j) => j.ins_co_nm)
.map((s) => { // .filter(onlyUnique)
return { // .map((s) => {
text: s, // return {
value: [s], // text: s,
}; // value: [s],
})) || // };
[], // })) ||
onFilter: (value, record) => value.includes(record.ins_co_nm), // [],
// onFilter: (value, record) => value.includes(record.ins_co_nm),
responsive: ["md"], responsive: ["md"],
}, },
{ {
@@ -283,10 +283,9 @@ export function JobsList({ bodyshop }) {
key: "clm_total", key: "clm_total",
responsive: ["md"], responsive: ["md"],
ellipsis: true, ellipsis: true,
sorter: true,
sorter: (a, b) => a.clm_total - b.clm_total,
sortOrder: sortOrder:
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order, sortcolumn === "clm_total" && sortorder,
render: (text, record) => ( render: (text, record) => (
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter> <CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
), ),
@@ -297,23 +296,24 @@ export function JobsList({ bodyshop }) {
key: "jobs.labels.estimator", key: "jobs.labels.estimator",
ellipsis: true, ellipsis: true,
responsive: ["xl"], responsive: ["xl"],
filterSearch: true, // TODO Restore Filters?
filters: // filterSearch: true,
(jobs && // filters:
jobs // (jobs &&
.map((j) => `${j.est_ct_fn || ""} ${j.est_ct_ln || ""}`.trim()) // jobs
.filter(onlyUnique) // .map((j) => `${j.est_ct_fn || ""} ${j.est_ct_ln || ""}`.trim())
.map((s) => { // .filter(onlyUnique)
return { // .map((s) => {
text: s || "N/A", // return {
value: [s], // text: s || "N/A",
}; // value: [s],
})) || // };
[], // })) ||
onFilter: (value, record) => // [],
value.includes( // onFilter: (value, record) =>
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim() // value.includes(
), // `${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim()
// ),
render: (text, record) => render: (text, record) =>
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim(), `${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim(),
}, },
@@ -349,26 +349,57 @@ export function JobsList({ bodyshop }) {
title={t("titles.bc.jobs-active")} title={t("titles.bc.jobs-active")}
extra={ extra={
<Space wrap> <Space wrap>
{search.search && (
<>
<Typography.Title level={4}>
{t("general.labels.searchresults", { search: search.search })}
</Typography.Title>
<Button
onClick={() => {
delete search.search;
delete search.page;
history.push({ search: queryString.stringify(search) });
}}
>
{t("general.actions.clear")}
</Button>
</>
)}
<Button onClick={() => refetch()}> <Button onClick={() => refetch()}>
<SyncOutlined /> <SyncOutlined />
</Button> </Button>
<Input.Search <Input.Search
placeholder={t("general.labels.search")} placeholder={search.search || t("general.labels.search")}
onChange={(e) => { onSearch={(value) => {
setSearchText(e.target.value); search.search = value;
history.push({ search: queryString.stringify(search) });
searchJobs(value);
}} }}
value={searchText} loading={loading || searchLoading}
enterButton enterButton
/> />
</Space> </Space>
} }
> >
<Table <Table
loading={loading} loading={loading || searchLoading }
pagination={{ defaultPageSize: 50 }} pagination={
search?.search
? {
pageSize: pageLimit,
showSizeChanger: false,
}
: {
pageSize: pageLimit,
current: parseInt(page || 1),
total: total,
showSizeChanger: false,
}
}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={jobs} dataSource={search?.search ? openSearchResults : jobs}
scroll={{ scroll={{
x: selectedBreakpoint ? scrollMapper[selectedBreakpoint[0]] : "100%", x: selectedBreakpoint ? scrollMapper[selectedBreakpoint[0]] : "100%",
}} }}
@@ -380,9 +411,9 @@ export function JobsList({ bodyshop }) {
type: "radio", type: "radio",
}} }}
onChange={handleTableChange} onChange={handleTableChange}
onRow={(record, rowIndex) => { onRow={(record) => {
return { return {
onClick: (event) => { onClick: () => {
handleOnRowClick(record); handleOnRowClick(record);
}, },
}; };
@@ -392,4 +423,4 @@ export function JobsList({ bodyshop }) {
); );
} }
export default connect(mapStateToProps, null)(JobsList); export default connect(mapStateToProps, mapDispatchToProps)(JobsList);

View File

@@ -1,333 +0,0 @@
import {
Button,
Card,
Col,
Row,
Space,
Table,
Typography,
notification,
} from "antd";
import { SyncOutlined } from "@ant-design/icons";
import axios from "axios";
import _ from "lodash";
import React, { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectTechnician } from "../../redux/tech/tech.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import "./labor-allocations-table.styles.scss";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
technician: selectTechnician,
});
export function PayrollLaborAllocationsTable({
jobId,
joblines,
timetickets,
bodyshop,
adjustments,
technician,
refetch,
}) {
const { t } = useTranslation();
const [totals, setTotals] = useState([]);
const [state, setState] = useState({
sortedInfo: {
columnKey: "cost_center",
field: "cost_center",
order: "ascend",
},
filteredInfo: {},
});
useEffect(() => {
async function CalculateTotals() {
const { data } = await axios.post("/payroll/calculatelabor", {
jobid: jobId,
});
setTotals(data);
}
if (!!joblines && !!timetickets && !!bodyshop) {
CalculateTotals();
}
if (!jobId) setTotals([]);
}, [joblines, timetickets, bodyshop, adjustments, jobId]);
const convertedLines = useMemo(
() => joblines && joblines.filter((j) => j.convertedtolbr),
[joblines]
);
const columns = [
{
title: t("timetickets.fields.employee"),
dataIndex: "employeeid",
key: "employeeid",
render: (text, record) => {
if (record.employeeid === undefined) {
return (
<span style={{ color: "tomato", fontWeight: "bolder" }}>
{t("timetickets.labels.unassigned")}
</span>
);
}
const emp = bodyshop.employees.find((e) => e.id === record.employeeid);
return `${emp?.first_name} ${emp?.last_name}`;
},
},
{
title: t("joblines.fields.mod_lbr_ty"),
dataIndex: "mod_lbr_ty",
key: "mod_lbr_ty",
render: (text, record) =>
record.employeeid === undefined ? (
<span style={{ color: "tomato", fontWeight: "bolder" }}>
{t("timetickets.labels.unassigned")}
</span>
) : (
t(`joblines.fields.lbr_types.${record.mod_lbr_ty?.toUpperCase()}`)
),
},
// {
// title: t("timetickets.fields.rate"),
// dataIndex: "rate",
// key: "rate",
// },
{
title: t("jobs.labels.hrs_total"),
dataIndex: "expectedHours",
key: "expectedHours",
sorter: (a, b) => a.expectedHours - b.expectedHours,
sortOrder:
state.sortedInfo.columnKey === "expectedHours" &&
state.sortedInfo.order,
render: (text, record) => record.expectedHours.toFixed(5),
},
{
title: t("jobs.labels.hrs_claimed"),
dataIndex: "claimedHours",
key: "claimedHours",
sorter: (a, b) => a.claimedHours - b.claimedHours,
sortOrder:
state.sortedInfo.columnKey === "claimedHours" && state.sortedInfo.order,
render: (text, record) =>
record.claimedHours && record.claimedHours.toFixed(5),
},
{
title: t("jobs.labels.difference"),
dataIndex: "difference",
key: "difference",
sorter: (a, b) => a.difference - b.difference,
sortOrder:
state.sortedInfo.columnKey === "difference" && state.sortedInfo.order,
render: (text, record) => {
const difference = _.round(
record.expectedHours - record.claimedHours,
5
);
return (
<strong
style={{
color: difference >= 0 ? "green" : "red",
}}
>
{difference}
</strong>
);
},
},
];
const convertedTableCols = [
{
title: t("joblines.fields.line_desc"),
dataIndex: "line_desc",
key: "line_desc",
ellipsis: true,
},
{
title: t("joblines.fields.op_code_desc"),
dataIndex: "op_code_desc",
key: "op_code_desc",
ellipsis: true,
render: (text, record) =>
`${record.op_code_desc || ""}${
record.alt_partm ? ` ${record.alt_partm}` : ""
}`,
},
{
title: t("joblines.fields.act_price"),
dataIndex: "act_price",
key: "act_price",
ellipsis: true,
render: (text, record) => (
<>
<CurrencyFormatter>
{record.db_ref === "900510" || record.db_ref === "900511"
? record.prt_dsmk_m
: record.act_price}
</CurrencyFormatter>
{record.prt_dsmk_p && record.prt_dsmk_p !== 0 ? (
<span
style={{ marginLeft: ".2rem" }}
>{`(${record.prt_dsmk_p}%)`}</span>
) : (
<></>
)}
</>
),
},
{
title: t("joblines.fields.part_qty"),
dataIndex: "part_qty",
key: "part_qty",
},
{
title: t("joblines.fields.mod_lbr_ty"),
dataIndex: "conv_mod_lbr_ty",
key: "conv_mod_lbr_ty",
render: (text, record) =>
record.convertedtolbr_data && record.convertedtolbr_data.mod_lbr_ty,
},
{
title: t("joblines.fields.mod_lb_hrs"),
dataIndex: "conv_mod_lb_hrs",
key: "conv_mod_lb_hrs",
render: (text, record) =>
record.convertedtolbr_data &&
record.convertedtolbr_data.mod_lb_hrs &&
record.convertedtolbr_data.mod_lb_hrs.toFixed(5),
},
];
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
const summary =
totals &&
totals.reduce(
(acc, val) => {
acc.hrs_total += val.expectedHours;
acc.hrs_claimed += val.claimedHours;
// acc.adjustments += val.adjustments;
acc.difference += val.expectedHours - val.claimedHours;
return acc;
},
{ hrs_total: 0, hrs_claimed: 0, adjustments: 0, difference: 0 }
);
return (
<Row gutter={[16, 16]}>
<Col span={24}>
<Card
title={t("jobs.labels.laborallocations")}
extra={
<Space>
<Button
onClick={async () => {
const response = await axios.post("/payroll/payall", {
jobid: jobId,
});
if (response.status === 200) {
if (response.data.success !== false) {
notification.open({
type: "success",
message: t("timetickets.successes.payall"),
});
} else {
notification.open({
type: "error",
message: t("timetickets.errors.payall", {
error: response.data.error,
}),
});
}
if (refetch) refetch();
} else {
notification.open({
type: "error",
message: t("timetickets.errors.payall", {
error: JSON.stringify(""),
}),
});
}
}}
>
{t("timetickets.actions.payall")}
</Button>
<Button
onClick={async () => {
const { data } = await axios.post("/payroll/calculatelabor", {
jobid: jobId,
});
setTotals(data);
refetch();
}}
>
<SyncOutlined />
</Button>
</Space>
}
>
<Table
columns={columns}
rowKey={(record) => `${record.employeeid} ${record.mod_lbr_ty}`}
pagination={false}
onChange={handleTableChange}
dataSource={totals}
scroll={{
x: true,
}}
summary={() => (
<Table.Summary.Row>
<Table.Summary.Cell>
<Typography.Title level={4}>
{t("general.labels.totals")}
</Typography.Title>
</Table.Summary.Cell>
<Table.Summary.Cell></Table.Summary.Cell>
<Table.Summary.Cell>
{summary.hrs_total.toFixed(5)}
</Table.Summary.Cell>
<Table.Summary.Cell>
{summary.hrs_claimed.toFixed(5)}
</Table.Summary.Cell>
<Table.Summary.Cell>
{summary.difference.toFixed(5)}
</Table.Summary.Cell>
</Table.Summary.Row>
)}
/>
</Card>
</Col>
{convertedLines && convertedLines.length > 0 && (
<Col span={24}>
<Card title={t("jobs.labels.convertedtolabor")}>
<Table
columns={convertedTableCols}
rowKey="id"
pagination={false}
dataSource={convertedLines}
scroll={{
x: true,
}}
/>
</Card>
</Col>
)}
</Row>
);
}
export default connect(mapStateToProps, null)(PayrollLaborAllocationsTable);

View File

@@ -1,81 +0,0 @@
import { useMutation } from "@apollo/client";
import { Button, Card, Col, Row, Table, notification } from "antd";
import moment from "moment-business-days";
import React from "react";
import { useTranslation } from "react-i18next";
import { UPDATE_PARTS_DISPATCH_LINE } from "../../graphql/parts-dispatch.queries";
import { DateTimeFormatter } from "../../utils/DateFormatter";
export default function PartsDispatchExpander({ dispatch, job }) {
const { t } = useTranslation();
const [updateDispatchLine] = useMutation(UPDATE_PARTS_DISPATCH_LINE);
const handleAccept = async ({ partsDispatchLineId }) => {
const accepted_at = moment();
const result = await updateDispatchLine({
variables: { id: partsDispatchLineId, line: { accepted_at } },
optimisticResponse: {
update_parts_dispatch_lines_by_pk: {
accepted_at,
id: partsDispatchLineId,
},
},
});
if (result.errors) {
notification.open({
type: "error",
message: t("parts_dispatch.errors.accepting", {
error: JSON.stringify(result.errors),
}),
});
}
};
const columns = [
{
title: t("joblines.fields.part_qty"),
dataIndex: "quantity",
key: "quantity",
width: "10%",
//sorter: (a, b) => alphaSort(a.number, b.number),
},
{
title: t("joblines.fields.line_desc"),
dataIndex: "joblineid",
key: "joblineid",
//sorter: (a, b) => alphaSort(a.number, b.number),
render: (text, record) => record.jobline.line_desc,
},
{
title: t("parts_dispatch_lines.fields.accepted_at"),
dataIndex: "accepted_at",
key: "accepted_at",
width: "20%",
//sorter: (a, b) => alphaSort(a.number, b.number),
render: (text, record) =>
record.accepted_at ? (
<DateTimeFormatter>{record.accepted_at}</DateTimeFormatter>
) : (
<Button
onClick={() => handleAccept({ partsDispatchLineId: record.id })}
>
{t("parts_dispatch.actions.accept")}
</Button>
),
},
];
return (
<Card>
<Row gutter={[16, 16]}>
<Col span={24}>
<Table
rowKey={"id"}
dataSource={dispatch.parts_dispatch_lines}
columns={columns}
/>
</Col>
</Row>
</Card>
);
}

View File

@@ -1,155 +0,0 @@
import {
MinusCircleTwoTone,
PlusCircleTwoTone,
SyncOutlined,
} from "@ant-design/icons";
import { Button, Card, Input, Space, Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { TemplateList } from "../../utils/TemplateConstants";
import { alphaSort } from "../../utils/sorters";
import PartsDispatchExpander from "../parts-dispatch-expander/parts-dispatch-expander.component";
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({});
export function PartDispatchTableComponent({
bodyshop,
jobRO,
job,
billsQuery,
handleOnRowClick,
}) {
const { t } = useTranslation();
const [state, setState] = useState({
sortedInfo: {},
});
// const search = queryString.parse(useLocation().search);
// const selectedBill = search.billid;
const [searchText, setSearchText] = useState("");
const Templates = TemplateList("job_special");
const { refetch } = billsQuery;
const recordActions = (record) => (
<Space wrap>
<PrintWrapperComponent
templateObject={{
name: Templates.parts_dispatch.key,
variables: { id: record.id },
}}
/>
</Space>
);
const columns = [
{
title: t("parts_dispatch.fields.number"),
dataIndex: "number",
key: "number",
sorter: (a, b) => alphaSort(a.number, b.number),
width: "10%",
sortOrder:
state.sortedInfo.columnKey === "number" && state.sortedInfo.order,
},
{
title: t("timetickets.fields.employee"),
dataIndex: "employeeid",
key: "employeeid",
sorter: (a, b) => alphaSort(a.employeeid, b.employeeid),
sortOrder:
state.sortedInfo.columnKey === "employeeid" && state.sortedInfo.order,
render: (text, record) => {
const e = bodyshop.employees.find((e) => e.id === record.employeeid);
return `${e?.first_name || ""} ${e?.last_name || ""}`.trim();
},
},
{
title: t("parts_dispatch.fields.percent_accepted"),
dataIndex: "percent_accepted",
key: "percent_accepted",
render: (text, record) =>
record.parts_dispatch_lines.length > 0
? `
${(
(record.parts_dispatch_lines.filter((l) => l.accepted_at)
.length /
record.parts_dispatch_lines.length) *
100
).toFixed(0)}%`
: "0%",
},
{
title: t("general.labels.actions"),
dataIndex: "actions",
key: "actions",
width: "10%",
render: (text, record) => recordActions(record, true),
},
];
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
return (
<Card
title={t("parts_dispatch.labels.parts_dispatch")}
extra={
<Space wrap>
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
<Input.Search
placeholder={t("general.labels.search")}
value={searchText}
onChange={(e) => {
e.preventDefault();
setSearchText(e.target.value);
}}
/>
</Space>
}
>
<Table
loading={billsQuery.loading}
scroll={{
x: true, // y: "50rem"
}}
expandable={{
expandedRowRender: (record) => (
<PartsDispatchExpander dispatch={record} job={job} />
),
rowExpandable: (record) => true,
expandIcon: ({ expanded, onExpand, record }) =>
expanded ? (
<MinusCircleTwoTone onClick={(e) => onExpand(record, e)} />
) : (
<PlusCircleTwoTone onClick={(e) => onExpand(record, e)} />
),
}}
columns={columns}
rowKey="id"
dataSource={billsQuery.data ? billsQuery.data.parts_dispatch : []}
onChange={handleTableChange}
/>
</Card>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(PartDispatchTableComponent);

View File

@@ -1,4 +1,3 @@
import { useTreatments } from "@splitsoftware/splitio-react";
import { Card, Col, Input, Row, Space, Typography } from "antd"; import { Card, Col, Input, Row, Space, Typography } from "antd";
import _ from "lodash"; import _ from "lodash";
import React, { useState } from "react"; import React, { useState } from "react";
@@ -24,13 +23,8 @@ export function PrintCenterJobsComponent({ printCenterModal, bodyshop }) {
const { id: jobId, job } = printCenterModal.context; const { id: jobId, job } = printCenterModal.context;
const tempList = TemplateList("job", {}); const tempList = TemplateList("job", {});
const { t } = useTranslation(); const { t } = useTranslation();
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
const Templates = const JobsReportsList =
bodyshop.cdk_dealerid === null && bodyshop.pbs_serialnumber === null bodyshop.cdk_dealerid === null && bodyshop.pbs_serialnumber === null
? Object.keys(tempList) ? Object.keys(tempList)
.map((key) => { .map((key) => {
@@ -57,26 +51,7 @@ export function PrintCenterJobsComponent({ printCenterModal, bodyshop }) {
bodyshop.region_config.includes(Object.keys(temp.regions)) === bodyshop.region_config.includes(Object.keys(temp.regions)) ===
true) true)
); );
const JobsReportsList =
Enhanced_Payroll.treatment === "on"
? Object.keys(Templates)
.map((key) => {
return Templates[key];
})
.filter(
(temp) =>
temp.enhanced_payroll === undefined ||
temp.enhanced_payroll === true
)
: Object.keys(Templates)
.map((key) => {
return Templates[key];
})
.filter(
(temp) =>
temp.enhanced_payroll === undefined ||
temp.enhanced_payroll === false
);
const filteredJobsReportsList = const filteredJobsReportsList =
search !== "" search !== ""
? JobsReportsList.filter((r) => ? JobsReportsList.filter((r) =>

View File

@@ -9,11 +9,9 @@ export default function PrintWrapperComponent({
children, children,
id, id,
emailOnly = false, emailOnly = false,
disabled,
}) { }) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const handlePrint = async (type) => { const handlePrint = async (type) => {
if (disabled) return;
setLoading(true); setLoading(true);
await GenerateDocument(templateObject, messageObject, type, id); await GenerateDocument(templateObject, messageObject, type, id);
setLoading(false); setLoading(false);
@@ -22,18 +20,8 @@ export default function PrintWrapperComponent({
return ( return (
<Space> <Space>
{children || null} {children || null}
{!emailOnly && ( {!emailOnly && <PrinterFilled onClick={() => handlePrint("p")} />}
<PrinterFilled <MailFilled onClick={() => handlePrint("e")} />
disabled={disabled}
onClick={() => handlePrint("p")}
style={{ cursor: disabled ? "not-allowed" : null }}
/>
)}
<MailFilled
disabled={disabled}
onClick={() => handlePrint("e")}
style={{ cursor: disabled ? "not-allowed" : null }}
/>
{loading && <Spin />} {loading && <Spin />}
</Space> </Space>
); );

View File

@@ -24,8 +24,6 @@ import ProductionListColumnNote from "./production-list-columns.productionnote.c
import ProductionListColumnCategory from "./production-list-columns.status.category"; import ProductionListColumnCategory from "./production-list-columns.status.category";
import ProductionListColumnStatus from "./production-list-columns.status.component"; import ProductionListColumnStatus from "./production-list-columns.status.component";
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component"; import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
import { store } from "../../redux/store";
import { setModalContext } from "../../redux/modals/modals.actions";
const r = ({ technician, state, activeStatuses, bodyshop }) => { const r = ({ technician, state, activeStatuses, bodyshop }) => {
return [ return [
@@ -40,29 +38,6 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
</Link> </Link>
), ),
}, },
{
title: i18n.t("timetickets.actions.claimtasks"),
dataIndex: "claimtasks",
key: "claimtasks",
ellipsis: true,
render: (text, record) => (
<div
onClick={() => {
store.dispatch(
setModalContext({
context: {
actions: {},
context: { jobid: record.id },
},
modal: "timeTicketTask",
})
);
}}
>
{i18n.t("timetickets.actions.claimtasks")}
</div>
),
},
{ {
title: i18n.t("jobs.fields.ro_number"), title: i18n.t("jobs.fields.ro_number"),
dataIndex: "ro_number", dataIndex: "ro_number",

View File

@@ -95,7 +95,7 @@ export function ProductionListDetail({
/> />
} }
placement="right" placement="right"
width={"50%"} width={"33%"}
onClose={handleClose} onClose={handleClose}
visible={selected} visible={selected}
> >

View File

@@ -1,5 +1,4 @@
import { useLazyQuery } from "@apollo/client"; import { useLazyQuery } from "@apollo/client";
import { useTreatments } from "@splitsoftware/splitio-react";
import { import {
Button, Button,
Card, Card,
@@ -20,7 +19,6 @@ import { createStructuredSelector } from "reselect";
import { QUERY_ACTIVE_EMPLOYEES } from "../../graphql/employees.queries"; import { QUERY_ACTIVE_EMPLOYEES } from "../../graphql/employees.queries";
import { QUERY_ALL_VENDORS } from "../../graphql/vendors.queries"; import { QUERY_ALL_VENDORS } from "../../graphql/vendors.queries";
import { selectReportCenter } from "../../redux/modals/modals.selectors"; import { selectReportCenter } from "../../redux/modals/modals.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import DatePIckerRanges from "../../utils/DatePickerRanges"; import DatePIckerRanges from "../../utils/DatePickerRanges";
import { GenerateDocument } from "../../utils/RenderTemplate"; import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
@@ -29,7 +27,6 @@ import VendorSearchSelect from "../vendor-search-select/vendor-search-select.com
import "./report-center-modal.styles.scss"; import "./report-center-modal.styles.scss";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
reportCenterModal: selectReportCenter, reportCenterModal: selectReportCenter,
bodyshop: selectBodyshop,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language)) //setUserLanguage: language => dispatch(setUserLanguage(language))
@@ -39,38 +36,16 @@ export default connect(
mapDispatchToProps mapDispatchToProps
)(ReportCenterModalComponent); )(ReportCenterModalComponent);
export function ReportCenterModalComponent({ reportCenterModal, bodyshop }) { export function ReportCenterModalComponent({ reportCenterModal }) {
const [form] = Form.useForm(); const [form] = Form.useForm();
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const { t } = useTranslation(); const { t } = useTranslation();
const Templates = TemplateList("report_center"); const Templates = TemplateList("report_center");
const ReportsList = const ReportsList = Object.keys(Templates).map((key) => {
Enhanced_Payroll.treatment === "on" return Templates[key];
? Object.keys(Templates) });
.map((key) => {
return Templates[key];
})
.filter(
(temp) =>
temp.enhanced_payroll === undefined ||
temp.enhanced_payroll === true
)
: Object.keys(Templates)
.map((key) => {
return Templates[key];
})
.filter(
(temp) =>
temp.enhanced_payroll === undefined ||
temp.enhanced_payroll === false
);
const { visible } = reportCenterModal; const { visible } = reportCenterModal;
const [callVendorQuery, { data: vendorData, called: vendorCalled }] = const [callVendorQuery, { data: vendorData, called: vendorCalled }] =

View File

@@ -11,7 +11,7 @@ import {
Switch, Switch,
Table, Table,
} from "antd"; } from "antd";
import { useForm } from "antd/es/form/Form";
import moment from "moment"; import moment from "moment";
import querystring from "query-string"; import querystring from "query-string";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
@@ -36,7 +36,6 @@ import FormDatePicker from "../form-date-picker/form-date-picker.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 ShopEmployeeAddVacation from "./shop-employees-add-vacation.component"; import ShopEmployeeAddVacation from "./shop-employees-add-vacation.component";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -47,7 +46,7 @@ const mapDispatchToProps = (dispatch) => ({
export function ShopEmployeesFormComponent({ bodyshop }) { export function ShopEmployeesFormComponent({ bodyshop }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [form] = Form.useForm(); const [form] = useForm();
const history = useHistory(); const history = useHistory();
const search = querystring.parse(useLocation().search); const search = querystring.parse(useLocation().search);
const [deleteVacation] = useMutation(DELETE_VACATION); const [deleteVacation] = useMutation(DELETE_VACATION);
@@ -57,11 +56,7 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
}); });
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
const client = useApolloClient(); const client = useApolloClient();
useEffect(() => { useEffect(() => {
if (data && data.employees_by_pk) form.setFieldsValue(data.employees_by_pk); if (data && data.employees_by_pk) form.setFieldsValue(data.employees_by_pk);
@@ -367,7 +362,7 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
{t("timetickets.labels.shift")} {t("timetickets.labels.shift")}
</Select.Option> </Select.Option>
{bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || Enhanced_Payroll.treatment === "on" {bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
? CiecaSelect(false, true) ? CiecaSelect(false, true)
: bodyshop.md_responsibility_centers.costs.map( : bodyshop.md_responsibility_centers.costs.map(
(c) => ( (c) => (

View File

@@ -15,7 +15,6 @@ import ShopInfoResponsibilityCenterComponent from "./shop-info.responsibilitycen
import ShopInfoROStatusComponent from "./shop-info.rostatus.component"; import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
import ShopInfoSchedulingComponent from "./shop-info.scheduling.component"; import ShopInfoSchedulingComponent from "./shop-info.scheduling.component";
import ShopInfoSpeedPrint from "./shop-info.speedprint.component"; import ShopInfoSpeedPrint from "./shop-info.speedprint.component";
import ShopInfoTaskPresets from "./shop-info.task-presets.component";
import { useHistory, useLocation } from "react-router-dom"; import { useHistory, useLocation } from "react-router-dom";
import queryString from "query-string"; import queryString from "query-string";
@@ -97,12 +96,6 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
<ShopInfoPartsScan form={form} /> <ShopInfoPartsScan form={form} />
</Tabs.TabPane> </Tabs.TabPane>
)} )}
<Tabs.TabPane
key="task-presets"
tab={t("bodyshop.labels.task-presets")}
>
<ShopInfoTaskPresets form={form} />
</Tabs.TabPane>
</Tabs> </Tabs>
</Card> </Card>
); );

View File

@@ -189,22 +189,20 @@ export function ShopInfoGeneral({ form, bodyshop }) {
<Switch /> <Switch />
</Form.Item> </Form.Item>
{ <Form.Item shouldUpdate noStyle>
// <Form.Item shouldUpdate noStyle> {() => (
// {() => ( <Form.Item
// <Form.Item label={t("bodyshop.labels.qbo_usa")}
// label={t("bodyshop.labels.qbo_usa")} shouldUpdate
// shouldUpdate valuePropName="checked"
// valuePropName="checked" name={["accountingconfig", "qbo_usa"]}
// name={["accountingconfig", "qbo_usa"]} >
// > <Switch
// <Switch disabled={!form.getFieldValue(["accountingconfig", "qbo"])}
// disabled={!form.getFieldValue(["accountingconfig", "qbo"])} />
// /> </Form.Item>
// </Form.Item> )}
// )} </Form.Item>
// </Form.Item>
}
<Form.Item <Form.Item
label={t("bodyshop.labels.qbo_departmentid")} label={t("bodyshop.labels.qbo_departmentid")}
name={["accountingconfig", "qbo_departmentid"]} name={["accountingconfig", "qbo_departmentid"]}
@@ -293,34 +291,36 @@ export function ShopInfoGeneral({ form, bodyshop }) {
> >
<InputNumber min={0} precision={2} /> <InputNumber min={0} precision={2} />
</Form.Item> </Form.Item>
{ <Form.Item
// <Form.Item label={t("bodyshop.fields.federal_tax_id")}
// label={t("bodyshop.fields.federal_tax_id")} name="federal_tax_id"
// name="federal_tax_id" rules={[
// > {
// <Input /> required: true,
// </Form.Item> //message: t("general.validation.required"),
} },
]}
>
<Input />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.state_tax_id")} label={t("bodyshop.fields.state_tax_id")}
name="state_tax_id" name="state_tax_id"
> >
<Input /> <Input />
</Form.Item> </Form.Item>
{ <Form.Item
// <Form.Item label={t("bodyshop.fields.invoice_federal_tax_rate")}
// label={t("bodyshop.fields.invoice_federal_tax_rate")} name={["bill_tax_rates", "federal_tax_rate"]}
// name={["bill_tax_rates", "federal_tax_rate"]} rules={[
// rules={[ {
// { required: true,
// required: true, //message: t("general.validation.required"),
// //message: t("general.validation.required"), },
// }, ]}
// ]} >
// > <InputNumber />
// <InputNumber /> </Form.Item>
// </Form.Item>
}
<Form.Item <Form.Item
label={t("bodyshop.fields.invoice_state_tax_rate")} label={t("bodyshop.fields.invoice_state_tax_rate")}
name={["bill_tax_rates", "state_tax_rate"]} name={["bill_tax_rates", "state_tax_rate"]}

View File

@@ -400,18 +400,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.employee_teams.page")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "employee_teams:page"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.owners.list")} label={t("bodyshop.fields.rbac.owners.list")}
rules={[ rules={[
@@ -544,42 +532,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.editcommitted")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "timetickets:editcommitted"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.ttapprovals.view")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "ttapprovals:view"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.ttapprovals.approve")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "ttapprovals:approve"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.shop.vendors")} label={t("bodyshop.fields.rbac.shop.vendors")}
rules={[ rules={[

View File

@@ -17,7 +17,6 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { useTreatments } from "@splitsoftware/splitio-react"; import { useTreatments } from "@splitsoftware/splitio-react";
import ShopInfoResponsibilitycentersTaxesComponent from "./shop-info.responsibilitycenters.taxes.component";
const SelectorDiv = styled.div` const SelectorDiv = styled.div`
.ant-form-item .ant-select { .ant-form-item .ant-select {
@@ -4117,124 +4116,122 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</LayoutFormRow> </LayoutFormRow>
</SelectorDiv> </SelectorDiv>
{ <LayoutFormRow
// <LayoutFormRow header={t("bodyshop.labels.responsibilitycenters.tax_accounts")}
// header={t("bodyshop.labels.responsibilitycenters.tax_accounts")} id="tax_accounts"
// id="tax_accounts" >
// > <Form.Item
// <Form.Item label={t("bodyshop.fields.responsibilitycenters.federal_tax")}
// label={t("bodyshop.fields.responsibilitycenters.federal_tax")} rules={[
// rules={[ {
// { required: true,
// required: true, //message: t("general.validation.required"),
// //message: t("general.validation.required"), },
// }, ]}
// ]} name={["md_responsibility_centers", "taxes", "federal", "name"]}
// name={["md_responsibility_centers", "taxes", "federal", "name"]} >
// > <Input />
// <Input /> </Form.Item>
// </Form.Item> {/* <Form.Item
// {/* <Form.Item label={t("bodyshop.fields.responsibilitycenter_accountnumber")}
// label={t("bodyshop.fields.responsibilitycenter_accountnumber")} rules={[
// rules={[ {
// { required: true,
// required: true, //message: t("general.validation.required"),
// //message: t("general.validation.required"), },
// }, ]}
// ]} name={[
// name={[ "md_responsibility_centers",
// "md_responsibility_centers", "taxes",
// "taxes", "federal",
// "federal", "accountnumber",
// "accountnumber", ]}
// ]} >
// > <Input />
// <Input /> </Form.Item> */}
// </Form.Item> */} {/* <Form.Item
// {/* <Form.Item label={t("bodyshop.fields.responsibilitycenter_accountname")}
// label={t("bodyshop.fields.responsibilitycenter_accountname")} rules={[
// rules={[ {
// { required: true,
// required: true, //message: t("general.validation.required"),
// //message: t("general.validation.required"), },
// }, ]}
// ]} name={[
// name={[ "md_responsibility_centers",
// "md_responsibility_centers", "taxes",
// "taxes", "federal",
// "federal", "accountname",
// "accountname", ]}
// ]} >
// > <Input />
// <Input /> </Form.Item> */}
// </Form.Item> */} <Form.Item
// <Form.Item label={t("bodyshop.fields.responsibilitycenter_accountdesc")}
// label={t("bodyshop.fields.responsibilitycenter_accountdesc")} rules={[
// rules={[ {
// { required: true,
// required: true, //message: t("general.validation.required"),
// //message: t("general.validation.required"), },
// }, ]}
// ]} name={[
// name={[ "md_responsibility_centers",
// "md_responsibility_centers", "taxes",
// "taxes", "federal",
// "federal", "accountdesc",
// "accountdesc", ]}
// ]} >
// > <Input />
// <Input /> </Form.Item>
// </Form.Item> <Form.Item
// <Form.Item label={t("bodyshop.fields.responsibilitycenter_accountitem")}
// label={t("bodyshop.fields.responsibilitycenter_accountitem")} rules={[
// rules={[ {
// { required: true,
// required: true, //message: t("general.validation.required"),
// //message: t("general.validation.required"), },
// }, ]}
// ]} name={[
// name={[ "md_responsibility_centers",
// "md_responsibility_centers", "taxes",
// "taxes", "federal",
// "federal", "accountitem",
// "accountitem", ]}
// ]} >
// > <Input />
// <Input /> </Form.Item>
// </Form.Item> {(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
// {(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && ( <Form.Item
// <Form.Item label={t("bodyshop.fields.dms.dms_acctnumber")}
// label={t("bodyshop.fields.dms.dms_acctnumber")} rules={[
// rules={[ {
// { required: true,
// required: true, //message: t("general.validation.required"),
// //message: t("general.validation.required"), },
// }, ]}
// ]} name={[
// name={[ "md_responsibility_centers",
// "md_responsibility_centers", "taxes",
// "taxes", "federal",
// "federal", "dms_acctnumber",
// "dms_acctnumber", ]}
// ]} >
// > <Input />
// <Input /> </Form.Item>
// </Form.Item> )}
// )} <Form.Item
// <Form.Item label={t("bodyshop.fields.responsibilitycenter_rate")}
// label={t("bodyshop.fields.responsibilitycenter_rate")} rules={[
// rules={[ {
// { required: true,
// required: true, //message: t("general.validation.required"),
// //message: t("general.validation.required"), },
// }, ]}
// ]} name={["md_responsibility_centers", "taxes", "federal", "rate"]}
// name={["md_responsibility_centers", "taxes", "federal", "rate"]} >
// > <InputNumber precision={2} />
// <InputNumber precision={2} /> </Form.Item>
// </Form.Item> </LayoutFormRow>
// </LayoutFormRow>
}
{DmsAp.treatment === "on" && ( {DmsAp.treatment === "on" && (
<LayoutFormRow id="federal_tax_itc"> <LayoutFormRow id="federal_tax_itc">
<Form.Item <Form.Item
@@ -4350,242 +4347,202 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
)} )}
{ <LayoutFormRow id="state_tax">
// <LayoutFormRow id="state_tax"> <Form.Item
// <Form.Item label={t("bodyshop.fields.responsibilitycenters.state_tax")}
// label={t("bodyshop.fields.responsibilitycenters.state_tax")} rules={[
// rules={[ {
// { required: true,
// required: true, //message: t("general.validation.required"),
// //message: t("general.validation.required"), },
// }, ]}
// ]} name={["md_responsibility_centers", "taxes", "state", "name"]}
// name={["md_responsibility_centers", "taxes", "state", "name"]} >
// > <Input />
// <Input /> </Form.Item>
// </Form.Item> {/* <Form.Item
// {/* <Form.Item label={t("bodyshop.fields.responsibilitycenter_accountnumber")}
// label={t("bodyshop.fields.responsibilitycenter_accountnumber")} rules={[
// rules={[ {
// { required: true,
// required: true, //message: t("general.validation.required"),
// //message: t("general.validation.required"), },
// }, ]}
// ]} name={[
// name={[ "md_responsibility_centers",
// "md_responsibility_centers", "taxes",
// "taxes", "state",
// "state", "accountnumber",
// "accountnumber", ]}
// ]} >
// > <Input />
// <Input /> </Form.Item>
// </Form.Item> <Form.Item
// <Form.Item label={t("bodyshop.fields.responsibilitycenter_accountname")}
// label={t("bodyshop.fields.responsibilitycenter_accountname")} rules={[
// rules={[ {
// { required: true,
// required: true, //message: t("general.validation.required"),
// //message: t("general.validation.required"), },
// }, ]}
// ]} name={["md_responsibility_centers", "taxes", "state", "accountname"]}
// name={["md_responsibility_centers", "taxes", "state", "accountname"]} >
// > <Input />
// <Input /> </Form.Item> */}
// </Form.Item> */} <Form.Item
// <Form.Item label={t("bodyshop.fields.responsibilitycenter_accountdesc")}
// label={t("bodyshop.fields.responsibilitycenter_accountdesc")} rules={[
// rules={[ {
// { required: true,
// required: true, //message: t("general.validation.required"),
// //message: t("general.validation.required"), },
// }, ]}
// ]} name={["md_responsibility_centers", "taxes", "state", "accountdesc"]}
// name={["md_responsibility_centers", "taxes", "state", "accountdesc"]} >
// > <Input />
// <Input /> </Form.Item>
// </Form.Item> <Form.Item
// <Form.Item label={t("bodyshop.fields.responsibilitycenter_accountitem")}
// label={t("bodyshop.fields.responsibilitycenter_accountitem")} rules={[
// rules={[ {
// { required: true,
// required: true, //message: t("general.validation.required"),
// //message: t("general.validation.required"), },
// }, ]}
// ]} name={["md_responsibility_centers", "taxes", "state", "accountitem"]}
// name={["md_responsibility_centers", "taxes", "state", "accountitem"]} >
// > <Input />
// <Input /> </Form.Item>
// </Form.Item> {(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
// {(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && ( <Form.Item
// <Form.Item label={t("bodyshop.fields.dms.dms_acctnumber")}
// label={t("bodyshop.fields.dms.dms_acctnumber")} rules={[
// rules={[ {
// { required: true,
// required: true, //message: t("general.validation.required"),
// //message: t("general.validation.required"), },
// }, ]}
// ]} name={[
// name={[ "md_responsibility_centers",
// "md_responsibility_centers", "taxes",
// "taxes", "state",
// "state", "dms_acctnumber",
// "dms_acctnumber", ]}
// ]} >
// > <Input />
// <Input /> </Form.Item>
// </Form.Item> )}
// )} <Form.Item
// <Form.Item label={t("bodyshop.fields.responsibilitycenter_rate")}
// label={t("bodyshop.fields.responsibilitycenter_rate")} rules={[
// rules={[ {
// { required: true,
// required: true, //message: t("general.validation.required"),
// //message: t("general.validation.required"), },
// }, ]}
// ]} name={["md_responsibility_centers", "taxes", "state", "rate"]}
// name={["md_responsibility_centers", "taxes", "state", "rate"]} >
// > <InputNumber precision={2} />
// <InputNumber precision={2} /> </Form.Item>
// </Form.Item> </LayoutFormRow>
// </LayoutFormRow> <LayoutFormRow id="local_tax">
} <Form.Item
<ShopInfoResponsibilitycentersTaxesComponent form={form} /> label={t("bodyshop.fields.responsibilitycenters.local_tax")}
<Form.Item rules={[
label={t("bodyshop.fields.responsibilitycenters.itemexemptcode")} {
rules={[ required: true,
{ //message: t("general.validation.required"),
required: true, },
//message: t("general.validation.required"), ]}
}, name={["md_responsibility_centers", "taxes", "local", "name"]}
]} >
name={["md_responsibility_centers", "taxes", "itemexemptcode"]} <Input />
> </Form.Item>
<Input /> {/* <Form.Item
</Form.Item> label={t("bodyshop.fields.responsibilitycenter_accountnumber")}
<Form.Item rules={[
label={t("bodyshop.fields.responsibilitycenters.invoiceexemptcode")} {
rules={[ required: true,
{ //message: t("general.validation.required"),
required: true, },
//message: t("general.validation.required"), ]}
}, name={[
]} "md_responsibility_centers",
name={["md_responsibility_centers", "taxes", "invoiceexemptcode"]} "taxes",
> "local",
<Input /> "accountnumber",
</Form.Item> ]}
>
{ <Input />
// <LayoutFormRow id="local_tax"> </Form.Item>
// <Form.Item <Form.Item
// label={t("bodyshop.fields.responsibilitycenters.local_tax")} label={t("bodyshop.fields.responsibilitycenter_accountname")}
// rules={[ rules={[
// { {
// required: true, required: true,
// //message: t("general.validation.required"), //message: t("general.validation.required"),
// }, },
// ]} ]}
// name={["md_responsibility_centers", "taxes", "local", "name"]} name={["md_responsibility_centers", "taxes", "local", "accountname"]}
// > >
// <Input /> <Input />
// </Form.Item> </Form.Item> */}
// {/* <Form.Item <Form.Item
// label={t("bodyshop.fields.responsibilitycenter_accountnumber")} label={t("bodyshop.fields.responsibilitycenter_accountdesc")}
// rules={[ rules={[
// { {
// required: true, required: true,
// //message: t("general.validation.required"), //message: t("general.validation.required"),
// }, },
// ]} ]}
// name={[ name={["md_responsibility_centers", "taxes", "local", "accountdesc"]}
// "md_responsibility_centers", >
// "taxes", <Input />
// "local", </Form.Item>
// "accountnumber", <Form.Item
// ]} label={t("bodyshop.fields.responsibilitycenter_accountitem")}
// > rules={[
// <Input /> {
// </Form.Item> required: true,
// <Form.Item //message: t("general.validation.required"),
// label={t("bodyshop.fields.responsibilitycenter_accountname")} },
// rules={[ ]}
// { name={["md_responsibility_centers", "taxes", "local", "accountitem"]}
// required: true, >
// //message: t("general.validation.required"), <Input />
// }, </Form.Item>
// ]} {(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
// name={["md_responsibility_centers", "taxes", "local", "accountname"]} <Form.Item
// > label={t("bodyshop.fields.dms.dms_acctnumber")}
// <Input /> rules={[
// </Form.Item> */} {
// <Form.Item required: true,
// label={t("bodyshop.fields.responsibilitycenter_accountdesc")} //message: t("general.validation.required"),
// rules={[ },
// { ]}
// required: true, name={[
// //message: t("general.validation.required"), "md_responsibility_centers",
// }, "taxes",
// ]} "local",
// name={[ "dms_acctnumber",
// "md_responsibility_centers", ]}
// "taxes", >
// "local", <Input />
// "accountdesc", </Form.Item>
// ]} )}
// > <Form.Item
// <Input /> label={t("bodyshop.fields.responsibilitycenter_rate")}
// </Form.Item> rules={[
// <Form.Item {
// label={t("bodyshop.fields.responsibilitycenter_accountitem")} required: true,
// rules={[ //message: t("general.validation.required"),
// { },
// required: true, ]}
// //message: t("general.validation.required"), name={["md_responsibility_centers", "taxes", "local", "rate"]}
// }, >
// ]} <InputNumber precision={2} />
// name={[ </Form.Item>
// "md_responsibility_centers", </LayoutFormRow>
// "taxes",
// "local",
// "accountitem",
// ]}
// >
// <Input />
// </Form.Item>
// {(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
// <Form.Item
// label={t("bodyshop.fields.dms.dms_acctnumber")}
// rules={[
// {
// required: true,
// //message: t("general.validation.required"),
// },
// ]}
// name={[
// "md_responsibility_centers",
// "taxes",
// "local",
// "dms_acctnumber",
// ]}
// >
// <Input />
// </Form.Item>
// )}
// <Form.Item
// label={t("bodyshop.fields.responsibilitycenter_rate")}
// rules={[
// {
// required: true,
// //message: t("general.validation.required"),
// },
// ]}
// name={["md_responsibility_centers", "taxes", "local", "rate"]}
// >
// <InputNumber precision={2} />
// </Form.Item>
// </LayoutFormRow>
}
<LayoutFormRow header={<div>AR</div>} id="AR"> <LayoutFormRow header={<div>AR</div>} id="AR">
{/* <Form.Item {/* <Form.Item
label={t("bodyshop.fields.responsibilitycenters.ar")} label={t("bodyshop.fields.responsibilitycenters.ar")}

View File

@@ -1,269 +0,0 @@
import { DeleteFilled } from "@ant-design/icons";
import {
Button,
Checkbox,
Col,
Form,
Input,
InputNumber,
Row,
Select,
Space,
Switch,
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
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
)(ShopInfoTaskPresets);
export function ShopInfoTaskPresets({ bodyshop, form }) {
const { t } = useTranslation();
return (
<>
<LayoutFormRow noDivider>
<Form.Item
label={t("bodyshop.fields.md_tasks_presets.enable_tasks")}
valuePropName="checked"
name={["md_tasks_presets", "enable_tasks"]}
>
<Switch />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.md_tasks_presets.use_approvals")}
valuePropName="checked"
name={["md_tasks_presets", "use_approvals"]}
>
<Switch />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("bodyshop.labels.md_tasks_presets")}>
<Form.List name={["md_tasks_presets", "presets"]}>
{(fields, { add, remove, move }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<LayoutFormRow noDivider>
<Form.Item
label={t("bodyshop.fields.md_tasks_presets.name")}
key={`${index}name`}
name={[field.name, "name"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input />
</Form.Item>
<Form.Item
span={12}
label={t("bodyshop.fields.md_tasks_presets.hourstype")}
key={`${index}hourstype`}
name={[field.name, "hourstype"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Checkbox.Group>
<Row>
<Col span={4}>
<Checkbox
value="LAA"
style={{ lineHeight: "32px" }}
>
{t("joblines.fields.lbr_types.LAA")}
</Checkbox>
</Col>
<Col span={4}>
<Checkbox
value="LAB"
style={{ lineHeight: "32px" }}
>
{t("joblines.fields.lbr_types.LAB")}
</Checkbox>
</Col>
<Col span={4}>
<Checkbox
value="LAD"
style={{ lineHeight: "32px" }}
>
{t("joblines.fields.lbr_types.LAD")}
</Checkbox>
</Col>
<Col span={4}>
<Checkbox
value="LAE"
style={{ lineHeight: "32px" }}
>
{t("joblines.fields.lbr_types.LAE")}
</Checkbox>
</Col>
<Col span={4}>
<Checkbox
value="LAF"
style={{ lineHeight: "32px" }}
>
{t("joblines.fields.lbr_types.LAF")}
</Checkbox>
</Col>
<Col span={4}>
<Checkbox
value="LAG"
style={{ lineHeight: "32px" }}
>
{t("joblines.fields.lbr_types.LAG")}
</Checkbox>
</Col>
<Col span={4}>
<Checkbox
value="LAM"
style={{ lineHeight: "32px" }}
>
{t("joblines.fields.lbr_types.LAM")}
</Checkbox>
</Col>
<Col span={4}>
<Checkbox
value="LAR"
style={{ lineHeight: "32px" }}
>
{t("joblines.fields.lbr_types.LAR")}
</Checkbox>
</Col>
<Col span={4}>
<Checkbox
value="LAS"
style={{ lineHeight: "32px" }}
>
{t("joblines.fields.lbr_types.LAS")}
</Checkbox>
</Col>
<Col span={4}>
<Checkbox
value="LAU"
style={{ lineHeight: "32px" }}
>
{t("joblines.fields.lbr_types.LAU")}
</Checkbox>
</Col>
<Col span={4}>
<Checkbox
value="LA1"
style={{ lineHeight: "32px" }}
>
{t("joblines.fields.lbr_types.LA1")}
</Checkbox>
</Col>
<Col span={4}>
<Checkbox
value="LA2"
style={{ lineHeight: "32px" }}
>
{t("joblines.fields.lbr_types.LA2")}
</Checkbox>
</Col>
<Col span={4}>
<Checkbox
value="LA3"
style={{ lineHeight: "32px" }}
>
{t("joblines.fields.lbr_types.LA3")}
</Checkbox>
</Col>
<Col span={4}>
<Checkbox
value="LA4"
style={{ lineHeight: "32px" }}
>
{t("joblines.fields.lbr_types.LA4")}
</Checkbox>
</Col>
</Row>
</Checkbox.Group>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.md_tasks_presets.percent")}
key={`${index}percent`}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={[field.name, "percent"]}
>
<InputNumber min={0} max={100} />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.md_tasks_presets.memo")}
key={`${index}memo`}
name={[field.name, "memo"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.md_tasks_presets.nextstatus")}
key={`${index}nextstatus`}
name={[field.name, "nextstatus"]}
>
<Select
options={bodyshop.md_ro_statuses.production_statuses.map(
(o) => ({ value: o, label: o })
)}
/>
</Form.Item>
<Space wrap>
<DeleteFilled
onClick={() => {
remove(field.name);
}}
/>
<FormListMoveArrows
move={move}
index={index}
total={fields.length}
/>
</Space>
</LayoutFormRow>
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("bodyshop.actions.add_task_preset")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</LayoutFormRow>
</>
);
}

View File

@@ -1,7 +0,0 @@
import React from 'react'
export default function ShopEmployeeTeamMember({teamMember}) {
return (
<div>ShopEmployeeTeamMember</div>
)
}

View File

@@ -1,436 +0,0 @@
import { DeleteFilled } from "@ant-design/icons";
import { useMutation, useQuery } from "@apollo/client";
import {
Button,
Card,
Form,
Input,
InputNumber,
Space,
Switch,
notification,
} from "antd";
import querystring from "query-string";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import {
INSERT_EMPLOYEE_TEAM,
QUERY_EMPLOYEE_TEAM_BY_ID,
UPDATE_EMPLOYEE_TEAM,
} from "../../graphql/employee_teams.queries";
import EmployeeSearchSelectComponent from "../employee-search-select/employee-search-select.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export function ShopEmployeeTeamsFormComponent({ bodyshop }) {
const { t } = useTranslation();
const [form] = Form.useForm();
const history = useHistory();
const search = querystring.parse(useLocation().search);
const { error, data } = useQuery(QUERY_EMPLOYEE_TEAM_BY_ID, {
variables: { id: search.employeeTeamId },
skip: !search.employeeTeamId || search.employeeTeamId === "new",
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
});
useEffect(() => {
if (data && data.employee_teams_by_pk)
form.setFieldsValue(data.employee_teams_by_pk);
else {
form.resetFields();
}
}, [form, data, search.employeeTeamId]);
const [updateEmployeeTeam] = useMutation(UPDATE_EMPLOYEE_TEAM);
const [insertEmployeeTeam] = useMutation(INSERT_EMPLOYEE_TEAM);
const handleFinish = async ({ employee_team_members, ...values }) => {
if (search.employeeTeamId && search.employeeTeamId !== "new") {
//Update a record.
logImEXEvent("shop_employee_update");
const result = await updateEmployeeTeam({
variables: {
employeeTeamId: search.employeeTeamId,
employeeTeam: values,
teamMemberUpdates: employee_team_members
.filter((e) => e.id)
.map((e) => {
delete e.__typename;
return { where: { id: { _eq: e.id } }, _set: e };
}),
teamMemberInserts: employee_team_members
.filter((e) => e.id === null || e.id === undefined)
.map((e) => ({ ...e, teamid: search.employeeTeamId })),
teamMemberDeletes:
data.employee_teams_by_pk.employee_team_members.filter(
(e) => !employee_team_members.find((etm) => etm.id === e.id)
),
},
});
if (!result.errors) {
notification["success"]({
message: t("employees.successes.save"),
});
} else {
notification["error"]({
message: t("employees.errors.save", {
message: JSON.stringify(error),
}),
});
}
} else {
//New record, insert it.
logImEXEvent("shop_employee_insert");
insertEmployeeTeam({
variables: {
employeeTeam: {
...values,
employee_team_members: { data: employee_team_members },
bodyshopid: bodyshop.id,
},
},
refetchQueries: ["QUERY_TEAMS"],
}).then((r) => {
search.employeeTeamId = r.data.insert_employee_teams_one.id;
history.push({ search: querystring.stringify(search) });
notification["success"]({
message: t("employees.successes.save"),
});
});
}
};
if (!search.employeeTeamId) return null;
if (error) return <AlertComponent message={error.message} type="error" />;
return (
<Card
extra={
<Button type="primary" onClick={() => form.submit()}>
{t("general.actions.save")}
</Button>
}
>
<Form
onFinish={handleFinish}
autoComplete={"off"}
layout="vertical"
form={form}
>
<LayoutFormRow>
<Form.Item
name="name"
label={t("employee_teams.fields.name")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input />
</Form.Item>
<Form.Item
label={t("employee_teams.fields.active")}
name="active"
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("employee_teams.fields.max_load")}
name="max_load"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber min={0} precision={1} />
</Form.Item>
</LayoutFormRow>
<Form.List name={["employee_team_members"]}>
{(fields, { add, remove, move }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item key={field.key} style={{ padding: 0, margin: 2 }}>
<Form.Item
label={t("employees.fields.id")}
key={`${index}`}
name={[field.name, "id"]}
hidden
>
<Input />
</Form.Item>
<LayoutFormRow grow>
<Form.Item
label={t("employee_teams.fields.employeeid")}
key={`${index}`}
name={[field.name, "employeeid"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<EmployeeSearchSelectComponent
options={bodyshop.employees}
/>
</Form.Item>
<Form.Item
label={t("employee_teams.fields.percentage")}
key={`${index}`}
name={[field.name, "percentage"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber min={0} max={100} precision={2} />
</Form.Item>
<Form.Item
label={t("joblines.fields.lbr_types.LAA")}
key={`${index}`}
name={[field.name, "labor_rates", "LAA"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<CurrencyInput />
</Form.Item>
<Form.Item
label={t("joblines.fields.lbr_types.LAB")}
key={`${index}`}
name={[field.name, "labor_rates", "LAB"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<CurrencyInput />
</Form.Item>
<Form.Item
label={t("joblines.fields.lbr_types.LAD")}
key={`${index}`}
name={[field.name, "labor_rates", "LAD"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<CurrencyInput />
</Form.Item>
<Form.Item
label={t("joblines.fields.lbr_types.LAE")}
key={`${index}`}
name={[field.name, "labor_rates", "LAE"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<CurrencyInput />
</Form.Item>
<Form.Item
label={t("joblines.fields.lbr_types.LAF")}
key={`${index}`}
name={[field.name, "labor_rates", "LAF"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<CurrencyInput />
</Form.Item>
<Form.Item
label={t("joblines.fields.lbr_types.LAG")}
key={`${index}`}
name={[field.name, "labor_rates", "LAG"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<CurrencyInput />
</Form.Item>
<Form.Item
label={t("joblines.fields.lbr_types.LAM")}
key={`${index}`}
name={[field.name, "labor_rates", "LAM"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<CurrencyInput />
</Form.Item>
<Form.Item
label={t("joblines.fields.lbr_types.LAR")}
key={`${index}`}
name={[field.name, "labor_rates", "LAR"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<CurrencyInput />
</Form.Item>
<Form.Item
label={t("joblines.fields.lbr_types.LAS")}
key={`${index}`}
name={[field.name, "labor_rates", "LAS"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<CurrencyInput />
</Form.Item>
<Form.Item
label={t("joblines.fields.lbr_types.LAU")}
key={`${index}`}
name={[field.name, "labor_rates", "LAU"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<CurrencyInput />
</Form.Item>
<Form.Item
label={t("joblines.fields.lbr_types.LA1")}
key={`${index}`}
name={[field.name, "labor_rates", "LA1"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<CurrencyInput />
</Form.Item>
<Form.Item
label={t("joblines.fields.lbr_types.LA2")}
key={`${index}`}
name={[field.name, "labor_rates", "LA2"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<CurrencyInput />
</Form.Item>
<Form.Item
label={t("joblines.fields.lbr_types.LA3")}
key={`${index}`}
name={[field.name, "labor_rates", "LA3"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<CurrencyInput />
</Form.Item>
<Form.Item
label={t("joblines.fields.lbr_types.LA4")}
key={`${index}`}
name={[field.name, "labor_rates", "LA4"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<CurrencyInput />
</Form.Item>
<Space align="center">
<DeleteFilled
onClick={() => {
remove(field.name);
}}
/>
<FormListMoveArrows
move={move}
index={index}
total={fields.length}
/>
</Space>
</LayoutFormRow>
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("employee_teams.actions.newmember")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</Form>
</Card>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ShopEmployeeTeamsFormComponent);

View File

@@ -1,71 +0,0 @@
import { Button, Table } from "antd";
import queryString from "query-string";
import React from "react";
import { useTranslation } from "react-i18next";
import { useHistory, useLocation } from "react-router-dom";
export default function ShopEmployeeTeamsListComponent({
loading,
employee_teams,
}) {
const { t } = useTranslation();
const history = useHistory();
const search = queryString.parse(useLocation().search);
const handleOnRowClick = (record) => {
if (record) {
search.employeeTeamId = record.id;
history.push({ search: queryString.stringify(search) });
} else {
delete search.employeeTeamId;
history.push({ search: queryString.stringify(search) });
}
};
const columns = [
{
title: t("employee_teams.fields.name"),
dataIndex: "name",
key: "name",
},
];
return (
<div>
<Table
title={() => {
return (
<Button
type="primary"
onClick={() => {
search.employeeTeamId = "new";
history.push({ search: queryString.stringify(search) });
}}
>
{t("employee_teams.actions.new")}
</Button>
);
}}
loading={loading}
pagination={{ position: "top" }}
columns={columns}
rowKey="id"
dataSource={employee_teams}
rowSelection={{
onSelect: (props) => {
search.employeeTeamId = props.id;
history.push({ search: queryString.stringify(search) });
},
type: "radio",
selectedRowKeys: [search.employeeTeamId],
}}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
handleOnRowClick(record);
},
};
}}
/>
</div>
);
}

View File

@@ -1,43 +0,0 @@
import { useQuery } from "@apollo/client";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { QUERY_TEAMS } from "../../graphql/employee_teams.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
import ShopEmployeeTeamsListComponent from "./shop-employee-teams.list";
import ShopEmployeeTeamsFormComponent from "./shop-employee-teams.form.component";
import { Col, Row } from "antd";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
function ShopTeamsContainer({ bodyshop }) {
const { loading, error, data } = useQuery(QUERY_TEAMS, {
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
});
if (error) return <AlertComponent message={error.message} type="error" />;
return (
<div>
<RbacWrapper action="employee_teams:page">
<Row gutter={[16, 16]}>
<Col span={6}>
<ShopEmployeeTeamsListComponent
employee_teams={data ? data.employee_teams : []}
loading={loading}
/>
</Col>
<Col span={18}>
<ShopEmployeeTeamsFormComponent />
</Col>
</Row>
</RbacWrapper>
</div>
);
}
export default connect(mapStateToProps, null)(ShopTeamsContainer);

View File

@@ -1,12 +1,12 @@
import { LockOutlined, UserOutlined } from "@ant-design/icons"; import { LockOutlined, UserOutlined } from "@ant-design/icons";
import { Button, Form, Input } from "antd"; import { Button, Form, Input, Typography } from "antd";
import queryString from "query-string"; import queryString from "query-string";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Link, Redirect, useLocation } from "react-router-dom"; import { Link, Redirect, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import RomeLogo from "../../assets/RomeOnlineBlue.png"; import ImEXOnlineLogo from "../../assets/logo192.png";
import { import {
emailSignInStart, emailSignInStart,
sendPasswordReset, sendPasswordReset,
@@ -53,7 +53,8 @@ export function SignInComponent({
return ( return (
<div className="login-container"> <div className="login-container">
<div className="login-logo-container"> <div className="login-logo-container">
<img src={RomeLogo} width={200} alt="Rome Online" /> <img src={ImEXOnlineLogo} height="100" width="100" alt="ImEX Online" />
<Typography.Title>{t("titles.app")}</Typography.Title>
</div> </div>
<Form onFinish={handleFinish} form={form} size="large"> <Form onFinish={handleFinish} form={form} size="large">
<Form.Item <Form.Item

View File

@@ -8,7 +8,6 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import JobSearchSelect from "../job-search-select/job-search-select.component"; import JobSearchSelect from "../job-search-select/job-search-select.component";
import JobsDetailLaborContainer from "../jobs-detail-labor/jobs-detail-labor.container"; import JobsDetailLaborContainer from "../jobs-detail-labor/jobs-detail-labor.container";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -17,11 +16,6 @@ const mapStateToProps = createStructuredSelector({
export function TechClockInComponent({ form, bodyshop, technician }) { export function TechClockInComponent({ form, bodyshop, technician }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
const emps = bodyshop.employees.filter((e) => e.id === technician.id)[0]; const emps = bodyshop.employees.filter((e) => e.id === technician.id)[0];
return ( return (
<div> <div>
@@ -59,7 +53,7 @@ export function TechClockInComponent({ form, bodyshop, technician }) {
<Select.Option key={item.cost_center} value={item.cost_center}> <Select.Option key={item.cost_center} value={item.cost_center}>
{item.cost_center === "timetickets.labels.shift" {item.cost_center === "timetickets.labels.shift"
? t(item.cost_center) ? t(item.cost_center)
: bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || Enhanced_Payroll.treatment === "on" : bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
? t( ? t(
`joblines.fields.lbr_types.${item.cost_center.toUpperCase()}` `joblines.fields.lbr_types.${item.cost_center.toUpperCase()}`
) )

View File

@@ -16,7 +16,6 @@ import {
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import TechJobPrintTickets from "../tech-job-print-tickets/tech-job-print-tickets.component"; import TechJobPrintTickets from "../tech-job-print-tickets/tech-job-print-tickets.component";
import TechClockInComponent from "./tech-job-clock-in-form.component"; import TechClockInComponent from "./tech-job-clock-in-form.component";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
technician: selectTechnician, technician: selectTechnician,
@@ -33,10 +32,9 @@ export function TechClockInContainer({
bodyshop, bodyshop,
currentUser, currentUser,
}) { }) {
const { Enhanced_Payroll } = useTreatments( console.log(
["Enhanced_Payroll"], "🚀 ~ file: tech-job-clock-in-form.container.jsx:30 ~ technician:",
{}, technician
bodyshop.imexshopid
); );
const [form] = Form.useForm(); const [form] = Form.useForm();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@@ -70,7 +68,7 @@ export function TechClockInContainer({
jobid: values.jobid, jobid: values.jobid,
cost_center: values.cost_center, cost_center: values.cost_center,
ciecacode: ciecacode:
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || Enhanced_Payroll.treatment === 'on' bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
? values.cost_center ? values.cost_center
: Object.keys( : Object.keys(
bodyshop.md_responsibility_centers.defaults.costs bodyshop.md_responsibility_centers.defaults.costs

View File

@@ -24,7 +24,6 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import { CalculateAllocationsTotals } from "../labor-allocations-table/labor-allocations-table.utility"; import { CalculateAllocationsTotals } from "../labor-allocations-table/labor-allocations-table.utility";
import TechJobClockoutDelete from "../tech-job-clock-out-delete/tech-job-clock-out-delete.component"; import TechJobClockoutDelete from "../tech-job-clock-out-delete/tech-job-clock-out-delete.component";
import { LaborAllocationContainer } from "../time-ticket-modal/time-ticket-modal.component"; import { LaborAllocationContainer } from "../time-ticket-modal/time-ticket-modal.component";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -44,12 +43,6 @@ export function TechClockOffButton({
const [updateTimeticket] = useMutation(UPDATE_TIME_TICKET); const [updateTimeticket] = useMutation(UPDATE_TIME_TICKET);
const [updateJobStatus] = useMutation(UPDATE_JOB_STATUS); const [updateJobStatus] = useMutation(UPDATE_JOB_STATUS);
const [form] = Form.useForm(); const [form] = Form.useForm();
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
const { queryLoading, data: lineTicketData } = useQuery( const { queryLoading, data: lineTicketData } = useQuery(
GET_LINE_TICKET_BY_PK, GET_LINE_TICKET_BY_PK,
{ {
@@ -83,9 +76,7 @@ export function TechClockOffButton({
?.rate, ?.rate,
flat_rate: emps && emps.flat_rate, flat_rate: emps && emps.flat_rate,
ciecacode: ciecacode:
bodyshop.cdk_dealerid || bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
bodyshop.pbs_serialnumber ||
Enhanced_Payroll.treatment === "on"
? values.cost_center ? values.cost_center
: Object.keys( : Object.keys(
bodyshop.md_responsibility_centers.defaults.costs bodyshop.md_responsibility_centers.defaults.costs

View File

@@ -1,9 +1,4 @@
import Icon, { import Icon, { SearchOutlined, ScheduleOutlined } from "@ant-design/icons";
SearchOutlined,
ScheduleOutlined,
UserAddOutlined,
CarOutlined,
} from "@ant-design/icons";
import { Layout, Menu } from "antd"; import { Layout, Menu } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -16,38 +11,22 @@ import { createStructuredSelector } from "reselect";
import { techLogout } from "../../redux/tech/tech.actions"; import { techLogout } from "../../redux/tech/tech.actions";
import { selectTechnician } from "../../redux/tech/tech.selectors"; import { selectTechnician } from "../../redux/tech/tech.selectors";
import { BsKanban } from "react-icons/bs"; import { BsKanban } from "react-icons/bs";
import { useTreatments } from "@splitsoftware/splitio-react";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { setModalContext } from "../../redux/modals/modals.actions";
const { Sider } = Layout; const { Sider } = Layout;
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
technician: selectTechnician, technician: selectTechnician,
bodyshop: selectBodyshop,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
techLogout: () => dispatch(techLogout()), techLogout: () => dispatch(techLogout()),
setTimeTicketTaskContext: (context) =>
dispatch(setModalContext({ context: context, modal: "timeTicketTask" })),
}); });
export function TechSider({ export function TechSider({ technician, techLogout }) {
technician,
techLogout,
bodyshop,
setTimeTicketTaskContext,
}) {
const [collapsed, setCollapsed] = useState(true); const [collapsed, setCollapsed] = useState(true);
const { t } = useTranslation(); const { t } = useTranslation();
const onCollapse = (collapsed) => { const onCollapse = (collapsed) => {
setCollapsed(collapsed); setCollapsed(collapsed);
}; };
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
return ( return (
<Sider <Sider
@@ -72,38 +51,13 @@ export function TechSider({
<Menu.Item key="2" disabled={!!!technician} icon={<SearchOutlined />}> <Menu.Item key="2" disabled={!!!technician} icon={<SearchOutlined />}>
<Link to={`/tech/joblookup`}>{t("menus.tech.joblookup")}</Link> <Link to={`/tech/joblookup`}>{t("menus.tech.joblookup")}</Link>
</Menu.Item> </Menu.Item>
{Enhanced_Payroll.treatment === "on" ? ( <Menu.Item
<> key="3"
<Menu.Item disabled={!!!technician}
key="TechAssignedProdJobs" icon={<Icon component={FaBusinessTime} />}
disabled={!!!technician} >
icon={<UserAddOutlined />} <Link to={`/tech/jobclock`}>{t("menus.tech.jobclockin")}</Link>
> </Menu.Item>
<Link to={`/tech/assigned`}> {t("menus.tech.assignedjobs")}</Link>
</Menu.Item>
<Menu.Item
key="3"
disabled={!!!technician}
icon={<Icon component={FaBusinessTime} />}
onClick={() => {
setTimeTicketTaskContext({
actions: {},
context: { jobid: null },
});
}}
>
{t("menus.tech.claimtask")}
</Menu.Item>
</>
) : (
<Menu.Item
key="3"
disabled={!!!technician}
icon={<Icon component={FaBusinessTime} />}
>
<Link to={`/tech/jobclock`}>{t("menus.tech.jobclockin")}</Link>
</Menu.Item>
)}
<Menu.Item <Menu.Item
key="4" key="4"
disabled={!!!technician} disabled={!!!technician}
@@ -114,15 +68,6 @@ export function TechSider({
<Menu.Item key="5" disabled={!!!technician} icon={<ScheduleOutlined />}> <Menu.Item key="5" disabled={!!!technician} icon={<ScheduleOutlined />}>
<Link to={`/tech/list`}>{t("menus.tech.productionlist")}</Link> <Link to={`/tech/list`}>{t("menus.tech.productionlist")}</Link>
</Menu.Item> </Menu.Item>
<Menu.Item
key="dispatchedparts"
disabled={!!!technician}
icon={<CarOutlined />}
>
<Link to={`/tech/dispatchedparts`}>
{t("menus.tech.dispatchedparts")}
</Link>
</Menu.Item>
<Menu.Item <Menu.Item
key="6" key="6"
disabled={!!!technician} disabled={!!!technician}
@@ -130,7 +75,6 @@ export function TechSider({
> >
<Link to={`/tech/board`}> {t("menus.tech.productionboard")}</Link> <Link to={`/tech/board`}> {t("menus.tech.productionboard")}</Link>
</Menu.Item> </Menu.Item>
<Menu.Item <Menu.Item
key="7" key="7"
disabled={!!!technician} disabled={!!!technician}

View File

@@ -1,142 +0,0 @@
import { DownOutlined } from "@ant-design/icons";
import {
Button,
Checkbox,
Col,
Form,
InputNumber,
Popover,
Radio,
Row,
Space,
Spin,
} from "antd";
import React, { useState } from "react";
import { GET_JOB_INFO_DRAW_CALCULATIONS } from "../../graphql/jobs-lines.queries";
import { useQuery } from "@apollo/client";
export default function TimeTicketCalculatorComponent({
setProductiveHours,
jobid,
}) {
const { loading, data: lineTicketData } = useQuery(GET_JOB_INFO_DRAW_CALCULATIONS, {
variables: { id: jobid },
skip: !jobid,
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
});
const [visible, setVisible] = useState(false);
const handleOpenChange = (flag) => setVisible(flag);
const handleFinish = ({ type, hourstype, percent }) => {
//setProductiveHours(values);
//setVisible(false);
const eligibleHours = Array.isArray(hourstype)
? lineTicketData.joblines.reduce(
(acc, val) =>
acc + (hourstype.includes(val.mod_lbr_ty) ? val.mod_lb_hrs : 0),
0
)
: lineTicketData.joblines.reduce(
(acc, val) =>
acc + (hourstype === val.mod_lbr_ty ? val.mod_lb_hrs : 0),
0
);
if (type === "draw") {
setProductiveHours(eligibleHours * (percent / 100));
} else if (type === "cut") {
setProductiveHours(eligibleHours * (percent / 100));
console.log(
"Cut selected, rate set to: ",
lineTicketData.jobs_by_pk[`rate_${hourstype.toLowerCase()}`]
);
}
};
const popContent = (
<Spin spinning={loading}>
<Form onFinish={handleFinish}>
<Form.Item name="type">
<Radio.Group>
<Radio.Button value="draw">Draw</Radio.Button>
<Radio.Button value="cut">Cut of Sale</Radio.Button>
</Radio.Group>
</Form.Item>
<Form.Item noStyle shouldUpdate>
{({ getFieldValue }) => (
<Form.Item name="hourstype">
{getFieldValue("type") === "draw" ? (
<Checkbox.Group>
<Row>
<Col span={8}>
<Checkbox value="LAB" style={{ lineHeight: "32px" }}>
Body
</Checkbox>
</Col>
<Col span={8}>
<Checkbox value="LAR" style={{ lineHeight: "32px" }}>
Refinish
</Checkbox>
</Col>
<Col span={8}>
<Checkbox value="LAM" style={{ lineHeight: "32px" }}>
Mechanical
</Checkbox>
</Col>
<Col span={8}>
<Checkbox value="LAF" style={{ lineHeight: "32px" }}>
Frame
</Checkbox>
</Col>
<Col span={8}>
<Checkbox value="LAG" style={{ lineHeight: "32px" }}>
Glass
</Checkbox>
</Col>
</Row>
</Checkbox.Group>
) : (
<Radio.Group>
<Radio value="LAB">Body</Radio>
<Radio value="LAR">Refinish</Radio>
<Radio value="LAM">Mechanical</Radio>
<Radio value="LAF">Frame</Radio>
<Radio value="LAG">Glass</Radio>
</Radio.Group>
)}
</Form.Item>
)}
</Form.Item>
<Form.Item name="percent">
<InputNumber min={0} max={100} precision={1} addonAfter="%" />
</Form.Item>
<Button htmlType="submit">Calculate</Button>
</Form>
</Spin>
);
return (
<Popover
content={popContent}
trigger={["click"]}
open={visible}
onOpenChange={handleOpenChange}
placement="right"
destroyTooltipOnHide
>
<Button onClick={(e) => e.preventDefault()}>
<Space>
Draw Calculator
<DownOutlined />
</Space>
</Button>
</Popover>
);
}

View File

@@ -1,277 +0,0 @@
import { useQuery } from "@apollo/client";
import {
Button,
Form,
InputNumber,
Modal,
Radio,
Select,
Space,
Table,
Typography,
} from "antd";
import Dinero from "dinero.js";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { GET_JOB_INFO_DRAW_CALCULATIONS } from "../../graphql/jobs-lines.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
import JobSearchSelectComponent from "../job-search-select/job-search-select.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({});
export default connect(
mapStateToProps,
mapDispatchToProps
)(TimeTicketListTeamPay);
export function TimeTicketListTeamPay({ bodyshop, context, actions }) {
//const { refetch } = actions;
const { jobId } = context;
const [visible, setVisible] = useState(false);
const [form] = Form.useForm();
const { t } = useTranslation();
const {
//loading,
data: lineTicketData,
} = useQuery(GET_JOB_INFO_DRAW_CALCULATIONS, {
variables: { id: jobId },
skip: !jobId,
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
});
const handleOk = () => {
setVisible(false);
};
return (
<>
<Modal
width={"80%"}
open={visible}
destroyOnClose
onOk={handleOk}
onCancel={() => setVisible(false)}
>
<Form layout="vertical" form={form} initialValues={{ jobid: jobId }}>
<LayoutFormRow grow noDivider>
<Form.Item shouldUpdate>
{() => (
<Form.Item
name="jobid"
label={t("timetickets.fields.ro_number")}
rules={[
{
required: !(
form.getFieldValue("cost_center") ===
"timetickets.labels.shift"
),
//message: t("general.validation.required"),
},
]}
>
<JobSearchSelectComponent
convertedOnly={!bodyshop.tt_allow_post_to_invoiced}
notExported={!bodyshop.tt_allow_post_to_invoiced}
/>
</Form.Item>
)}
</Form.Item>
<Form.Item
label={t("timetickets.fields.date")}
name="date"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<FormDatePicker />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow grow noDivider>
<Form.Item name="team" label="Team">
<Select
options={Teams.map((team) => ({
value: team.name,
label: team.name,
}))}
/>
</Form.Item>
<Form.Item name="hourstype">
<Radio.Group>
<Radio value="LAB">Body</Radio>
<Radio value="LAR">Refinish</Radio>
<Radio value="LAM">Mechanical</Radio>
<Radio value="LAF">Frame</Radio>
<Radio value="LAG">Glass</Radio>
</Radio.Group>
</Form.Item>
<Form.Item name="percent">
<InputNumber min={0} max={100} precision={1} addonAfter="%" />
</Form.Item>
</LayoutFormRow>
<Form.Item shouldUpdate noStyle>
{({ getFieldsValue }) => {
const formData = getFieldsValue();
let data = [];
let eligibleHours = 0;
const theTeam = Teams.find((team) => team.name === formData.team);
if (theTeam) {
eligibleHours =
lineTicketData.joblines.reduce(
(acc, val) =>
acc +
(formData.hourstype === val.mod_lbr_ty
? val.mod_lb_hrs
: 0),
0
) * (formData.percent / 100 || 0);
data = theTeam.employees.map((e) => {
return {
employeeid: e.employeeid,
percentage: e.percentage,
rate: e.rates[formData.hourstype],
cost_center:
bodyshop.md_responsibility_centers.defaults.costs[
formData.hourstype
],
productivehrs:
Math.round(eligibleHours * 100 * (e.percentage / 100)) /
100,
pay: Dinero({
amount: Math.round(
(e.rates[formData.hourstype] || 0) * 100
),
})
.multiply(
Math.round(eligibleHours * 100 * (e.percentage / 100)) /
100
)
.toFormat("$0.00"),
};
});
}
return (
<Table
dataSource={data}
rowKey={"employeeid"}
title={() => (
<Space>
<Typography.Title level={4}>
Tickets to be Created
</Typography.Title>
<span>{`(${eligibleHours} hours to split)`}</span>
</Space>
)}
columns={[
{
title: t("timetickets.fields.employee"),
dataIndex: "employee",
key: "employee",
render: (text, record) => {
const emp = bodyshop.employees.find(
(e) => e.id === record.employeeid
);
return `${emp?.first_name} ${emp?.last_name}`;
},
},
{
title: t("timetickets.fields.cost_center"),
dataIndex: "cost_center",
key: "cost_center",
render: (text, record) =>
record.cost_center === "timetickets.labels.shift"
? t(record.cost_center)
: record.cost_center,
},
{
title: t("timetickets.fields.productivehrs"),
dataIndex: "productivehrs",
key: "productivehrs",
},
{
title: "Percentage",
dataIndex: "percentage",
key: "percentage",
},
{
title: "Rate",
dataIndex: "rate",
key: "rate",
},
{
title: "Pay",
dataIndex: "pay",
key: "pay",
},
]}
/>
);
}}
</Form.Item>
</Form>
</Modal>
<Button onClick={() => setVisible(true)}>Assign Team Pay </Button>
</>
);
}
const Teams = [
{
name: "Team A",
employees: [
{
employeeid: "9f1bdc23-8dc2-4b6a-8ca1-5f83a6fdcc22",
percentage: 50,
rates: {
LAB: 10,
LAR: 15,
},
},
{
employeeid: "201db66c-96c7-41ec-bed4-76842ba93087",
percentage: 50,
rates: {
LAB: 20,
LAR: 25,
},
},
],
},
{
name: "Team B",
employees: [
{
employeeid: "9f1bdc23-8dc2-4b6a-8ca1-5f83a6fdcc22",
percentage: 75,
rates: {
LAB: 100,
LAR: 150,
},
},
{
employeeid: "201db66c-96c7-41ec-bed4-76842ba93087",
percentage: 25,
rates: {
LAB: 200,
LAR: 250,
},
},
],
},
];

View File

@@ -1,12 +1,11 @@
import { EditFilled, SyncOutlined } from "@ant-design/icons"; import { EditFilled } from "@ant-design/icons";
import { Button, Card, Checkbox, Space, Table } from "antd"; import { Card, Space, Table } from "antd";
import moment from "moment"; import moment from "moment";
import React, { useMemo, useState } from "react"; import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions";
import { import {
selectAuthLevel, selectAuthLevel,
selectBodyshop, selectBodyshop,
@@ -19,22 +18,18 @@ import RbacWrapper, {
HasRbacAccess, HasRbacAccess,
} from "../rbac-wrapper/rbac-wrapper.component"; } from "../rbac-wrapper/rbac-wrapper.component";
import TimeTicketEnterButton from "../time-ticket-enter-button/time-ticket-enter-button.component"; import TimeTicketEnterButton from "../time-ticket-enter-button/time-ticket-enter-button.component";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
authLevel: selectAuthLevel, authLevel: selectAuthLevel,
currentUser: selectCurrentUser, currentUser: selectCurrentUser,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setTimeTicketTaskContext: (context) => //setUserLanguage: language => dispatch(setUserLanguage(language))
dispatch(setModalContext({ context: context, modal: "timeTicketTask" })),
}); });
export default connect(mapStateToProps, mapDispatchToProps)(TimeTicketList); export default connect(mapStateToProps, mapDispatchToProps)(TimeTicketList);
export function TimeTicketList({ export function TimeTicketList({
bodyshop, bodyshop,
setTimeTicketTaskContext,
authLevel, authLevel,
currentUser, currentUser,
disabled, disabled,
@@ -51,11 +46,7 @@ export function TimeTicketList({
}); });
const { t } = useTranslation(); const { t } = useTranslation();
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
const totals = useMemo(() => { const totals = useMemo(() => {
if (timetickets) if (timetickets)
return timetickets.reduce( return timetickets.reduce(
@@ -70,14 +61,6 @@ export function TimeTicketList({
}, [timetickets]); }, [timetickets]);
const columns = [ const columns = [
{
title: t("timetickets.fields.committed"),
dataIndex: "committed_at",
key: "committed_at",
render: (text, record) => (
<Checkbox disabled checked={record.committed_at} />
),
},
{ {
title: t("timetickets.fields.date"), title: t("timetickets.fields.date"),
dataIndex: "date", dataIndex: "date",
@@ -135,26 +118,21 @@ export function TimeTicketList({
}) || [], }) || [],
onFilter: (value, record) => value.includes(record.cost_center), onFilter: (value, record) => value.includes(record.cost_center),
}, },
...(jobId {
? [] title: t("jobs.fields.ro_number"),
: [ dataIndex: "ro_number",
{ key: "ro_number",
title: t("jobs.fields.ro_number"), sorter: (a, b) =>
dataIndex: "ro_number", alphaSort(a.job && a.job.ro_number, b.job && b.job.ro_number),
key: "ro_number", sortOrder:
sorter: (a, b) => state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
alphaSort(a.job && a.job.ro_number, b.job && b.job.ro_number), render: (text, record) =>
sortOrder: record.job && (
state.sortedInfo.columnKey === "ro_number" && <Link to={"/manage/jobs/" + record.job.id}>
state.sortedInfo.order, {record.job.ro_number || "N/A"}
render: (text, record) => </Link>
record.job && ( ),
<Link to={"/manage/jobs/" + record.job.id}> },
{record.job.ro_number || "N/A"}
</Link>
),
},
]),
{ {
title: t("timetickets.fields.productivehrs"), title: t("timetickets.fields.productivehrs"),
dataIndex: "productivehrs", dataIndex: "productivehrs",
@@ -164,20 +142,14 @@ export function TimeTicketList({
state.sortedInfo.columnKey === "productivehrs" && state.sortedInfo.columnKey === "productivehrs" &&
state.sortedInfo.order, state.sortedInfo.order,
}, },
...(Enhanced_Payroll.treatment === "on" {
? [] title: t("timetickets.fields.actualhrs"),
: [ dataIndex: "actualhrs",
{ key: "actualhrs",
title: t("timetickets.fields.actualhrs"), sorter: (a, b) => a.actualhrs - b.actualhrs,
dataIndex: "actualhrs", sortOrder:
key: "actualhrs", state.sortedInfo.columnKey === "actualhrs" && state.sortedInfo.order,
sorter: (a, b) => a.actualhrs - b.actualhrs, },
sortOrder:
state.sortedInfo.columnKey === "actualhrs" &&
state.sortedInfo.order,
},
]),
{ {
title: t("timetickets.fields.memo"), title: t("timetickets.fields.memo"),
dataIndex: "memo", dataIndex: "memo",
@@ -188,59 +160,42 @@ export function TimeTicketList({
render: (text, record) => render: (text, record) =>
record.clockon || record.clockoff ? t(record.memo) : record.memo, record.clockon || record.clockoff ? t(record.memo) : record.memo,
}, },
...(Enhanced_Payroll.treatment === "on" {
? [ title: t("timetickets.fields.clockon"),
{ dataIndex: "clockon",
title: t("timetickets.fields.task_name"), key: "clockon",
dataIndex: "task_name",
key: "task_name",
sorter: (a, b) => alphaSort(a.task_name, b.task_name),
sortOrder:
state.sortedInfo.columnKey === "task_name" &&
state.sortedInfo.order,
},
]
: []),
...(Enhanced_Payroll.treatment === "on"
? []
: [
{
title: t("timetickets.fields.clockon"),
dataIndex: "clockon",
key: "clockon",
render: (text, record) => ( render: (text, record) => (
<DateTimeFormatter>{record.clockon}</DateTimeFormatter> <DateTimeFormatter>{record.clockon}</DateTimeFormatter>
), ),
}, },
{ {
title: t("timetickets.fields.clockoff"), title: t("timetickets.fields.clockoff"),
dataIndex: "clockoff", dataIndex: "clockoff",
key: "clockoff", key: "clockoff",
render: (text, record) => ( render: (text, record) => (
<DateTimeFormatter>{record.clockoff}</DateTimeFormatter> <DateTimeFormatter>{record.clockoff}</DateTimeFormatter>
), ),
}, },
{ {
title: t("timetickets.fields.clockhours"), title: t("timetickets.fields.clockhours"),
dataIndex: "clockhours", dataIndex: "clockhours",
key: "clockhours", key: "clockhours",
render: (text, record) => { render: (text, record) => {
if (record.clockoff && record.clockon) if (record.clockoff && record.clockon)
return ( return (
<div> <div>
{moment(record.clockoff) {moment(record.clockoff)
.diff(moment(record.clockon), "hours", true) .diff(moment(record.clockon), "hours", true)
.toFixed(2)} .toFixed(2)}
</div> </div>
); );
else { else {
return null; return null;
} }
}, },
}, },
]),
{ {
title: t("timetickets.fields.created_by"), title: t("timetickets.fields.created_by"),
dataIndex: "created_by", dataIndex: "created_by",
@@ -250,15 +205,6 @@ export function TimeTicketList({
state.sortedInfo.columnKey === "created_by" && state.sortedInfo.order, state.sortedInfo.columnKey === "created_by" && state.sortedInfo.order,
render: (text, record) => record.created_by, render: (text, record) => record.created_by,
}, },
// {
// title: "Pay",
// dataIndex: "pay",
// key: "pay",
// render: (text, record) =>
// Dinero({ amount: Math.round(record.rate * 100) })
// .multiply(record.flat_rate ? record.productivehrs : record.actualhrs)
// .toFormat("$0.00"),
// },
{ {
title: t("general.labels.actions"), title: t("general.labels.actions"),
dataIndex: "actions", dataIndex: "actions",
@@ -288,11 +234,6 @@ export function TimeTicketList({
timeticket: record, timeticket: record,
}} }}
disabled={ disabled={
HasRbacAccess({
bodyshop,
authLevel: authLevel,
action: "timetickets:editcommitted",
}) &&
HasRbacAccess({ HasRbacAccess({
bodyshop, bodyshop,
authLevel: authLevel, authLevel: authLevel,
@@ -320,19 +261,6 @@ export function TimeTicketList({
title={t("timetickets.labels.timetickets")} title={t("timetickets.labels.timetickets")}
extra={ extra={
<Space wrap> <Space wrap>
{jobId && bodyshop.md_tasks_presets.enable_tasks && (
<Button
disabled={disabled}
onClick={() => {
setTimeTicketTaskContext({
actions: { refetch: refetch },
context: { jobid: jobId },
});
}}
>
{t("timetickets.actions.claimtasks")}
</Button>
)}
{jobId && {jobId &&
(techConsole ? null : ( (techConsole ? null : (
<TimeTicketEnterButton <TimeTicketEnterButton
@@ -349,13 +277,6 @@ export function TimeTicketList({
</TimeTicketEnterButton> </TimeTicketEnterButton>
))} ))}
{extra} {extra}
<Button
onClick={async () => {
refetch();
}}
>
<SyncOutlined />
</Button>
</Space> </Space>
} }
> >

View File

@@ -19,8 +19,6 @@ import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component"; import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component"; import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
import TimeTicketList from "../time-ticket-list/time-ticket-list.component"; import TimeTicketList from "../time-ticket-list/time-ticket-list.component";
import TimeTicketCalculatorComponent from "../time-ticket-calculator/time-ticket-calculator.component";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -38,15 +36,9 @@ export function TimeTicketModalComponent({
authLevel, authLevel,
employeeAutoCompleteOptions, employeeAutoCompleteOptions,
isEdit, isEdit,
disabled,
employeeSelectDisabled, employeeSelectDisabled,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
const [loadLineTicketData, { called, loading, data: lineTicketData }] = const [loadLineTicketData, { called, loading, data: lineTicketData }] =
useLazyQuery(GET_LINE_TICKET_BY_PK, { useLazyQuery(GET_LINE_TICKET_BY_PK, {
fetchPolicy: "network-only", fetchPolicy: "network-only",
@@ -57,14 +49,14 @@ export function TimeTicketModalComponent({
<Select <Select
value={value === "timetickets.labels.shift" ? t(value) : value} value={value === "timetickets.labels.shift" ? t(value) : value}
{...props} {...props}
disabled={value === "timetickets.labels.shift" || disabled} disabled={value === "timetickets.labels.shift"}
> >
{emps && {emps &&
emps.rates.map((item) => ( emps.rates.map((item) => (
<Select.Option key={item.cost_center} value={item.cost_center}> <Select.Option key={item.cost_center} value={item.cost_center}>
{item.cost_center === "timetickets.labels.shift" {item.cost_center === "timetickets.labels.shift"
? t(item.cost_center) ? t(item.cost_center)
: bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || Enhanced_Payroll.treatment === 'on' : bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
? t( ? t(
`joblines.fields.lbr_types.${item.cost_center.toUpperCase()}` `joblines.fields.lbr_types.${item.cost_center.toUpperCase()}`
) )
@@ -80,7 +72,7 @@ export function TimeTicketModalComponent({
<Input <Input
value={value?.startsWith("timetickets.") ? t(value) : value} value={value?.startsWith("timetickets.") ? t(value) : value}
{...props} {...props}
disabled={value?.startsWith("timetickets.") || disabled} disabled={value?.startsWith("timetickets.")}
/> />
); );
}; };
@@ -134,7 +126,7 @@ export function TimeTicketModalComponent({
]} ]}
> >
<EmployeeSearchSelect <EmployeeSearchSelect
disabled={employeeSelectDisabled || disabled} disabled={employeeSelectDisabled}
options={employeeAutoCompleteOptions} options={employeeAutoCompleteOptions}
onSelect={(value) => { onSelect={(value) => {
const emps = const emps =
@@ -186,73 +178,65 @@ export function TimeTicketModalComponent({
<LayoutFormRow> <LayoutFormRow>
<Form.Item shouldUpdate> <Form.Item shouldUpdate>
{() => ( {() => (
<> <Form.Item
<Form.Item label={t("timetickets.fields.productivehrs")}
label={t("timetickets.fields.productivehrs")} name="productivehrs"
name="productivehrs" rules={[
rules={[ ({ getFieldValue }) => ({
({ getFieldValue }) => ({ validator(rule, value) {
validator(rule, value) { if (!bodyshop.tt_enforce_hours_for_tech_console) {
if (!bodyshop.tt_enforce_hours_for_tech_console) { return Promise.resolve();
return Promise.resolve(); }
} if (
if ( !value ||
!value || getFieldValue("cost_center") === null ||
getFieldValue("cost_center") === null || !lineTicketData
!lineTicketData )
) return Promise.resolve();
return Promise.resolve();
//Check the cost center, //Check the cost center,
const totals = CalculateAllocationsTotals( const totals = CalculateAllocationsTotals(
bodyshop, bodyshop,
lineTicketData.joblines, lineTicketData.joblines,
lineTicketData.timetickets, lineTicketData.timetickets,
lineTicketData.jobs_by_pk.lbr_adjustments lineTicketData.jobs_by_pk.lbr_adjustments
);
const fieldTypeToCheck =
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
? "mod_lbr_ty"
: "cost_center";
const costCenterDiff =
Math.round(
totals.find(
(total) =>
total[fieldTypeToCheck] ===
getFieldValue("cost_center")
)?.difference * 10
) / 10;
if (value > costCenterDiff)
return Promise.reject(
t(
"timetickets.validation.hoursenteredmorethanavailable"
)
); );
else {
const fieldTypeToCheck = return Promise.resolve();
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber }
? "mod_lbr_ty"
: "cost_center";
const costCenterDiff =
Math.round(
totals.find(
(total) =>
total[fieldTypeToCheck] ===
getFieldValue("cost_center")
)?.difference * 10
) / 10;
if (value > costCenterDiff)
return Promise.reject(
t(
"timetickets.validation.hoursenteredmorethanavailable"
)
);
else {
return Promise.resolve();
}
},
}),
{
required:
form.getFieldValue("cost_center") !==
"timetickets.labels.shift",
//message: t("general.validation.required"),
}, },
]} }),
> {
<InputNumber precision={1} /> required:
</Form.Item> form.getFieldValue("cost_center") !==
<TimeTicketCalculatorComponent "timetickets.labels.shift",
jobid={form.getFieldValue("jobid")} //message: t("general.validation.required"),
setProductiveHours={(productivehrs) => },
form.setFieldsValue({ productivehrs }) ]}
} >
/> <InputNumber precision={1} />
</> </Form.Item>
)} )}
</Form.Item> </Form.Item>
<Form.Item <Form.Item
@@ -286,7 +270,6 @@ export function TimeTicketModalComponent({
<FormDateTimePicker <FormDateTimePicker
minuteStep={5} minuteStep={5}
disabled={ disabled={
disabled ||
!HasRbacAccess({ !HasRbacAccess({
bodyshop, bodyshop,
authLevel, authLevel,
@@ -325,7 +308,6 @@ export function TimeTicketModalComponent({
<FormDateTimePicker <FormDateTimePicker
minuteStep={5} minuteStep={5}
disabled={ disabled={
disabled ||
!HasRbacAccess({ !HasRbacAccess({
bodyshop, bodyshop,
authLevel, authLevel,
@@ -381,12 +363,7 @@ export function TimeTicketModalComponent({
); );
} }
export function LaborAllocationContainer({ export function LaborAllocationContainer({ jobid, loading, lineTicketData }) {
jobid,
loading,
lineTicketData,
hideTimeTickets = false,
}) {
if (loading) return <LoadingSkeleton />; if (loading) return <LoadingSkeleton />;
if (!lineTicketData) return null; if (!lineTicketData) return null;
return ( return (
@@ -397,13 +374,12 @@ export function LaborAllocationContainer({
timetickets={lineTicketData.timetickets} timetickets={lineTicketData.timetickets}
adjustments={lineTicketData.jobs_by_pk.lbr_adjustments} adjustments={lineTicketData.jobs_by_pk.lbr_adjustments}
/> />
{!hideTimeTickets && (
<TimeTicketList <TimeTicketList
loading={loading} loading={loading}
timetickets={lineTicketData.timetickets} timetickets={lineTicketData.timetickets}
techConsole techConsole
/> />
)}
</div> </div>
); );
} }

View File

@@ -14,8 +14,6 @@ import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectTimeTicket } from "../../redux/modals/modals.selectors"; import { selectTimeTicket } from "../../redux/modals/modals.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import TimeTicketModalComponent from "./time-ticket-modal.component"; import TimeTicketModalComponent from "./time-ticket-modal.component";
import TimeTicketsCommitToggleComponent from "../time-tickets-commit-toggle/time-tickets-commit-toggle.component";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
timeTicketModal: selectTimeTicket, timeTicketModal: selectTimeTicket,
@@ -36,11 +34,7 @@ export function TimeTicketModalContainer({
const [enterAgain, setEnterAgain] = useState(false); const [enterAgain, setEnterAgain] = useState(false);
const [insertTicket] = useMutation(INSERT_NEW_TIME_TICKET); const [insertTicket] = useMutation(INSERT_NEW_TIME_TICKET);
const [updateTicket] = useMutation(UPDATE_TIME_TICKET); const [updateTicket] = useMutation(UPDATE_TIME_TICKET);
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
const { data: EmployeeAutoCompleteData } = useQuery(QUERY_ACTIVE_EMPLOYEES, { const { data: EmployeeAutoCompleteData } = useQuery(QUERY_ACTIVE_EMPLOYEES, {
skip: !timeTicketModal.visible, skip: !timeTicketModal.visible,
fetchPolicy: "network-only", fetchPolicy: "network-only",
@@ -153,7 +147,7 @@ export function TimeTicketModalContainer({
if (!!changedFields.cost_center && !!EmployeeAutoCompleteData) { if (!!changedFields.cost_center && !!EmployeeAutoCompleteData) {
form.setFieldsValue({ form.setFieldsValue({
ciecacode: ciecacode:
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || Enhanced_Payroll.treatment === 'on' bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
? changedFields.cost_center ? changedFields.cost_center
: Object.keys( : Object.keys(
bodyshop.md_responsibility_centers.defaults.costs bodyshop.md_responsibility_centers.defaults.costs
@@ -181,11 +175,7 @@ export function TimeTicketModalContainer({
footer={ footer={
<span> <span>
<Button onClick={handleCancel}>{t("general.actions.cancel")}</Button> <Button onClick={handleCancel}>{t("general.actions.cancel")}</Button>
<Button <Button loading={loading} onClick={() => form.submit()}>
loading={loading}
disabled={timeTicketModal.context?.timeticket?.committed_at}
onClick={() => form.submit()}
>
{t("general.actions.save")} {t("general.actions.save")}
</Button> </Button>
{timeTicketModal.context && timeTicketModal.context.id ? null : ( {timeTicketModal.context && timeTicketModal.context.id ? null : (
@@ -209,7 +199,6 @@ export function TimeTicketModalContainer({
autoComplete={"off"} autoComplete={"off"}
form={form} form={form}
onFinishFailed={() => setEnterAgain(false)} onFinishFailed={() => setEnterAgain(false)}
disabled={timeTicketModal.context?.timeticket?.committed_at}
initialValues={ initialValues={
timeTicketModal.context.timeticket timeTicketModal.context.timeticket
? { ? {
@@ -230,9 +219,6 @@ export function TimeTicketModalContainer({
<PageHeader <PageHeader
extra={ extra={
<Space> <Space>
<TimeTicketsCommitToggleComponent
timeticket={timeTicketModal.context?.timeticket}
/>
<Button onClick={handleCancel}> <Button onClick={handleCancel}>
{t("general.actions.cancel")} {t("general.actions.cancel")}
</Button> </Button>
@@ -256,16 +242,14 @@ export function TimeTicketModalContainer({
<TimeTicketModalComponent <TimeTicketModalComponent
isEdit={timeTicketModal.context.id} isEdit={timeTicketModal.context.id}
form={form} form={form}
disabled={timeTicketModal.context?.timeticket?.committed_at}
employeeAutoCompleteOptions={ employeeAutoCompleteOptions={
EmployeeAutoCompleteData && EmployeeAutoCompleteData.employees EmployeeAutoCompleteData && EmployeeAutoCompleteData.employees
} }
employeeSelectDisabled={ employeeSelectDisabled={
timeTicketModal.context?.timeticket?.committed_at || timeTicketModal.context?.timeticket?.employeeid &&
(timeTicketModal.context?.timeticket?.employeeid &&
!timeTicketModal.context.id !timeTicketModal.context.id
? true ? true
: false) : false
} }
/> />
</Form> </Form>

View File

@@ -1,197 +0,0 @@
import {
Alert,
Col,
Form,
Radio,
Row,
Skeleton,
Space,
Spin,
Typography,
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import ReadOnlyFormItemComponent from "../form-items-formatted/read-only-form-item.component";
import JobSearchSelectComponent from "../job-search-select/job-search-select.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(TimeTicketTaskModalComponent);
export function TimeTicketTaskModalComponent({
bodyshop,
form,
loading,
completedTasks,
unassignedHours,
}) {
const { t } = useTranslation();
return (
<div>
<Row gutter={[16, 16]}>
<Col xl={12} lg={24}>
<Form.Item
name="jobid"
label={t("timetickets.fields.ro_number")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<JobSearchSelectComponent convertedOnly={true} notExported={true} />
</Form.Item>
<Space wrap>
<Form.Item name="task" label={t("timetickets.labels.task")}>
{loading ? (
<Spin />
) : (
<Radio.Group
optionType="button"
options={bodyshop.md_tasks_presets.presets.map((preset) => ({
value: preset.name,
label: preset.name,
disabled: completedTasks
.map((task) => task.name)
.includes(preset.name),
}))}
/>
)}
</Form.Item>
<Form.Item dependencies={["task"]}>
{() => {
const { task } = form.getFieldsValue();
const theTaskPreset = bodyshop.md_tasks_presets?.presets?.find(
(tp) => tp.name === task
);
if (!task) return null;
return (
<table className="task-tickets-table">
<tbody>
<tr>
<td>{t("bodyshop.fields.md_tasks_presets.percent")}</td>
<td>{`${theTaskPreset.percent || 0}%`}</td>
</tr>
<tr>
<td>
{t("bodyshop.fields.md_tasks_presets.hourstype")}
</td>
<td>{theTaskPreset.hourstype.join(", ")}</td>
</tr>
<tr>
<td>
{t("bodyshop.fields.md_tasks_presets.nextstatus")}
</td>
<td>{theTaskPreset.nextstatus}</td>
</tr>
</tbody>
</table>
);
}}
</Form.Item>
</Space>
</Col>
<Col xl={12} lg={24}>
{loading ? (
<Skeleton />
) : (
<Form.List name="timetickets">
{(fields, { add, remove, move }) => {
return (
<>
<Typography.Title level={4}>
{t("timetickets.labels.claimtaskpreview")}
</Typography.Title>
<table className="task-tickets-table">
<thead>
<tr>
<th>{t("timetickets.fields.employee")}</th>
<th>{t("timetickets.fields.cost_center")}</th>
<th>{t("timetickets.fields.ciecacode")}</th>
<th>{t("timetickets.fields.productivehrs")}</th>
</tr>
</thead>
<tbody>
{fields.map((field, index) => (
<tr key={field.key}>
<td>
<Form.Item
key={`${index}employeeid`}
name={[field.name, "employeeid"]}
>
<ReadOnlyFormItemComponent type="employee" />
</Form.Item>
</td>
<td>
<Form.Item
key={`${index}cost_center`}
name={[field.name, "cost_center"]}
>
<ReadOnlyFormItemComponent />
</Form.Item>
</td>
<td>
<Form.Item
key={`${index}ciecacode`}
name={[field.name, "ciecacode"]}
>
<ReadOnlyFormItemComponent />
</Form.Item>
</td>
<td>
<Form.Item
key={`${index}productivehrs`}
name={[field.name, "productivehrs"]}
>
<ReadOnlyFormItemComponent />
</Form.Item>
</td>
</tr>
))}
</tbody>
</table>
<Alert
type="success"
message={t("timetickets.labels.payrollclaimedtasks")}
/>
</>
);
}}
</Form.List>
)}
{unassignedHours > 0 && (
<Alert
type="error"
message={t("timetickets.validation.unassignedlines", {
unassignedHours: unassignedHours,
})}
/>
)}
</Col>
</Row>
{bodyshop?.md_tasks_presets?.use_approvals && (
<Col xl={12} lg={24}>
<Alert
message={t("tt_approvals.labels.approval_queue_in_use")}
type="warning"
/>
</Col>
)}
</div>
);
}

View File

@@ -1,151 +0,0 @@
import React, { useCallback, useEffect, useState } from "react";
import { Form, Modal, notification } from "antd";
import axios from "axios";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectTimeTicketTasks } from "../../redux/modals/modals.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import TimeTicketTaskModalComponent from "./time-ticket-task-modal.component";
import { useApolloClient } from "@apollo/client";
import { QUERY_COMPLETED_TASKS } from "../../graphql/jobs.queries";
import "./time-ticket-task-modal.styles.scss";
import { selectTechnician } from "../../redux/tech/tech.selectors";
const mapStateToProps = createStructuredSelector({
timeTicketTasksModal: selectTimeTicketTasks,
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
technician: selectTechnician,
});
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("timeTicketTask")),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(TimeTickeTaskModalContainer);
export function TimeTickeTaskModalContainer({
bodyshop,
currentUser,
technician,
timeTicketTasksModal,
toggleModalVisible,
}) {
const [form] = Form.useForm();
const { context, visible, actions } = timeTicketTasksModal;
const [completedTasks, setCompletedTasks] = useState([]);
const [unassignedHours, setUnassignedHours] = useState(0);
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const client = useApolloClient();
async function handleFinish(values) {
calculateTickets({ values, handleFinish: true });
}
const getCompletedTasks = useCallback(
async (jobid) => {
setLoading(true);
const { data } = await client.query({
query: QUERY_COMPLETED_TASKS,
variables: { jobid },
});
setCompletedTasks(data.jobs_by_pk.completed_tasks || []);
setLoading(false);
},
[client]
);
useEffect(() => {
if (visible) {
form.setFieldsValue({ ...context, task: null, timetickets: null });
if (context.jobid) {
getCompletedTasks(context.jobid);
}
}
}, [context.jobid, visible, getCompletedTasks, form, context]);
async function handleValueChange(changedValues, allValues) {
if (changedValues.jobid) {
getCompletedTasks(changedValues.jobid);
}
if (allValues.jobid && allValues.task) {
calculateTickets({ values: allValues, handleFinish: false });
}
}
const calculateTickets = async ({ values, handleFinish }) => {
setLoading(true);
try {
const { data, ...response } = await axios.post("/payroll/claimtask", {
jobid: values.jobid,
task: values.task,
calculateOnly: !handleFinish,
employee: technician
? {
name: `${technician.first_name} ${technician.last_name}`.trim(),
employeeid: technician.id,
}
: { name: currentUser.displayName, email: currentUser.email },
});
if (response.status === 200 && handleFinish) {
//Close the modal
if (actions?.refetch) actions.refetch();
toggleModalVisible();
} else if (handleFinish === false) {
form.setFieldsValue({ timetickets: data.ticketsToInsert });
setUnassignedHours(data.unassignedHours);
} else {
notification.open({
type: "error",
message: t("timetickets.errors.creating", {
message: JSON.stringify(data),
}),
});
}
} catch (error) {
notification.open({
type: "error",
message: t("timetickets.errors.creating", { message: error.message }),
});
} finally {
setLoading(false);
}
};
return (
<Modal
destroyOnClose
open={visible}
onCancel={() => {
toggleModalVisible();
form.resetFields();
}}
width="80%"
onOk={() => form.submit()}
>
<Form
autoComplete={"off"}
form={form}
layout="vertical"
onFinish={handleFinish}
initialValues={context}
onValuesChange={handleValueChange}
>
<TimeTicketTaskModalComponent
form={form}
loading={loading}
completedTasks={completedTasks}
unassignedHours={unassignedHours}
/>
</Form>
</Modal>
);
}

View File

@@ -1,19 +0,0 @@
.task-tickets-table {
table-layout: fixed;
width: 100%;
th,
td {
padding: 8px;
text-align: left;
border-bottom: 1px solid #ddd;
.ant-form-item {
margin-bottom: 0px !important;
}
}
tr:hover {
background-color: #f5f5f5;
}
}

View File

@@ -1,104 +0,0 @@
import { useMutation } from "@apollo/client";
import { Button, notification } from "antd";
import moment from "moment";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { UPDATE_TIME_TICKET } from "../../graphql/timetickets.queries";
import { setModalContext } from "../../redux/modals/modals.actions";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
setTimeTicketContext: (context) =>
dispatch(setModalContext({ context: context, modal: "timeTicket" })),
});
export function TimeTicketsCommit({
bodyshop,
currentUser,
timeticket,
disabled,
refetch,
setTimeTicketContext,
}) {
const { t } = useTranslation();
const [updateTimeTicket] = useMutation(UPDATE_TIME_TICKET);
const [loading, setLoading] = useState(false);
const handleCommit = async () => {
setLoading(true);
try {
const ticketUpdate = timeticket.committed_at
? { commited_by: null, committed_at: null }
: {
commited_by: currentUser.email,
committed_at: moment(),
};
const result = await updateTimeTicket({
variables: {
timeticketId: timeticket.id,
timeticket: ticketUpdate,
},
update(cache) {
cache.modify({
fields: {
timeTickets(existingtickets, { readField }) {
return existingtickets.map((ticket) => {
if (timeticket.id === readField("id", ticket)) {
return {
...ticket,
...ticketUpdate,
};
}
return ticket;
});
},
},
});
},
});
if (result.errors) {
notification.open({
type: "error",
message: t("timetickets.errors.creating", {
message: JSON.stringify(result.errors),
}),
});
} else {
setTimeTicketContext({
context: {
id: timeticket.id,
timeticket: result.data.update_timetickets.returning[0],
},
});
notification.open({
type: "success",
message: t("timetickets.successes.committed"),
});
}
} catch (error) {
} finally {
setLoading(false);
}
};
if (!timeticket?.id) return null;
return (
<Button onClick={handleCommit} loading={loading} disabled={!timeticket?.id}>
{timeticket?.committed_at
? t("timetickets.actions.uncommit")
: t("timetickets.actions.commitone")}
</Button>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(TimeTicketsCommit);

View File

@@ -1,95 +0,0 @@
import { useMutation } from "@apollo/client";
import { Button, notification } from "antd";
import moment from "moment";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { UPDATE_TIME_TICKETS } from "../../graphql/timetickets.queries";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
export function TimeTicketsCommit({
bodyshop,
currentUser,
timetickets,
disabled,
loadingCallback,
completedCallback,
refetch,
}) {
const { t } = useTranslation();
const [updateTimeTickets] = useMutation(UPDATE_TIME_TICKETS);
const [loading, setLoading] = useState(false);
const handleCommit = async () => {
setLoading(true);
try {
const result = await updateTimeTickets({
variables: {
timeticketIds: timetickets.map((ticket) => ticket.id),
timeticket: {
commited_by: currentUser.email,
committed_at: moment(),
},
},
update(cache) {
cache.modify({
fields: {
timeTickets(existingtickets, { readField }) {
const modifiedIds = timetickets.map((ticket) => ticket.id);
return existingtickets.map((ticket) => {
if (modifiedIds.includes(readField("id", ticket))) {
return {
...ticket,
commited_by: currentUser.email,
committed_at: moment(),
};
}
return ticket;
});
},
},
});
},
});
if (result.errors) {
notification.open({
type: "error",
message: t("timetickets.errors.creating", {
message: JSON.stringify(result.errors),
}),
});
} else {
notification.open({
type: "success",
message: t("timetickets.successes.committed"),
});
if (!!completedCallback) completedCallback([]);
if (!!loadingCallback) loadingCallback(false);
}
} catch (error) {
} finally {
setLoading(false);
}
};
return (
<Button
onClick={handleCommit}
loading={loading}
disabled={disabled || timetickets?.length === 0}
>
{t("timetickets.actions.commit", { count: timetickets?.length })}
</Button>
);
}
export default connect(mapStateToProps, null)(TimeTicketsCommit);

View File

@@ -10,7 +10,7 @@ import { onlyUnique } from "../../utils/arrayHelper";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component"; import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
import Dinero from "dinero.js";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
@@ -129,16 +129,6 @@ const JobRelatedTicketsTable = ({
return acc; return acc;
}, 0); }, 0);
const pay = item.tickets
.filter((ticket) => ticket.cost_center === costCenter)
.reduce((acc, val) => {
return acc.add(
Dinero({ amount: Math.round(val.rate * 100) }).multiply(
val.flat_rate ? val.productivehrs : val.actualhrs
)
);
}, Dinero());
return { return {
id: `${item.jobKey}${costCenter}`, id: `${item.jobKey}${costCenter}`,
costCenter, costCenter,
@@ -146,7 +136,6 @@ const JobRelatedTicketsTable = ({
actHrs: actHrs.toFixed(1), actHrs: actHrs.toFixed(1),
prodHrs: prodHrs.toFixed(1), prodHrs: prodHrs.toFixed(1),
clockHrs, clockHrs,
pay,
}; };
}); });
}) })
@@ -206,15 +195,6 @@ const JobRelatedTicketsTable = ({
state.sortedInfo.columnKey === "clockHrs" && state.sortedInfo.order, state.sortedInfo.columnKey === "clockHrs" && state.sortedInfo.order,
render: (text, record) => record.clockHrs.toFixed(2), render: (text, record) => record.clockHrs.toFixed(2),
}, },
{
title: "Pay",
dataIndex: "Pay",
key: "Pay",
sorter: (a, b) => a.clockHrs - b.clockHrs,
sortOrder:
state.sortedInfo.columnKey === "clockHrs" && state.sortedInfo.order,
render: (text, record) => record.pay.toFormat("$0.00"),
},
{ {
title: t("general.labels.actions"), title: t("general.labels.actions"),
dataIndex: "actions", dataIndex: "actions",

View File

@@ -1,241 +0,0 @@
import { SyncOutlined } from "@ant-design/icons";
import { Button, Card, Space, Table, Tag } from "antd";
import Dinero from "dinero.js";
import queryString from "query-string";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link, useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions";
import {
selectAuthLevel,
selectBodyshop,
} from "../../redux/user/user.selectors";
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
import { onlyUnique } from "../../utils/arrayHelper";
import { alphaSort, dateSort } from "../../utils/sorters";
import TtApproveButtonComponent from "../tt-approve-button/tt-approve-button.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
authLevel: selectAuthLevel,
});
const mapDispatchToProps = (dispatch) => ({
setTimeTicketTaskContext: (context) =>
dispatch(setModalContext({ context: context, modal: "timeTicketTask" })),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(TtApprovalsListComponent);
export function TtApprovalsListComponent({
bodyshop,
setTimeTicketTaskContext,
authLevel,
disabled,
loading,
tt_approval_queue,
total,
refetch,
techConsole,
jobId,
extra,
}) {
const [state, setState] = useState({
sortedInfo: {},
filteredInfo: { text: "" },
});
const { t } = useTranslation();
const history = useHistory();
const search = queryString.parse(useLocation().search);
const { page } = search;
const [selectedTickets, setSelectedTickets] = useState([]);
const columns = [
{
title: t("timetickets.fields.date"),
dataIndex: "date",
key: "date",
sorter: (a, b) => dateSort(a.date, b.date),
sortOrder:
state.sortedInfo.columnKey === "date" && state.sortedInfo.order,
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
},
{
title: t("timetickets.fields.employee"),
dataIndex: "employeeid",
key: "employeeid",
sorter: (a, b) => alphaSort(a.employee.last_name, b.employee.last_name),
sortOrder:
state.sortedInfo.columnKey === "employee" && state.sortedInfo.order,
render: (text, record) =>
`${record.employee.first_name} ${record.employee.last_name}`,
filters:
tt_approval_queue
.map((l) => l.employeeid)
.filter(onlyUnique)
.map((s) => {
return {
text: (() => {
const emp = bodyshop.employees.find((e) => e.id === s);
return `${emp?.first_name} ${emp?.last_name}`;
})(), //
value: [s],
};
}) || [],
onFilter: (value, record) => value.includes(record.employeeid),
},
{
title: t("timetickets.fields.cost_center"),
dataIndex: "cost_center",
key: "cost_center",
sorter: (a, b) => alphaSort(a.cost_center, b.cost_center),
render: (text, record) =>
record.cost_center === "timetickets.labels.shift"
? t(record.cost_center)
: record.cost_center,
sortOrder:
state.sortedInfo.columnKey === "cost_center" && state.sortedInfo.order,
filters:
tt_approval_queue
.map((l) => l.cost_center)
.filter(onlyUnique)
.map((s) => {
return {
text: s === "timetickets.labels.shift" ? t(s) : s, //|| "No Status*",
value: [s],
};
}) || [],
onFilter: (value, record) => value.includes(record.cost_center),
},
{
title: t("jobs.fields.ro_number"),
dataIndex: "ro_number",
key: "ro_number",
sorter: (a, b) =>
alphaSort(a.job && a.job.ro_number, b.job && b.job.ro_number),
sortOrder:
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
render: (text, record) =>
record.job && (
<Link to={"/manage/jobs/" + record.job.id}>
<Space wrap>
{record.job.ro_number || "N/A"}
<Tag>{record.job.status}</Tag>
</Space>
</Link>
),
},
{
title: t("timetickets.fields.productivehrs"),
dataIndex: "productivehrs",
key: "productivehrs",
sorter: (a, b) => a.productivehrs - b.productivehrs,
sortOrder:
state.sortedInfo.columnKey === "productivehrs" &&
state.sortedInfo.order,
},
{
title: t("timetickets.fields.actualhrs"),
dataIndex: "actualhrs",
key: "actualhrs",
sorter: (a, b) => a.actualhrs - b.actualhrs,
sortOrder:
state.sortedInfo.columnKey === "actualhrs" && state.sortedInfo.order,
},
{
title: t("timetickets.fields.memo"),
dataIndex: "memo",
key: "memo",
sorter: (a, b) => alphaSort(a.memo, b.memo),
sortOrder:
state.sortedInfo.columnKey === "memo" && state.sortedInfo.order,
render: (text, record) =>
record.clockon || record.clockoff ? t(record.memo) : record.memo,
},
{
title: t("timetickets.fields.clockon"),
dataIndex: "clockon",
key: "clockon",
render: (text, record) => (
<DateTimeFormatter>{record.clockon}</DateTimeFormatter>
),
},
{
title: "Pay",
dataIndex: "pay",
key: "pay",
render: (text, record) =>
Dinero({ amount: Math.round(record.rate * 100) })
.multiply(record.flat_rate ? record.productivehrs : record.actualhrs)
.toFormat("$0.00"),
},
];
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
search.page = pagination.current;
if (sorter && sorter.column && sorter.column.sortObject) {
search.searchObj = JSON.stringify(sorter.column.sortObject(sorter.order));
} else {
delete search.searchObj;
search.sortcolumn = sorter.order ? sorter.columnKey : null;
search.sortorder = sorter.order;
}
search.sort = JSON.stringify({ [sorter.columnKey]: sorter.order });
history.push({ search: queryString.stringify(search) });
};
return (
<Card
title={t("timetickets.labels.timetickets")}
extra={
<Space wrap>
{extra}
<TtApproveButtonComponent
selectedTickets={selectedTickets}
disabled={selectedTickets.length === 0}
completedCallback={setSelectedTickets}
refetch={refetch}
/>
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
</Space>
}
>
<Table
loading={loading}
columns={columns}
rowKey="id"
scroll={{
x: true,
}}
pagination={{
position: "top",
pageSize: 25,
current: parseInt(page || 1),
total: total,
}}
dataSource={tt_approval_queue}
onChange={handleTableChange}
rowSelection={{
onSelectAll: (selected, selectedRows) =>
setSelectedTickets(selectedRows.map((i) => i.id)),
onSelect: (record, selected, selectedRows, nativeEvent) => {
setSelectedTickets(selectedRows.map((i) => i.id));
},
selectedRowKeys: selectedTickets,
type: "checkbox",
}}
/>
</Card>
);
}

View File

@@ -1,69 +0,0 @@
import { useQuery } from "@apollo/client";
import queryString from "query-string";
import React from "react";
import { connect } from "react-redux";
import { useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { QUERY_ALL_TT_APPROVALS_PAGINATED } from "../../graphql/tt-approvals.queries";
import {
setBreadcrumbs,
setSelectedHeader,
} from "../../redux/application/application.actions";
import AlertComponent from "../alert/alert.component";
import TtApprovalsListComponent from "./tt-approvals-list.component";
const mapStateToProps = createStructuredSelector({});
const mapDispatchToProps = (dispatch) => ({
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
});
export function TimeTicketsContainer({
bodyshop,
setBreadcrumbs,
setSelectedHeader,
}) {
const searchParams = queryString.parse(useLocation().search);
const { page, sortcolumn, sortorder, search, searchObj } = searchParams;
const { loading, error, data, refetch } = useQuery(
QUERY_ALL_TT_APPROVALS_PAGINATED,
{
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
variables: {
search: search || "",
offset: page ? (page - 1) * 25 : 0,
limit: 25,
order: [
searchObj
? JSON.parse(searchObj)
: {
[sortcolumn || "date"]: sortorder
? sortorder === "descend"
? "desc"
: "asc"
: "desc",
},
],
},
}
);
if (error) return <AlertComponent message={error.message} type="error" />;
return (
<TtApprovalsListComponent
loading={loading}
tt_approval_queue={data ? data.tt_approval_queue : []}
total={data ? data.tt_approval_queue_aggregate.aggregate.count : 0}
refetch={refetch}
/>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(TimeTicketsContainer);

View File

@@ -1,103 +0,0 @@
import { useApolloClient } from "@apollo/client";
import { Button, notification } from "antd";
import _ from "lodash";
import moment from "moment";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { INSERT_TIME_TICKET_AND_APPROVE } from "../../graphql/timetickets.queries";
import { QUERY_TT_APPROVALS_BY_IDS } from "../../graphql/tt-approvals.queries";
import {
selectAuthLevel,
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
authLevel: selectAuthLevel,
});
export function TtApproveButton({
bodyshop,
currentUser,
selectedTickets,
disabled,
authLevel,
loadingCallback,
completedCallback,
refetch,
}) {
const { t } = useTranslation();
const client = useApolloClient();
const [loading, setLoading] = useState(false);
const handleQbxml = async () => {
setLoading(true);
try {
const { data } = await client.query({
query: QUERY_TT_APPROVALS_BY_IDS,
variables: { ids: selectedTickets },
});
const insertResponse = await client.mutate({
mutation: INSERT_TIME_TICKET_AND_APPROVE,
variables: {
timeTicketInput: data.tt_approval_queue.map((tta) => ({
..._.omit(tta, ["id", "__typename"]),
ttapprovalqueueid: tta.id,
})),
approvalIds: selectedTickets,
approvalUpdate: {
approved_at: moment(),
approved_by: currentUser.email,
},
},
});
if (insertResponse.errors) {
notification.open({
type: "error",
message: t("timetickets.errors.creating", {
message: JSON.stringify(insertResponse.errors),
}),
});
} else {
notification.open({
type: "success",
message: t("timetickets.successes.created"),
});
}
} catch (error) {
notification.open({
type: "error",
message: t("timetickets.errors.creating", {
message: error.message,
}),
});
} finally {
setLoading(false);
}
// if (!!completedCallback) completedCallback([]);
// if (!!loadingCallback) loadingCallback(false);
};
return (
<Button
onClick={handleQbxml}
loading={loading}
disabled={
disabled ||
!HasRbacAccess({ bodyshop, authLevel, action: "ttapprovals:approve" })
}
>
{t("tt_approvals.actions.approveselected")}
</Button>
);
}
export default connect(mapStateToProps, null)(TtApproveButton);

View File

@@ -55,7 +55,7 @@ export function UserRequestResetPw({
return ( return (
<div className="login-container"> <div className="login-container">
<div className="login-logo-container"> <div className="login-logo-container">
<img src={ImEXOnlineLogo} height="100" width="100" alt="Rome Online" /> <img src={ImEXOnlineLogo} height="100" width="100" alt="ImEX Online" />
<Typography.Title>{t("titles.app")}</Typography.Title> <Typography.Title>{t("titles.app")}</Typography.Title>
</div> </div>
<Typography.Title level={3}>{t("titles.resetpassword")}</Typography.Title> <Typography.Title level={3}>{t("titles.resetpassword")}</Typography.Title>

View File

@@ -25,7 +25,11 @@ export const QUERY_ALL_BILLS_PAGINATED = gql`
$limit: Int $limit: Int
$order: [bills_order_by!]! $order: [bills_order_by!]!
) { ) {
bills(offset: $offset, limit: $limit, order_by: $order) { bills(
offset: $offset
limit: $limit
order_by: $order
) {
id id
vendorid vendorid
vendor { vendor {
@@ -94,23 +98,6 @@ export const QUERY_BILLS_BY_JOBID = gql`
comments comments
user_email user_email
} }
parts_dispatch(where: { jobid: { _eq: $jobid } }) {
id
dispatched_at
dispatched_by
employeeid
number
parts_dispatch_lines {
joblineid
id
quantity
accepted_at
jobline {
id
line_desc
}
}
}
bills(where: { jobid: { _eq: $jobid } }, order_by: { date: desc }) { bills(where: { jobid: { _eq: $jobid } }, order_by: { date: desc }) {
id id
vendorid vendorid

View File

@@ -118,21 +118,7 @@ export const QUERY_BODYSHOP = gql`
md_parts_scan md_parts_scan
enforce_conversion_category enforce_conversion_category
tt_enforce_hours_for_tech_console tt_enforce_hours_for_tech_console
md_tasks_presets
use_paint_scale_data use_paint_scale_data
employee_teams(
order_by: { name: asc }
where: { active: { _eq: true } }
) {
id
name
employee_team_members {
id
employeeid
labor_rates
percentage
}
}
employees { employees {
user_email user_email
id id
@@ -249,20 +235,6 @@ export const UPDATE_SHOP = gql`
md_parts_scan md_parts_scan
enforce_conversion_category enforce_conversion_category
tt_enforce_hours_for_tech_console tt_enforce_hours_for_tech_console
md_tasks_presets
employee_teams(
order_by: { name: asc }
where: { active: { _eq: true } }
) {
id
name
employee_team_members {
id
employeeid
labor_rates
percentage
}
}
employees { employees {
id id
first_name first_name

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