RO into IO merge as of 02/05/2024.
This commit is contained in:
@@ -6,7 +6,7 @@ orbs:
|
||||
aws-cli: circleci/aws-cli@4.0
|
||||
eb: circleci/aws-elastic-beanstalk@2.0.1
|
||||
jobs:
|
||||
api-deploy:
|
||||
imex-api-deploy:
|
||||
docker:
|
||||
- image: cimg/node:18.18.2
|
||||
steps:
|
||||
@@ -19,9 +19,9 @@ jobs:
|
||||
eb deploy
|
||||
eb status
|
||||
|
||||
hasura-migrate:
|
||||
imex-hasura-migrate:
|
||||
docker:
|
||||
- image: cimg/node:16.15.0
|
||||
- image: cimg/node:18.18.2
|
||||
parameters:
|
||||
secret:
|
||||
type: string
|
||||
@@ -38,30 +38,10 @@ jobs:
|
||||
hasura metadata apply --endpoint https://db.imex.online/ --admin-secret << parameters.secret >>
|
||||
hasura metadata reload --endpoint https://db.imex.online/ --admin-secret << parameters.secret >>
|
||||
|
||||
app-build:
|
||||
docker:
|
||||
- image: cimg/node:16.15.0
|
||||
|
||||
working_directory: ~/repo/client
|
||||
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/repo
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: npm i
|
||||
|
||||
- run: npm run build
|
||||
|
||||
- aws-s3/sync:
|
||||
from: build
|
||||
to: "s3://imex-online-production/"
|
||||
arguments: "--exclude '*.map'"
|
||||
|
||||
app-beta-build:
|
||||
imex-app-build:
|
||||
docker:
|
||||
- image: cimg/node:18.18.2
|
||||
resource_class: large
|
||||
|
||||
working_directory: ~/repo/client
|
||||
|
||||
steps:
|
||||
@@ -71,7 +51,7 @@ jobs:
|
||||
name: Install Dependencies
|
||||
command: npm i
|
||||
|
||||
- run: npm run build
|
||||
- run: npm run build:imex
|
||||
|
||||
- aws-cli/setup:
|
||||
aws_access_key_id: AWS_ACCESS_KEY_ID
|
||||
@@ -80,9 +60,106 @@ jobs:
|
||||
|
||||
- aws-s3/sync:
|
||||
from: build
|
||||
to: "s3://imex-online-beta/"
|
||||
to: "s3://imex-online-production/"
|
||||
arguments: "--exclude '*.map'"
|
||||
|
||||
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
|
||||
|
||||
rome-hasura-migrate:
|
||||
docker:
|
||||
- image: cimg/node:18.18.2
|
||||
parameters:
|
||||
secret:
|
||||
type: string
|
||||
default: $HASURA_ROME_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:18.18.2
|
||||
|
||||
working_directory: ~/repo/client
|
||||
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/repo
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: npm i
|
||||
|
||||
- run: npm run build:rome
|
||||
|
||||
- aws-cli/setup:
|
||||
aws_access_key_id: AWS_ACCESS_KEY_ID
|
||||
aws_secret_access_key: AWS_SECRET_ACCESS_KEY
|
||||
region: AWS_REGION
|
||||
|
||||
- aws-s3/sync:
|
||||
from: build
|
||||
to: "s3://rome-online-production/"
|
||||
arguments: "--exclude '*.map'"
|
||||
|
||||
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
|
||||
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:18.18.2
|
||||
|
||||
working_directory: ~/repo/client
|
||||
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/repo
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: npm i
|
||||
|
||||
- run: npm run build:test:rome
|
||||
|
||||
- aws-s3/sync:
|
||||
from: build
|
||||
to: "s3://rome-online-test/"
|
||||
arguments: "--exclude '*.map'"
|
||||
|
||||
|
||||
test-hasura-migrate:
|
||||
docker:
|
||||
- image: cimg/node:16.15.0
|
||||
@@ -98,12 +175,11 @@ jobs:
|
||||
name: Execute migration
|
||||
command: |
|
||||
npm install hasura-cli -g
|
||||
echo ${HASURA_TEST_SECRET}
|
||||
hasura migrate apply --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >>
|
||||
hasura metadata apply --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >>
|
||||
hasura metadata reload --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >>
|
||||
|
||||
test-app-build:
|
||||
imex-test-app-build:
|
||||
docker:
|
||||
- image: cimg/node:16.15.0
|
||||
|
||||
@@ -116,40 +192,13 @@ jobs:
|
||||
name: Install Dependencies
|
||||
command: npm i
|
||||
|
||||
- run: npm run build:test
|
||||
- run: npm run build:test:imex
|
||||
|
||||
- aws-s3/sync:
|
||||
from: build
|
||||
to: "s3://imex-online-test/"
|
||||
arguments: "--exclude '*.map'"
|
||||
|
||||
test-app-beta-build:
|
||||
docker:
|
||||
- image: cimg/node:18.18.2
|
||||
resource_class: large
|
||||
working_directory: ~/repo/client
|
||||
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/repo
|
||||
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: npm i
|
||||
|
||||
- run: npm run build:test
|
||||
|
||||
- run: npm run sentry:sourcemaps
|
||||
|
||||
- aws-cli/setup:
|
||||
aws_access_key_id: AWS_ACCESS_KEY_ID
|
||||
aws_secret_access_key: AWS_SECRET_ACCESS_KEY
|
||||
region: AWS_REGION
|
||||
|
||||
- aws-s3/sync:
|
||||
from: build
|
||||
to: "s3://imex-online-test-beta/"
|
||||
arguments: "--exclude '*.map'"
|
||||
|
||||
admin-app-build:
|
||||
docker:
|
||||
@@ -184,37 +233,52 @@ jobs:
|
||||
workflows:
|
||||
deploy_and_build:
|
||||
jobs:
|
||||
- api-deploy:
|
||||
- imex-api-deploy:
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
- app-build:
|
||||
- imex-app-build:
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
- app-beta-build:
|
||||
filters:
|
||||
branches:
|
||||
only: master-beta
|
||||
- hasura-migrate:
|
||||
secret: ${HASURA_PROD_SECRET}
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
- test-app-build:
|
||||
- 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
|
||||
- imex-test-app-build:
|
||||
filters:
|
||||
branches:
|
||||
only: test
|
||||
- test-app-beta-build:
|
||||
filters:
|
||||
branches:
|
||||
only: test-beta
|
||||
|
||||
- test-hasura-migrate:
|
||||
secret: ${HASURA_TEST_SECRET}
|
||||
filters:
|
||||
branches:
|
||||
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:
|
||||
#filters:
|
||||
#branches:
|
||||
#only: master
|
||||
#only: master
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,4 +10,5 @@ REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BG3tzU7L2BXlGZ_3VLK4PNaRceoEXEnmHfxcVbRMF5o
|
||||
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
||||
REACT_APP_AXIOS_BASE_API_URL=http://localhost:4000
|
||||
REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
|
||||
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
|
||||
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
|
||||
INSTANCE=IMEX
|
||||
14
client/.env.development.promanager
Normal file
14
client/.env.development.promanager
Normal file
@@ -0,0 +1,14 @@
|
||||
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_GA_CODE=231099835
|
||||
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=https://res.cloudinary.com/io-test
|
||||
REACT_APP_CLOUDINARY_API_KEY=957865933348715
|
||||
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
|
||||
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BG3tzU7L2BXlGZ_3VLK4PNaRceoEXEnmHfxcVbRMF5o5g05ejslhVPki9kBM9cBBT-08Ad9kN3HSpS6JmrWD6h4'
|
||||
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
||||
REACT_APP_AXIOS_BASE_API_URL=http://localhost:4000
|
||||
REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
|
||||
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
|
||||
INSTANCE=PROMANAGER
|
||||
15
client/.env.development.rome
Normal file
15
client/.env.development.rome
Normal file
@@ -0,0 +1,15 @@
|
||||
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_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_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/io-test
|
||||
REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/io-test
|
||||
REACT_APP_CLOUDINARY_API_KEY=957865933348715
|
||||
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
|
||||
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo'
|
||||
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
||||
REACT_APP_AXIOS_BASE_API_URL=http://localhost:4000
|
||||
REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
|
||||
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
|
||||
REACT_APP_COUNTRY=USA
|
||||
INSTANCE=ROME
|
||||
@@ -11,4 +11,5 @@ REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BMgZT1NZztW2DsJl8Mg2L04hgY9FzAg6b8fbzgNAfww
|
||||
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
||||
REACT_APP_AXIOS_BASE_API_URL=https://api.imex.online/
|
||||
REACT_APP_REPORTS_SERVER_URL=https://reports.imex.online
|
||||
REACT_APP_SPLIT_API=et9pjkik6bn67he5evpmpr1agoo7gactphgk
|
||||
REACT_APP_SPLIT_API=et9pjkik6bn67he5evpmpr1agoo7gactphgk
|
||||
INSTANCE=IMEX
|
||||
15
client/.env.production.promanager
Normal file
15
client/.env.production.promanager
Normal file
@@ -0,0 +1,15 @@
|
||||
GENERATE_SOURCEMAP=true
|
||||
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_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=https://res.cloudinary.com/bodyshop
|
||||
REACT_APP_CLOUDINARY_API_KEY=473322739956866
|
||||
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
|
||||
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BMgZT1NZztW2DsJl8Mg2L04hgY9FzAg6b8fbzgNAfww2VDzH3VE63Ot9EaP_U7KWS2JT-7HPHaw0T_Tw_5vkZc8'
|
||||
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
||||
REACT_APP_AXIOS_BASE_API_URL=https://api.imex.online/
|
||||
REACT_APP_REPORTS_SERVER_URL=https://reports.imex.online
|
||||
REACT_APP_SPLIT_API=et9pjkik6bn67he5evpmpr1agoo7gactphgk
|
||||
INSTANCE=PROMANAGER
|
||||
15
client/.env.production.rome
Normal file
15
client/.env.production.rome
Normal file
@@ -0,0 +1,15 @@
|
||||
GENERATE_SOURCEMAP=true
|
||||
REACT_APP_GRAPHQL_ENDPOINT=https://db.romeonline.io/v1/graphql
|
||||
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.romeonline.io/v1/graphql
|
||||
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_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/bodyshop
|
||||
REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/bodyshop
|
||||
REACT_APP_CLOUDINARY_API_KEY=473322739956866
|
||||
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
|
||||
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo'
|
||||
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
||||
REACT_APP_AXIOS_BASE_API_URL=https://api.romeonline.io/
|
||||
REACT_APP_REPORTS_SERVER_URL=https://reports.romeonline.io
|
||||
REACT_APP_SPLIT_API=et9pjkik6bn67he5evpmpr1agoo7gactphgk
|
||||
INSTANCE=ROME
|
||||
@@ -11,4 +11,5 @@ REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxw
|
||||
REACT_APP_AXIOS_BASE_API_URL=https://api.test.imex.online/
|
||||
REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
|
||||
REACT_APP_IS_TEST=true
|
||||
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
|
||||
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
|
||||
INSTANCE=IMEX
|
||||
15
client/.env.test.promanager
Normal file
15
client/.env.test.promanager
Normal file
@@ -0,0 +1,15 @@
|
||||
REACT_APP_GRAPHQL_ENDPOINT=https://db.test.bodyshop.app/v1/graphql
|
||||
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.test.bodyshop.app/v1/graphql
|
||||
REACT_APP_GA_CODE=231099835
|
||||
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=https://res.cloudinary.com/bodyshop
|
||||
REACT_APP_CLOUDINARY_API_KEY=473322739956866
|
||||
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
|
||||
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BN2GcDPjipR5MTEosO5dT4CfQ3cmrdBIsI4juoOQrRijn_5aRiHlwj1mlq0W145mOusx6xynEKl_tvYJhpCc9lo'
|
||||
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
||||
REACT_APP_AXIOS_BASE_API_URL=https://api.test.imex.online/
|
||||
REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
|
||||
REACT_APP_IS_TEST=true
|
||||
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
|
||||
INSTANCE=PROMANAGER
|
||||
15
client/.env.test.rome
Normal file
15
client/.env.test.rome
Normal file
@@ -0,0 +1,15 @@
|
||||
REACT_APP_GRAPHQL_ENDPOINT=https://db.test.romeonline.io/v1/graphql
|
||||
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.test.romeonline.io/v1/graphql
|
||||
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_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/bodyshop
|
||||
REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/bodyshop
|
||||
REACT_APP_CLOUDINARY_API_KEY=473322739956866
|
||||
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
|
||||
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo'
|
||||
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
||||
REACT_APP_AXIOS_BASE_API_URL=https://api.test.romeonline.io/
|
||||
REACT_APP_REPORTS_SERVER_URL=https://reports.test.romeonline.io
|
||||
REACT_APP_IS_TEST=true
|
||||
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
|
||||
INSTANCE=ROME
|
||||
@@ -13,21 +13,7 @@ const v4Token = convertLegacyToken(mapToken);
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
// {
|
||||
// plugin: SentryWebpackPlugin,
|
||||
// options: {
|
||||
// // sentry-cli configuration
|
||||
// authToken:
|
||||
// "6b45b028a02342db97a9a2f92c0959058665443d379d4a3a876430009e744260",
|
||||
// org: "snapt-software",
|
||||
// project: "imexonline",
|
||||
// release: process.env.REACT_APP_GIT_SHA,
|
||||
//
|
||||
// // webpack-specific configuration
|
||||
// include: ".",
|
||||
// ignore: ["node_modules", "webpack.config.js"],
|
||||
// },
|
||||
// },
|
||||
|
||||
{
|
||||
plugin: CracoLessPlugin,
|
||||
options: {
|
||||
|
||||
1493
client/package-lock.json
generated
1493
client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -89,15 +89,21 @@
|
||||
"scripts": {
|
||||
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
||||
"start": "craco start",
|
||||
"start:imex": "dotenvx run --env-file=.env.development.imex -- craco start",
|
||||
"start:rome": "dotenvx run --env-file=.env.development.rome -- craco start",
|
||||
"start:promanager": "dotenvx run --env-file=.env.development.promanager -- craco start",
|
||||
"build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
|
||||
"build:test": "env-cmd -f .env.test npm run build",
|
||||
"build-deploy:test": "npm run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'",
|
||||
"buildcra": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
|
||||
"build:test:imex": "env-cmd -f .env.test.imex npm run build",
|
||||
"build:test:rome": "env-cmd -f .env.test.rome npm run build",
|
||||
"build:test:promanager": "env-cmd -f .env.test.promanager npm run build",
|
||||
"build:production:imex": "env-cmd -f .env.production.imex npm run build",
|
||||
"build:production:rome": "env-cmd -f .env.production.rome npm run build",
|
||||
"build:production:promanager": "env-cmd -f .env.production.promanager npm run build",
|
||||
"test": "cypress open",
|
||||
"eject": "react-scripts eject",
|
||||
"madge": "madge --image ./madge-graph.svg --extensions js,jsx,ts,tsx --circular .",
|
||||
"eulaize": "node src/utils/eulaize.js",
|
||||
"sentry:sourcemaps": "sentry-cli sourcemaps inject --org imex --project imexonline ./build && sentry-cli sourcemaps upload --org imex --project imexonline ./build"
|
||||
"sentry:sourcemaps:imex": "sentry-cli sourcemaps inject --org imex --project imexonline ./build && sentry-cli sourcemaps upload --org imex --project imexonline ./build"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
@@ -123,6 +129,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||
"@dotenvx/dotenvx": "^0.15.0",
|
||||
"@sentry/webpack-plugin": "^2.14.0",
|
||||
"@testing-library/cypress": "^10.0.1",
|
||||
"cypress": "^13.6.4",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -28,6 +28,18 @@ switch (this.location.hostname) {
|
||||
// measurementId: "${config.measurementId}",
|
||||
};
|
||||
break;
|
||||
//TODO:AIO Update this for pro manager and include all .env files for server and client.
|
||||
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":
|
||||
default:
|
||||
firebaseConfig = {
|
||||
|
||||
@@ -2,13 +2,88 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<link href="%PUBLIC_URL%/favicon.png" rel="icon"/>
|
||||
<!-- TODO:AIO Change the favicon based on instance type. -->
|
||||
<link href="%PUBLIC_URL%/ro-favicon.png" rel="icon"/>
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport"/>
|
||||
<meta content="#002366" name="theme-color"/>
|
||||
<meta content="ImEX Online" name="description"/>
|
||||
<!-- TODO:AIO Change the title name based on instance. -->
|
||||
<meta content="Rome Online" name="description"/>
|
||||
<!-- <link rel="apple-touch-icon" href="logo192.png" /> -->
|
||||
|
||||
<!-- TODO:AIO Need to verify that this will build based on the instance type. -->
|
||||
<!-- TODO:AIO Show the code below if the instance is ROME. -->
|
||||
<!--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 party="LiveChat528346" phonesystem-url=https://rometech.east.3cx.us:5001></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 charset="utf-8" defer id="tcx-callus-js"
|
||||
src=https://downloads-global.3cx.com/downloads/livechatandtalk/v1/callus.js></script>
|
||||
|
||||
<link href="logo192.png" rel="apple-touch-icon"/>
|
||||
<script type="text/javascript">
|
||||
<!-- TODO:AIO Show this script if instance type is IMEX -->
|
||||
<script type="text/javascript">
|
||||
window.$crisp = [];
|
||||
window.CRISP_WEBSITE_ID = "36724f62-2eb0-4b29-9cdd-9905fb99913e";
|
||||
(function () {
|
||||
@@ -19,7 +94,7 @@
|
||||
d.getElementsByTagName("head")[0].appendChild(s);
|
||||
})();
|
||||
</script>
|
||||
<script>
|
||||
<script>
|
||||
!(function () {
|
||||
"use strict";
|
||||
var e = [
|
||||
@@ -69,6 +144,7 @@
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<!-- TODO:AIO Update the manifest as a post-build step? -->
|
||||
<link href="%PUBLIC_URL%/manifest.json" rel="manifest"/>
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
@@ -79,7 +155,8 @@
|
||||
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`.
|
||||
-->
|
||||
<title>ImEX Online</title>
|
||||
<!-- TODO:AIO Update based on instance type. -->
|
||||
<title>Rome Online</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"short_name": "ImEX Online",
|
||||
"name": "ImEX Online",
|
||||
"short_name": "Rome Online",
|
||||
"name": "Rome Online",
|
||||
"description": "The ultimate bodyshop management system.",
|
||||
"icons": [
|
||||
{
|
||||
|
||||
BIN
client/public/ro-favicon.png
Normal file
BIN
client/public/ro-favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 376 B |
@@ -1,11 +1,11 @@
|
||||
import {useSplitClient} from "@splitsoftware/splitio-react";
|
||||
import {Button, Result} from "antd";
|
||||
import { useSplitClient } from "@splitsoftware/splitio-react";
|
||||
import { Button, Result } from "antd";
|
||||
import LogRocket from "logrocket";
|
||||
import React, {lazy, Suspense, useEffect, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {Route, Routes} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import React, { lazy, Suspense, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Route, Routes } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import DocumentEditorContainer from "../components/document-editor/document-editor.container";
|
||||
import ErrorBoundary from "../components/error-boundary/error-boundary.component";
|
||||
|
||||
@@ -14,15 +14,19 @@ import LoadingSpinner from "../components/loading-spinner/loading-spinner.compon
|
||||
import DisclaimerPage from "../pages/disclaimer/disclaimer.page";
|
||||
import LandingPage from "../pages/landing/landing.page";
|
||||
import TechPageContainer from "../pages/tech/tech.page.container";
|
||||
import {setOnline} from "../redux/application/application.actions";
|
||||
import {selectOnline} from "../redux/application/application.selectors";
|
||||
import {checkUserSession} from "../redux/user/user.actions";
|
||||
import {selectBodyshop, selectCurrentEula, selectCurrentUser,} from "../redux/user/user.selectors";
|
||||
import { setOnline } from "../redux/application/application.actions";
|
||||
import { selectOnline } from "../redux/application/application.selectors";
|
||||
import { checkUserSession } from "../redux/user/user.actions";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentEula,
|
||||
selectCurrentUser,
|
||||
} from "../redux/user/user.selectors";
|
||||
import PrivateRoute from "../components/PrivateRoute";
|
||||
import "./App.styles.scss";
|
||||
import handleBeta from "../utils/betaHandler";
|
||||
import Eula from "../components/eula/eula.component";
|
||||
|
||||
import InstanceRenderMgr from "../utils/instanceRenderMgr";
|
||||
const ResetPassword = lazy(() =>
|
||||
import("../pages/reset-password/reset-password.component")
|
||||
);
|
||||
@@ -37,18 +41,24 @@ const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
online: selectOnline,
|
||||
bodyshop: selectBodyshop,
|
||||
currentEula: selectCurrentEula
|
||||
currentEula: selectCurrentEula,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
checkUserSession: () => dispatch(checkUserSession()),
|
||||
setOnline: (isOnline) => dispatch(setOnline(isOnline)),
|
||||
});
|
||||
|
||||
export function App({bodyshop, checkUserSession, currentUser, online, setOnline, currentEula}) {
|
||||
export function App({
|
||||
bodyshop,
|
||||
checkUserSession,
|
||||
currentUser,
|
||||
online,
|
||||
setOnline,
|
||||
currentEula,
|
||||
}) {
|
||||
const client = useSplitClient().client;
|
||||
const [listenersAdded, setListenersAdded] = useState(false)
|
||||
const {t} = useTranslation();
|
||||
|
||||
const [listenersAdded, setListenersAdded] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
if (!navigator.onLine) {
|
||||
@@ -65,14 +75,14 @@ export function App({bodyshop, checkUserSession, currentUser, online, setOnline,
|
||||
useEffect(() => {
|
||||
const offlineListener = (e) => {
|
||||
setOnline(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onlineListener = (e) => {
|
||||
setOnline(true);
|
||||
}
|
||||
};
|
||||
|
||||
if (!listenersAdded) {
|
||||
console.log('Added events for offline and online');
|
||||
console.log("Added events for offline and online");
|
||||
window.addEventListener("offline", offlineListener);
|
||||
window.addEventListener("online", onlineListener);
|
||||
setListenersAdded(true);
|
||||
@@ -81,7 +91,7 @@ export function App({bodyshop, checkUserSession, currentUser, online, setOnline,
|
||||
return () => {
|
||||
window.removeEventListener("offline", offlineListener);
|
||||
window.removeEventListener("online", onlineListener);
|
||||
}
|
||||
};
|
||||
}, [setOnline, listenersAdded]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -90,16 +100,26 @@ export function App({bodyshop, checkUserSession, currentUser, online, setOnline,
|
||||
|
||||
if (
|
||||
client.getTreatment("LogRocket_Tracking") === "on" ||
|
||||
window.location.hostname === 'beta.imex.online'
|
||||
window.location.hostname ===
|
||||
InstanceRenderMgr({
|
||||
imex: "beta.imex.online",
|
||||
rome: "beta.romeonline.io",
|
||||
})
|
||||
) {
|
||||
console.log("LR Start");
|
||||
LogRocket.init("gvfvfw/bodyshopapp");
|
||||
LogRocket.init(
|
||||
InstanceRenderMgr({
|
||||
imex: "gvfvfw/bodyshopapp",
|
||||
rome: "rome-online/rome-online",
|
||||
promanager: "", //TODO:AIO Add in log rocket for promanager instances.
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}, [bodyshop, client, currentUser.authorized]);
|
||||
|
||||
if (currentUser.authorized === null) {
|
||||
return <LoadingSpinner message={t("general.labels.loggingin")}/>;
|
||||
return <LoadingSpinner message={t("general.labels.loggingin")} />;
|
||||
}
|
||||
|
||||
handleBeta();
|
||||
@@ -124,29 +144,102 @@ export function App({bodyshop, checkUserSession, currentUser, online, setOnline,
|
||||
);
|
||||
|
||||
if (currentEula && !currentUser.eulaIsAccepted) {
|
||||
return <Eula/>
|
||||
return <Eula />;
|
||||
}
|
||||
|
||||
// Any route that is not assigned and matched will default to the Landing Page component
|
||||
return (
|
||||
<Suspense fallback={<LoadingSpinner message="ImEX Online"/>}>
|
||||
<Suspense
|
||||
fallback={
|
||||
<LoadingSpinner
|
||||
message={InstanceRenderMgr({
|
||||
imex: "ImEX Online",
|
||||
rome: "Rome Online",
|
||||
promanager: "ProManager",
|
||||
})}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Routes>
|
||||
<Route path="*" element={<ErrorBoundary><LandingPage/></ErrorBoundary>}/>
|
||||
<Route path="/signin" element={<ErrorBoundary><SignInPage/></ErrorBoundary>}/>
|
||||
<Route path="/resetpassword" element={<ErrorBoundary><ResetPassword/></ErrorBoundary>}/>
|
||||
<Route path="/csi/:surveyId" element={<ErrorBoundary><CsiPage/></ErrorBoundary>}/>
|
||||
<Route path="/disclaimer" element={<ErrorBoundary><DisclaimerPage/></ErrorBoundary>}/>
|
||||
<Route path="/mp/:paymentIs" element={<ErrorBoundary><MobilePaymentContainer/></ErrorBoundary>}/>
|
||||
<Route path="/manage/*"
|
||||
element={<ErrorBoundary><PrivateRoute isAuthorized={currentUser.authorized}/></ErrorBoundary>}>
|
||||
<Route path="*" element={<ManagePage/>}/>
|
||||
<Route
|
||||
path="*"
|
||||
element={
|
||||
<ErrorBoundary>
|
||||
<LandingPage />
|
||||
</ErrorBoundary>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/signin"
|
||||
element={
|
||||
<ErrorBoundary>
|
||||
<SignInPage />
|
||||
</ErrorBoundary>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/resetpassword"
|
||||
element={
|
||||
<ErrorBoundary>
|
||||
<ResetPassword />
|
||||
</ErrorBoundary>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/csi/:surveyId"
|
||||
element={
|
||||
<ErrorBoundary>
|
||||
<CsiPage />
|
||||
</ErrorBoundary>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/disclaimer"
|
||||
element={
|
||||
<ErrorBoundary>
|
||||
<DisclaimerPage />
|
||||
</ErrorBoundary>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/mp/:paymentIs"
|
||||
element={
|
||||
<ErrorBoundary>
|
||||
<MobilePaymentContainer />
|
||||
</ErrorBoundary>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/manage/*"
|
||||
element={
|
||||
<ErrorBoundary>
|
||||
<PrivateRoute
|
||||
isAuthorized={currentUser.authorized}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
}
|
||||
>
|
||||
<Route path="*" element={<ManagePage />} />
|
||||
</Route>
|
||||
<Route path="/tech/*"
|
||||
element={<ErrorBoundary><PrivateRoute isAuthorized={currentUser.authorized}/></ErrorBoundary>}>
|
||||
<Route path="*" element={<TechPageContainer/>}/>
|
||||
<Route
|
||||
path="/tech/*"
|
||||
element={
|
||||
<ErrorBoundary>
|
||||
<PrivateRoute
|
||||
isAuthorized={currentUser.authorized}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
}
|
||||
>
|
||||
<Route path="*" element={<TechPageContainer />} />
|
||||
</Route>
|
||||
<Route path="/edit/*" element={<PrivateRoute isAuthorized={currentUser.authorized}/>}>
|
||||
<Route path="*" element={<DocumentEditorContainer/>}/>
|
||||
<Route
|
||||
path="/edit/*"
|
||||
element={
|
||||
<PrivateRoute isAuthorized={currentUser.authorized} />
|
||||
}
|
||||
>
|
||||
<Route path="*" element={<DocumentEditorContainer />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</Suspense>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {defaultsDeep} from "lodash";
|
||||
import {theme} from "antd";
|
||||
import InstanceRenderMgr from '../utils/instanceRenderMgr'
|
||||
|
||||
const {defaultAlgorithm, darkAlgorithm} = theme;
|
||||
|
||||
@@ -23,7 +24,10 @@ const defaultTheme = {
|
||||
}
|
||||
},
|
||||
token: {
|
||||
colorPrimary: '#1677ff'
|
||||
colorPrimary: InstanceRenderMgr({
|
||||
imex: "#1677ff",
|
||||
rome:"#326ade"}) ,
|
||||
colorInfo: "#326ade"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -57,4 +61,4 @@ const finaltheme = {
|
||||
algorithm: isDarkMode ? darkAlgorithm : defaultAlgorithm,
|
||||
...defaultsDeep(currentTheme, defaultTheme)
|
||||
}
|
||||
export default finaltheme;
|
||||
export default finaltheme;
|
||||
|
||||
BIN
client/src/assets/RomeOnline.png
Normal file
BIN
client/src/assets/RomeOnline.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
client/src/assets/RomeOnlineBlue.png
Normal file
BIN
client/src/assets/RomeOnlineBlue.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
BIN
client/src/assets/romelogo.png
Normal file
BIN
client/src/assets/romelogo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
@@ -1,4 +1,5 @@
|
||||
import {useApolloClient, useMutation} from "@apollo/client";
|
||||
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||
import {Button, Checkbox, Form, Modal, notification, Space} from "antd";
|
||||
import _ from "lodash";
|
||||
import React, {useEffect, useMemo, useState} from "react";
|
||||
@@ -57,9 +58,21 @@ function BillEnterModalContainer({
|
||||
"enter_bill_generate_label",
|
||||
false
|
||||
);
|
||||
|
||||
const {treatments: {Enhanced_Payroll}} = useSplitTreatments({
|
||||
attributes: {},
|
||||
names: ["Enhanced_Payroll"],
|
||||
splitKey: bodyshop.imexshopid,
|
||||
});
|
||||
|
||||
const formValues = useMemo(() => {
|
||||
return {
|
||||
...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:
|
||||
(billEnterModal.context.job && billEnterModal.context.job.id) || null,
|
||||
federal_tax_rate:
|
||||
@@ -92,6 +105,7 @@ function BillEnterModalContainer({
|
||||
} = values;
|
||||
|
||||
let adjustmentsToInsert = {};
|
||||
let payrollAdjustmentsToInsert = [];
|
||||
|
||||
const r1 = await insertBill({
|
||||
variables: {
|
||||
@@ -107,14 +121,33 @@ function BillEnterModalContainer({
|
||||
lbr_adjustment,
|
||||
location: lineLocation,
|
||||
part_type,
|
||||
create_ppc,
|
||||
original_actual_price,
|
||||
...restI
|
||||
} = i;
|
||||
|
||||
if (deductedfromlbr) {
|
||||
adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] =
|
||||
(adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] || 0) -
|
||||
restI.actual_price / lbr_adjustment.rate;
|
||||
if (Enhanced_Payroll.treatment === "on") {
|
||||
if (
|
||||
deductedfromlbr &&
|
||||
true //payroll is on
|
||||
) {
|
||||
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 {
|
||||
...restI,
|
||||
deductedfromlbr: deductedfromlbr,
|
||||
@@ -140,6 +173,20 @@ function BillEnterModalContainer({
|
||||
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);
|
||||
if (adjKeys.length > 0) {
|
||||
//Query the adjustments, merge, and update them.
|
||||
@@ -248,6 +295,14 @@ function BillEnterModalContainer({
|
||||
location: li.location || location,
|
||||
status:
|
||||
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,
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -316,12 +371,12 @@ function BillEnterModalContainer({
|
||||
});
|
||||
|
||||
if (enterAgain) {
|
||||
form.resetFields();
|
||||
form.resetFields();
|
||||
// form.resetFields();
|
||||
form.setFieldsValue({
|
||||
...formValues,
|
||||
billlines: [],
|
||||
});
|
||||
form.resetFields();
|
||||
} else {
|
||||
toggleModalVisible();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import React from "react";
|
||||
import {
|
||||
PlusCircleFilled,
|
||||
MinusCircleFilled,
|
||||
WarningOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import {Form, Button, InputNumber, Input, Select, Switch, Space} from "antd";
|
||||
import {MinusCircleFilled, PlusCircleFilled, WarningOutlined,} from "@ant-design/icons";
|
||||
import {Button, Form, Input, InputNumber, Select, Space, Switch} from "antd";
|
||||
import {useTranslation} from "react-i18next";
|
||||
|
||||
import {connect} from "react-redux";
|
||||
|
||||
@@ -21,6 +21,7 @@ import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
||||
import BillFormLines from "./bill-form.lines.component";
|
||||
import {CalculateBillTotal} from "./bill-form.totals.utility";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -68,17 +69,17 @@ export function BillFormComponent({
|
||||
};
|
||||
|
||||
const handleFederalTaxExemptSwitchToggle = (checked) => {
|
||||
// Early gate
|
||||
if (!checked) return;
|
||||
const values = form.getFieldsValue("billlines");
|
||||
// Gate bill lines
|
||||
if (!values?.billlines?.length) return;
|
||||
// Early gate
|
||||
if (!checked) return;
|
||||
const values = form.getFieldsValue("billlines");
|
||||
// Gate bill lines
|
||||
if (!values?.billlines?.length) return;
|
||||
|
||||
const billlines = values.billlines.map((b) => {
|
||||
b.applicable_taxes.federal = false;
|
||||
return b;
|
||||
});
|
||||
form.setFieldsValue({billlines});
|
||||
const billlines = values.billlines.map((b) => {
|
||||
b.applicable_taxes.federal = false;
|
||||
return b;
|
||||
});
|
||||
form.setFieldsValue({ billlines });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -368,13 +369,17 @@ export function BillFormComponent({
|
||||
)}
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow>
|
||||
<Form.Item
|
||||
span={3}
|
||||
label={t("bills.fields.federal_tax_rate")}
|
||||
name="federal_tax_rate"
|
||||
>
|
||||
<CurrencyInput min={0} disabled={disabled}/>
|
||||
</Form.Item>
|
||||
{
|
||||
InstanceRenderManager({imex:
|
||||
<Form.Item
|
||||
span={3}
|
||||
label={t("bills.fields.federal_tax_rate")}
|
||||
name="federal_tax_rate"
|
||||
>
|
||||
<CurrencyInput min={0} disabled={disabled} />
|
||||
</Form.Item> })
|
||||
|
||||
}
|
||||
<Form.Item
|
||||
span={3}
|
||||
label={t("bills.fields.state_tax_rate")}
|
||||
@@ -382,22 +387,26 @@ export function BillFormComponent({
|
||||
>
|
||||
<CurrencyInput min={0} disabled={disabled}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
span={3}
|
||||
label={t("bills.fields.local_tax_rate")}
|
||||
name="local_tax_rate"
|
||||
>
|
||||
<CurrencyInput min={0}/>
|
||||
</Form.Item>
|
||||
{bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? (
|
||||
<Form.Item
|
||||
span={2}
|
||||
label={t("bills.labels.federal_tax_exempt")}
|
||||
name="federal_tax_exempt"
|
||||
>
|
||||
<Switch onChange={handleFederalTaxExemptSwitchToggle}/>
|
||||
</Form.Item>
|
||||
) : null}
|
||||
{
|
||||
InstanceRenderManager({imex: <>
|
||||
<Form.Item
|
||||
span={3}
|
||||
label={t("bills.fields.local_tax_rate")}
|
||||
name="local_tax_rate"
|
||||
>
|
||||
<CurrencyInput min={0} />
|
||||
</Form.Item>
|
||||
{bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? (
|
||||
<Form.Item
|
||||
span={2}
|
||||
label={t("bills.labels.federal_tax_exempt")}
|
||||
name="federal_tax_exempt"
|
||||
>
|
||||
<Switch onChange={handleFederalTaxExemptSwitchToggle} />
|
||||
</Form.Item>
|
||||
) : null}
|
||||
</>})
|
||||
}
|
||||
<Form.Item shouldUpdate span={13}>
|
||||
{() => {
|
||||
const values = form.getFieldsValue([
|
||||
@@ -423,21 +432,25 @@ export function BillFormComponent({
|
||||
value={totals.subtotal.toFormat()}
|
||||
precision={2}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("bills.labels.federal_tax")}
|
||||
value={totals.federalTax.toFormat()}
|
||||
precision={2}
|
||||
/>
|
||||
{
|
||||
InstanceRenderManager({imex: <Statistic
|
||||
title={t("bills.labels.federal_tax")}
|
||||
value={totals.federalTax.toFormat()}
|
||||
precision={2}
|
||||
/> })
|
||||
}
|
||||
<Statistic
|
||||
title={t("bills.labels.state_tax")}
|
||||
value={totals.stateTax.toFormat()}
|
||||
precision={2}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("bills.labels.local_tax")}
|
||||
value={totals.localTax.toFormat()}
|
||||
precision={2}
|
||||
/>
|
||||
{
|
||||
InstanceRenderManager({imex: <Statistic
|
||||
title={t("bills.labels.local_tax")}
|
||||
value={totals.localTax.toFormat()}
|
||||
precision={2}
|
||||
/>})
|
||||
}
|
||||
<Statistic
|
||||
title={t("bills.labels.entered_total")}
|
||||
value={totals.enteredTotal.toFormat()}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {DeleteFilled, DollarCircleFilled} from "@ant-design/icons";
|
||||
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||
import {Button, Form, Input, InputNumber, Select, Space, Switch, Table, Tooltip,} from "antd";
|
||||
import {Button, Checkbox, Form, Input, InputNumber, Select, Space, Switch, Table, Tooltip,} from "antd";
|
||||
import React from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
@@ -10,6 +10,7 @@ import CiecaSelect from "../../utils/Ciecaselect";
|
||||
import BillLineSearchSelect from "../bill-line-search-select/bill-line-search-select.component";
|
||||
import BilllineAddInventory from "../billline-add-inventory/billline-add-inventory.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
@@ -32,9 +33,9 @@ export function BillEnterModalLinesComponent({
|
||||
const {t} = useTranslation();
|
||||
const {setFieldsValue, getFieldsValue, getFieldValue} = form;
|
||||
|
||||
const {treatments: {Simple_Inventory}} = useSplitTreatments({
|
||||
const {treatments: {Simple_Inventory, Enhanced_Payroll}} = useSplitTreatments({
|
||||
attributes: {},
|
||||
names: ["Simple_Inventory"],
|
||||
names: ["Simple_Inventory", "Enhanced_Payroll"],
|
||||
splitKey: bodyshop && bodyshop.imexshopid,
|
||||
});
|
||||
|
||||
@@ -87,6 +88,7 @@ export function BillEnterModalLinesComponent({
|
||||
line_desc: opt.line_desc,
|
||||
quantity: opt.part_qty || 1,
|
||||
actual_price: opt.cost,
|
||||
original_actual_price: opt.cost,
|
||||
cost_center: opt.part_type
|
||||
? bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid
|
||||
? opt.part_type !== "PAE"
|
||||
@@ -213,6 +215,46 @@ export function BillEnterModalLinesComponent({
|
||||
}}
|
||||
/>
|
||||
),
|
||||
additional: (record, index) => (
|
||||
InstanceRenderManager({rome: <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>
|
||||
//Do not need to set for promanager as it will default to Rome.
|
||||
})
|
||||
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("billlines.fields.actual_cost"),
|
||||
@@ -354,7 +396,7 @@ export function BillEnterModalLinesComponent({
|
||||
},
|
||||
formInput: (record, index) => <Switch disabled={disabled}/>,
|
||||
additional: (record, index) => (
|
||||
<Form.Item shouldUpdate style={{display: "inline-block"}}>
|
||||
<Form.Item shouldUpdate noStyle style={{display: "inline-block"}}>
|
||||
{() => {
|
||||
const price = getFieldValue([
|
||||
"billlines",
|
||||
@@ -369,12 +411,34 @@ export function BillEnterModalLinesComponent({
|
||||
"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"]))
|
||||
return (
|
||||
<div>
|
||||
{Enhanced_Payroll.treatment === "on" ?
|
||||
<Space>
|
||||
{t("joblines.fields.assigned_team", {
|
||||
name: employeeTeamName?.name,
|
||||
})}
|
||||
{`${jobline.mod_lb_hrs} units/${t(
|
||||
`joblines.fields.lbr_types.${jobline.mod_lbr_ty}`
|
||||
)}`}
|
||||
</Space> :
|
||||
null}
|
||||
|
||||
<Form.Item
|
||||
label={t("joblines.fields.mod_lbr_ty")}
|
||||
key={`${index}modlbrty`}
|
||||
initialValue={jobline ? jobline.mod_lbr_ty : null}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
@@ -428,22 +492,44 @@ export function BillEnterModalLinesComponent({
|
||||
</Select.Option>
|
||||
</Select>
|
||||
</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>
|
||||
{price &&
|
||||
adjustmentRate &&
|
||||
`${(price / adjustmentRate).toFixed(1)} hrs`}
|
||||
{Enhanced_Payroll.treatment === "on" ? (
|
||||
<Form.Item
|
||||
label={t("billlines.labels.mod_lbr_adjustment")}
|
||||
name={[record.name, "lbr_adjustment", "mod_lb_hrs"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber
|
||||
precision={5}
|
||||
min={0.01}
|
||||
max={jobline ? jobline.mod_lb_hrs : 0}
|
||||
/>
|
||||
</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>
|
||||
);
|
||||
return <></>;
|
||||
@@ -451,22 +537,24 @@ export function BillEnterModalLinesComponent({
|
||||
</Form.Item>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("billlines.fields.federal_tax_applicable"),
|
||||
dataIndex: "applicable_taxes.federal",
|
||||
editable: true,
|
||||
|
||||
formItemProps: (field) => {
|
||||
return {
|
||||
key: `${field.index}fedtax`,
|
||||
valuePropName: "checked",
|
||||
initialValue:
|
||||
form.getFieldValue("federal_tax_exempt") === true ? false : true,
|
||||
name: [field.name, "applicable_taxes", "federal"],
|
||||
};
|
||||
},
|
||||
formInput: (record, index) => <Switch disabled={disabled}/>,
|
||||
},
|
||||
|
||||
...InstanceRenderManager({imex: { title: t("billlines.fields.federal_tax_applicable"),
|
||||
dataIndex: "applicable_taxes.federal",
|
||||
editable: true,
|
||||
|
||||
formItemProps: (field) => {
|
||||
return {
|
||||
key: `${field.index}fedtax`,
|
||||
valuePropName: "checked",
|
||||
// initialValue: true,
|
||||
name: [field.name, "applicable_taxes", "federal"],
|
||||
};
|
||||
},
|
||||
formInput: (record, index) => <Switch disabled={disabled} />,}})
|
||||
|
||||
|
||||
,
|
||||
{
|
||||
title: t("billlines.fields.state_tax_applicable"),
|
||||
dataIndex: "applicable_taxes.state",
|
||||
@@ -481,20 +569,22 @@ export function BillEnterModalLinesComponent({
|
||||
},
|
||||
formInput: (record, index) => <Switch disabled={disabled}/>,
|
||||
},
|
||||
{
|
||||
|
||||
...InstanceRenderManager({imex: {
|
||||
title: t("billlines.fields.local_tax_applicable"),
|
||||
dataIndex: "applicable_taxes.local",
|
||||
editable: true,
|
||||
|
||||
formItemProps: (field) => {
|
||||
return {
|
||||
key: `${field.index}localtax`,
|
||||
valuePropName: "checked",
|
||||
name: [field.name, "applicable_taxes", "local"],
|
||||
};
|
||||
return {
|
||||
key: `${field.index}localtax`,
|
||||
valuePropName: "checked",
|
||||
name: [field.name, "applicable_taxes", "local"],
|
||||
};
|
||||
},
|
||||
formInput: (record, index) => <Switch disabled={disabled}/>,
|
||||
},
|
||||
formInput: (record, index) => <Switch disabled={disabled} />,
|
||||
}})
|
||||
,
|
||||
{
|
||||
title: t("general.labels.actions"),
|
||||
|
||||
@@ -619,7 +709,7 @@ const EditableCell = ({
|
||||
if (additional)
|
||||
return (
|
||||
<td {...restProps}>
|
||||
<Space size="small">
|
||||
<div size="small">
|
||||
<Form.Item
|
||||
name={dataIndex}
|
||||
labelCol={{span: 0}}
|
||||
@@ -628,7 +718,7 @@ const EditableCell = ({
|
||||
{(formInput && formInput(record, record.name)) || children}
|
||||
</Form.Item>
|
||||
{additional && additional(record, record.name)}
|
||||
</Space>
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
if (wrapper)
|
||||
|
||||
@@ -19,14 +19,14 @@ export const CalculateBillTotal = (invoice) => {
|
||||
}).multiply(i.quantity || 1);
|
||||
|
||||
subtotal = subtotal.add(itemTotal);
|
||||
if (i.applicable_taxes.federal) {
|
||||
if (i.applicable_taxes?.federal) {
|
||||
federalTax = federalTax.add(
|
||||
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));
|
||||
if (i.applicable_taxes.local)
|
||||
if (i.applicable_taxes?.local)
|
||||
localTax = localTax.add(itemTotal.percentage(local_tax_rate || 0));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {Select} from "antd";
|
||||
import React, {forwardRef} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import InstanceRenderMgr from '../../utils/instanceRenderMgr';
|
||||
|
||||
//To be used as a form element only.
|
||||
const {Option} = Select;
|
||||
@@ -67,6 +68,22 @@ const BillLineSearchSelect = (
|
||||
item.oem_partno ? ` - ${item.oem_partno}` : ""
|
||||
}${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim()}
|
||||
</span>
|
||||
{
|
||||
InstanceRenderMgr
|
||||
(
|
||||
{
|
||||
rome: 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"}}>
|
||||
{item.act_price
|
||||
? `$${item.act_price && item.act_price.toFixed(2)}`
|
||||
|
||||
@@ -4,11 +4,7 @@ import React, {useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
|
||||
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
bodyshop: selectBodyshop,
|
||||
|
||||
@@ -187,7 +187,7 @@ export const uploadToCloudinary = async (
|
||||
message: JSON.stringify(documentInsert.errors),
|
||||
}),
|
||||
});
|
||||
return;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -6,14 +6,8 @@ import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {logImEXEvent} from "../../firebase/firebase.utils";
|
||||
import {toggleEmailOverlayVisible} from "../../redux/email/email.actions";
|
||||
import {
|
||||
selectEmailConfig,
|
||||
selectEmailVisible,
|
||||
} from "../../redux/email/email.selectors.js";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import {selectEmailConfig, selectEmailVisible,} from "../../redux/email/email.selectors.js";
|
||||
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
|
||||
import RenderTemplate from "../../utils/RenderTemplate";
|
||||
import {EmailSettings} from "../../utils/TemplateConstants";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {Select, Space, Tag} from "antd";
|
||||
import React, {forwardRef} from "react";
|
||||
import React from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
|
||||
const {Option} = Select;
|
||||
//To be used as a form element only.
|
||||
|
||||
const EmployeeSearchSelect = ({options, ...props}, ref) => {
|
||||
const EmployeeSearchSelect = ({options, ...props}) => {
|
||||
const {t} = useTranslation();
|
||||
|
||||
return (
|
||||
@@ -40,4 +40,4 @@ const EmployeeSearchSelect = ({options, ...props}, ref) => {
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
export default forwardRef(EmployeeSearchSelect);
|
||||
export default EmployeeSearchSelect;
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
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);
|
||||
@@ -6,6 +6,7 @@ import * as Sentry from "@sentry/react";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
@@ -37,22 +38,24 @@ class ErrorBoundary extends React.Component {
|
||||
}
|
||||
|
||||
handleErrorSubmit = () => {
|
||||
window.$crisp.push([
|
||||
"do",
|
||||
"message:send",
|
||||
[
|
||||
"text",
|
||||
`I hit the following error: \n\n
|
||||
${this.state.error.message}\n\n
|
||||
${this.state.error.stack}\n\n
|
||||
URL:${window.location} as ${this.props.currentUser.email} for ${
|
||||
InstanceRenderManager({executeFunction:true, imex: () => {
|
||||
window.$crisp.push([
|
||||
"do",
|
||||
"message:send",
|
||||
[
|
||||
"text",
|
||||
`I hit the following error: \n\n
|
||||
${this.state.error.message}\n\n
|
||||
${this.state.error.stack}\n\n
|
||||
URL:${window.location} as ${this.props.currentUser.email} for ${
|
||||
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**
|
||||
|
||||
// ----
|
||||
|
||||
@@ -1,9 +1,27 @@
|
||||
import Dinero from "dinero.js";
|
||||
import React, {forwardRef} from "react";
|
||||
|
||||
const ReadOnlyFormItem = ({value, type = "text", onChange}, ref) => {
|
||||
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))
|
||||
});
|
||||
|
||||
const ReadOnlyFormItem = (
|
||||
{bodyshop, value, type = "text", onChange},
|
||||
ref
|
||||
) => {
|
||||
if (!value) return null;
|
||||
switch (type) {
|
||||
case "employee":
|
||||
const emp = bodyshop.employees.find((e) => e.id === value);
|
||||
return `${emp?.first_name} ${emp?.last_name}`;
|
||||
|
||||
case "text":
|
||||
return <div>{value}</div>;
|
||||
case "currency":
|
||||
@@ -14,4 +32,8 @@ const ReadOnlyFormItem = ({value, type = "text", onChange}, ref) => {
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
};
|
||||
export default forwardRef(ReadOnlyFormItem);
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(forwardRef(ReadOnlyFormItem));
|
||||
|
||||
@@ -5,9 +5,7 @@ import React, {useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {Link, useNavigate} from "react-router-dom";
|
||||
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||
import OwnerNameDisplay, {
|
||||
OwnerNameDisplayFunction,
|
||||
} from "../owner-name-display/owner-name-display.component";
|
||||
import OwnerNameDisplay, {OwnerNameDisplayFunction,} from "../owner-name-display/owner-name-display.component";
|
||||
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
|
||||
|
||||
export default function GlobalSearchOs() {
|
||||
|
||||
@@ -7,9 +7,7 @@ import {Link, useNavigate} from "react-router-dom";
|
||||
import {GLOBAL_SEARCH_QUERY} from "../../graphql/search.queries";
|
||||
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import OwnerNameDisplay, {
|
||||
OwnerNameDisplayFunction,
|
||||
} from "../owner-name-display/owner-name-display.component";
|
||||
import OwnerNameDisplay, {OwnerNameDisplayFunction,} from "../owner-name-display/owner-name-display.component";
|
||||
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
|
||||
|
||||
export default function GlobalSearch() {
|
||||
|
||||
@@ -43,6 +43,7 @@ import {signOutStart} from "../../redux/user/user.actions";
|
||||
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
|
||||
import {FiLogOut} from "react-icons/fi";
|
||||
import {checkBeta, handleBeta, setBeta} from "../../utils/betaHandler";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
@@ -174,8 +175,20 @@ function Header({
|
||||
key: 'timetickets',
|
||||
icon: <FieldTimeOutlined/>,
|
||||
label: (<Link to="/manage/timetickets">{t("menus.header.timetickets")}</Link>)
|
||||
},
|
||||
{
|
||||
});
|
||||
|
||||
if (bodyshop?.md_tasks_presets?.use_approvals) {
|
||||
accountingChildren.push({
|
||||
key: 'ttapprovals',
|
||||
icon: <FieldTimeOutlined/>,
|
||||
label:
|
||||
<Link to="/manage/ttapprovals">
|
||||
{t("menus.header.ttapprovals")}
|
||||
</Link>
|
||||
});
|
||||
}
|
||||
|
||||
accountingChildren.push({
|
||||
key: 'entertimetickets',
|
||||
icon: <Icon component={GiPlayerTime}/>,
|
||||
label: t("menus.header.entertimeticket"),
|
||||
@@ -421,17 +434,19 @@ function Header({
|
||||
icon: <Icon component={QuestionCircleFilled}/>,
|
||||
label: t("menus.header.help"),
|
||||
onClick: () => {
|
||||
window.open("https://help.imex.online/", "_blank");
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'rescue',
|
||||
icon: <Icon component={CarFilled}/>,
|
||||
label: t("menus.header.rescueme"),
|
||||
onClick: () => {
|
||||
window.open("https://imexrescue.com/", "_blank");
|
||||
window.open(InstanceRenderManager({imex:"https://help.imex.online/",rome: "https://rometech.com//", promanager:"https://web-est.com" })
|
||||
|
||||
, "_blank");
|
||||
}
|
||||
},
|
||||
// {
|
||||
// key: 'rescue',
|
||||
// icon: <Icon component={CarFilled}/>,
|
||||
// label: t("menus.header.rescueme"),
|
||||
// onClick: () => {
|
||||
// window.open("https://imexrescue.com/", "_blank");
|
||||
// }
|
||||
// },
|
||||
{
|
||||
key: 'shiftclock',
|
||||
icon: <Icon component={GiPlayerTime}/>,
|
||||
@@ -487,9 +502,9 @@ function Header({
|
||||
key: 'beta-switch',
|
||||
style: {marginLeft: 'auto'},
|
||||
label: (
|
||||
<Tooltip title="A more modern ImEX Online is ready for you to try! You can switch back at any time.">
|
||||
<Tooltip title={`A more modern ${InstanceRenderManager({imex: 'ImEX Online', rome: "Rome Online"})} is ready for you to try! You can switch back at any time.`}>
|
||||
<InfoCircleOutlined/>
|
||||
<span style={{marginRight: 8}}>Try the new ImEX Online</span>
|
||||
<span style={{marginRight: 8}}>Try the new app</span>
|
||||
<Switch
|
||||
checked={betaSwitch}
|
||||
onChange={betaSwitchChange}
|
||||
|
||||
@@ -1,27 +1,14 @@
|
||||
import {AlertFilled} from "@ant-design/icons";
|
||||
import {
|
||||
Button,
|
||||
Divider,
|
||||
Dropdown,
|
||||
Form,
|
||||
Input,
|
||||
notification,
|
||||
Popover,
|
||||
Select,
|
||||
Space,
|
||||
} from "antd";
|
||||
import {Button, Divider, Dropdown, Form, Input, notification, Popover, Select, Space,} from "antd";
|
||||
import parsePhoneNumber from "libphonenumber-js";
|
||||
import dayjs from "../../utils/day";
|
||||
import queryString from "query-string";
|
||||
import React, {useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {Link, useNavigate, useLocation} from "react-router-dom";
|
||||
import {Link, useLocation, useNavigate} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {
|
||||
openChatByPhone,
|
||||
setMessage,
|
||||
} from "../../redux/messaging/messaging.actions";
|
||||
import {openChatByPhone, setMessage,} from "../../redux/messaging/messaging.actions";
|
||||
import {setModalContext} from "../../redux/modals/modals.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
|
||||
@@ -39,7 +39,8 @@ export default function JobBillsTotalComponent({
|
||||
);
|
||||
|
||||
if (pol.cm_received === null) {
|
||||
return; // Skip this calculation for bills posted prior to the CNR change.
|
||||
return; //TODO:AIO This was previously removed. Check if functionality impacted.
|
||||
// Skip this calculation for bills posted prior to the CNR change.
|
||||
} else {
|
||||
if (pol.cm_received === false) {
|
||||
totalReturnsMarkedNotReceived = totalReturnsMarkedNotReceived.add(
|
||||
@@ -87,7 +88,8 @@ export default function JobBillsTotalComponent({
|
||||
const totalPartsSublet = Dinero(totals.parts.parts.total)
|
||||
.add(Dinero(totals.parts.sublets.total))
|
||||
.add(Dinero(totals.additional.shipping))
|
||||
.add(Dinero(totals.additional.towing));
|
||||
.add(Dinero(totals.additional.towing))
|
||||
.add(Dinero(totals.additional.additionalCosts)); //TODO:AIO Additional costs were captured for Rome, but not imex. This may need to be evaluated?
|
||||
|
||||
const discrepancy = totalPartsSublet.subtract(billTotals);
|
||||
|
||||
|
||||
@@ -1,48 +1,53 @@
|
||||
import {Button, notification} from "antd";
|
||||
import Axios from "axios";
|
||||
import React, {useState} from "react";
|
||||
import {useMutation} from "@apollo/client";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {UPDATE_JOB} from "../../graphql/jobs.queries";
|
||||
import Dinero from "dinero.js";
|
||||
|
||||
export default function JobCalculateTotals({job, disabled}) {
|
||||
export default function JobCalculateTotals({job, disabled, refetch}) {
|
||||
const {t} = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [updateJob] = useMutation(UPDATE_JOB);
|
||||
|
||||
const handleCalculate = async () => {
|
||||
setLoading(true);
|
||||
const newTotals = (
|
||||
await Axios.post("/job/totals", {
|
||||
job: job,
|
||||
})
|
||||
).data;
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
const result = await updateJob({
|
||||
refetchQueries: ["GET_JOB_BY_PK"],
|
||||
awaitRefetchQueries: true,
|
||||
variables: {
|
||||
jobId: job.id,
|
||||
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 {
|
||||
await Axios.post("/job/totalsssu", {
|
||||
id: job.id,
|
||||
});
|
||||
|
||||
if (refetch) refetch();
|
||||
// const result = await updateJob({
|
||||
// refetchQueries: ["GET_JOB_BY_PK"],
|
||||
// awaitRefetchQueries: true,
|
||||
// variables: {
|
||||
// jobId: job.id,
|
||||
// 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 {
|
||||
// notification["error"]({
|
||||
// message: t("jobs.errors.updating", {
|
||||
// error: JSON.stringify(result.errors),
|
||||
// }),
|
||||
// });
|
||||
// }
|
||||
} catch (error) {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.updating", {
|
||||
error: JSON.stringify(result.errors),
|
||||
error: JSON.stringify(error),
|
||||
}),
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -8,17 +8,11 @@ import {connect} from "react-redux";
|
||||
import {useLocation, useNavigate, useParams} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {logImEXEvent} from "../../../../firebase/firebase.utils";
|
||||
import {
|
||||
MARK_APPOINTMENT_ARRIVED,
|
||||
MARK_LATEST_APPOINTMENT_ARRIVED,
|
||||
} from "../../../../graphql/appointments.queries";
|
||||
import {MARK_APPOINTMENT_ARRIVED, MARK_LATEST_APPOINTMENT_ARRIVED,} from "../../../../graphql/appointments.queries";
|
||||
import {UPDATE_JOB} from "../../../../graphql/jobs.queries";
|
||||
import {UPDATE_OWNER} from "../../../../graphql/owners.queries";
|
||||
import {insertAuditTrail} from "../../../../redux/application/application.actions";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../../../redux/user/user.selectors";
|
||||
import {selectBodyshop, selectCurrentUser,} from "../../../../redux/user/user.selectors";
|
||||
import AuditTrailMapping from "../../../../utils/AuditTrailMappings";
|
||||
import ConfigFormComponents from "../../../config-form-components/config-form-components.component";
|
||||
import DateTimePicker from "../../../form-date-time-picker/form-date-time-picker.component";
|
||||
|
||||
@@ -4,12 +4,8 @@ import React, {useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {useParams} from "react-router-dom";
|
||||
import {logImEXEvent} from "../../../../firebase/firebase.utils";
|
||||
import {
|
||||
GenerateDocument,
|
||||
GenerateDocuments,
|
||||
} from "../../../../utils/RenderTemplate";
|
||||
import {GenerateDocument, GenerateDocuments,} from "../../../../utils/RenderTemplate";
|
||||
import {TemplateList} from "../../../../utils/TemplateConstants";
|
||||
|
||||
const TemplateListGenerated = TemplateList();
|
||||
|
||||
export default function JobIntakeTemplateList({templates}) {
|
||||
|
||||
@@ -7,9 +7,9 @@ import {connect} from "react-redux";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {UPDATE_JOB_LINES_IOU} from "../../graphql/jobs-lines.queries";
|
||||
import {selectTechnician} from "../../redux/tech/tech.selectors";
|
||||
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
|
||||
import {CreateIouForJob} from "../jobs-detail-header-actions/jobs-detail-header-actions.duplicate.util";
|
||||
import {selectTechnician} from "../../redux/tech/tech.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -22,13 +22,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobCreateIOU);
|
||||
|
||||
export function JobCreateIOU({
|
||||
bodyshop,
|
||||
currentUser,
|
||||
job,
|
||||
selectedJobLines,
|
||||
technician,
|
||||
}) {
|
||||
export function JobCreateIOU({bodyshop, currentUser, job, selectedJobLines, technician}) {
|
||||
const {t} = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const client = useApolloClient();
|
||||
@@ -88,19 +82,13 @@ export function JobCreateIOU({
|
||||
title={t("jobs.labels.createiouwarning")}
|
||||
onConfirm={handleCreateIou}
|
||||
disabled={
|
||||
!selectedJobLines ||
|
||||
selectedJobLines.length === 0 ||
|
||||
!job.converted ||
|
||||
technician
|
||||
!selectedJobLines || selectedJobLines.length === 0 || !job.converted || technician
|
||||
}
|
||||
>
|
||||
<Button
|
||||
loading={loading}
|
||||
disabled={
|
||||
!selectedJobLines ||
|
||||
selectedJobLines.length === 0 ||
|
||||
!job.converted ||
|
||||
technician
|
||||
!selectedJobLines || selectedJobLines.length === 0 || !job.converted || technician
|
||||
}
|
||||
>
|
||||
{t("jobs.actions.createiou")}
|
||||
|
||||
@@ -32,9 +32,9 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
});
|
||||
|
||||
const span = {
|
||||
sm: {span: 24},
|
||||
md: {span: 12},
|
||||
lg: {span: 8},
|
||||
lg: {span: 24},
|
||||
xl: {span: 12},
|
||||
xxl: {span: 8},
|
||||
};
|
||||
|
||||
export function JobDetailCards({bodyshop, setPrintCenterContext}) {
|
||||
@@ -137,12 +137,6 @@ export function JobDetailCards({bodyshop, setPrintCenterContext}) {
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...span}>
|
||||
<JobDetailCardsPartsComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...span}>
|
||||
<JobDetailCardsNotesComponent
|
||||
loading={loading}
|
||||
@@ -163,6 +157,12 @@ export function JobDetailCards({bodyshop, setPrintCenterContext}) {
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<JobDetailCardsPartsComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
) : null}
|
||||
|
||||
@@ -1,16 +1,120 @@
|
||||
import {Table} from "antd";
|
||||
import React from "react";
|
||||
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 CardTemplate from "./job-detail-cards.template.component";
|
||||
|
||||
export default function JobDetailCardsPartsComponent({loading, data}) {
|
||||
import {connect} from "react-redux";
|
||||
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 {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 (
|
||||
<div>
|
||||
<CardTemplate loading={loading} title={t("jobs.labels.cards.parts")}>
|
||||
<PartsStatusPie joblines_status={joblines_status}/>
|
||||
<Table
|
||||
key="id"
|
||||
columns={columns}
|
||||
dataSource={data ? data.joblines : []}
|
||||
/>
|
||||
</CardTemplate>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -8,7 +8,19 @@ import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import {DateFormatter} from "../../utils/DateFormatter";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
|
||||
export default function JobLinesExpander({jobline, jobid}) {
|
||||
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)(JobLinesExpander);
|
||||
|
||||
export function JobLinesExpander({jobline, jobid, bodyshop}) {
|
||||
const {t} = useTranslation();
|
||||
const {loading, error, data} = useQuery(GET_JOB_LINE_ORDERS, {
|
||||
fetchPolicy: "network-only",
|
||||
@@ -23,7 +35,7 @@ export default function JobLinesExpander({jobline, jobid}) {
|
||||
|
||||
return (
|
||||
<Row>
|
||||
<Col md={24} lg={12}>
|
||||
<Col md={24} lg={8}>
|
||||
<Typography.Title level={4}>
|
||||
{t("parts_orders.labels.parts_orders")}
|
||||
</Typography.Title>
|
||||
@@ -52,7 +64,7 @@ export default function JobLinesExpander({jobline, jobid}) {
|
||||
]
|
||||
}
|
||||
/> </Col>
|
||||
<Col md={24} lg={12}>
|
||||
<Col md={24} lg={8}>
|
||||
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
|
||||
<Timeline
|
||||
items={
|
||||
@@ -90,6 +102,37 @@ export default function JobLinesExpander({jobline, jobid}) {
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
<Col md={24} lg={8}>
|
||||
<Typography.Title level={4}>
|
||||
{t("parts_dispatch.labels.parts_dispatch")}
|
||||
</Typography.Title>
|
||||
<Timeline items={
|
||||
data.parts_dispatch_lines.length > 0 ? (
|
||||
data.parts_dispatch_lines.map((line) => ({
|
||||
key: line.id,
|
||||
children: (
|
||||
<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>
|
||||
)
|
||||
}))
|
||||
) : ({
|
||||
key: 'dispatch-lines',
|
||||
children: t("parts_orders.labels.notyetordered"),
|
||||
})
|
||||
}/>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
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";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
|
||||
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 || InstanceRenderManager({imex:false, rome:true})} 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>
|
||||
);
|
||||
}
|
||||
@@ -21,7 +21,6 @@ import {selectJobReadOnly} from "../../redux/application/application.selectors";
|
||||
import {setModalContext} from "../../redux/modals/modals.actions";
|
||||
import {selectTechnician} from "../../redux/tech/tech.selectors";
|
||||
import {onlyUnique} from "../../utils/arrayHelper";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import {alphaSort} from "../../utils/sorters";
|
||||
import JobLineLocationPopup from "../job-line-location-popup/job-line-location-popup.component";
|
||||
import JobLineNotePopup from "../job-line-note-popup/job-line-note-popup.component";
|
||||
@@ -30,13 +29,18 @@ import JobLinesBillRefernece from "../job-lines-bill-reference/job-lines-bill-re
|
||||
// import AllocationsAssignmentContainer from "../allocations-assignment/allocations-assignment.container";
|
||||
// import AllocationsBulkAssignmentContainer from "../allocations-bulk-assignment/allocations-bulk-assignment.container";
|
||||
// import AllocationsEmployeeLabelContainer from "../allocations-employee-label/allocations-employee-label.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 JobSendPartPriceChangeComponent from "../job-send-parts-price-change/job-send-parts-price-change.component";
|
||||
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
|
||||
import JobLinesExpander from "./job-lines-expander.component";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import dayjs from "../../utils/day";
|
||||
import JobLineConvertToLabor from "../job-line-convert-to-labor/job-line-convert-to-labor.component";
|
||||
import JobLinesPartPriceChange from "./job-lines-part-price-change.component";
|
||||
import JoblineTeamAssignment from "../job-line-team-assignment/job-line-team-assignmnent.component";
|
||||
import JobLineDispatchButton from "../job-line-dispatch-button/job-line-dispatch-button.component";
|
||||
import JobLineBulkAssignComponent from "../job-line-bulk-assign/job-line-bulk-assign.component";
|
||||
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -68,6 +72,11 @@ export function JobLinesComponent({
|
||||
setBillEnterContext,
|
||||
}) {
|
||||
const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK);
|
||||
const {treatments: {Enhanced_Payroll}} = useSplitTreatments({
|
||||
attributes: {},
|
||||
names: ["Enhanced_Payroll"],
|
||||
splitKey: bodyshop.imexshopid,
|
||||
});
|
||||
|
||||
const [selectedLines, setSelectedLines] = useState([]);
|
||||
const [state, setState] = useState({
|
||||
@@ -113,10 +122,21 @@ export function JobLinesComponent({
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "oem_partno" && state.sortedInfo.order,
|
||||
ellipsis: true,
|
||||
render: (text, record) =>
|
||||
`${record.oem_partno || ""} ${
|
||||
record.alt_partno ? `(${record.alt_partno})` : ""
|
||||
}`.trim(),
|
||||
onCell: (record) => ({
|
||||
className: record.manual_line && "job-line-manual",
|
||||
style: {
|
||||
...(record.parts_dispatch_lines[0]?.accepted_at
|
||||
? {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"),
|
||||
@@ -212,20 +232,7 @@ export function JobLinesComponent({
|
||||
state.sortedInfo.columnKey === "act_price" && state.sortedInfo.order,
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<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>
|
||||
<JobLinesPartPriceChange line={record} job={job} refetch={refetch}/>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -278,6 +285,23 @@ export function JobLinesComponent({
|
||||
state.sortedInfo.columnKey === "line_ind" && state.sortedInfo.order,
|
||||
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"),
|
||||
dataIndex: "notes",
|
||||
@@ -396,7 +420,11 @@ export function JobLinesComponent({
|
||||
setSelectedLines((selectedLines) =>
|
||||
_.uniq([
|
||||
...selectedLines,
|
||||
...jobLines.filter((item) => markedTypes.includes(item.part_type)),
|
||||
...jobLines.filter(
|
||||
(item) =>
|
||||
markedTypes.includes(item.part_type) ||
|
||||
markedTypes.includes(item.mod_lbr_ty)
|
||||
),
|
||||
])
|
||||
);
|
||||
}
|
||||
@@ -410,6 +438,21 @@ export function JobLinesComponent({
|
||||
{key: "PAL", label: t("joblines.fields.part_types.PAL")},
|
||||
{key: "PAS", label: t("joblines.fields.part_types.PAS")},
|
||||
{type: 'divider'},
|
||||
{key: "LAA", label: t("joblines.fields.lbr_types.LAA")},
|
||||
{key: "LAB", label: t("joblines.fields.lbr_types.LAB")},
|
||||
{key: "LAD", label: t("joblines.fields.part_types.LAD")},
|
||||
{key: "LAE", label: t("joblines.fields.part_types.LAE")},
|
||||
{key: "LAF", label: t("joblines.fields.part_types.LAF")},
|
||||
{key: "LAG", label: t("joblines.fields.part_types.LAG")},
|
||||
{key: "LAM", label: t("joblines.fields.part_types.LAM")},
|
||||
{key: "LAR", label: t("joblines.fields.part_types.LAR")},
|
||||
{key: "LAS", label: t("joblines.fields.part_types.LAS")},
|
||||
{key: "LAU", label: t("joblines.fields.part_types.LAU")},
|
||||
{key: "LA1", label: t("joblines.fields.part_types.LA1")},
|
||||
{key: "LA2", label: t("joblines.fields.part_types.LA2")},
|
||||
{key: "LA3", label: t("joblines.fields.part_types.LA3")},
|
||||
{key: "LA4", label: t("joblines.fields.part_types.LA4")},
|
||||
{type: 'divider'},
|
||||
{key: "clear", label: t("general.labels.clear")},
|
||||
]
|
||||
};
|
||||
@@ -433,6 +476,18 @@ export function JobLinesComponent({
|
||||
</Space>
|
||||
</Tag>
|
||||
)}
|
||||
<JobLineDispatchButton
|
||||
selectedLines={selectedLines}
|
||||
setSelectedLines={setSelectedLines}
|
||||
job={job}
|
||||
/>
|
||||
{Enhanced_Payroll.treatment === "on" && (
|
||||
<JobLineBulkAssignComponent
|
||||
selectedLines={selectedLines}
|
||||
setSelectedLines={setSelectedLines}
|
||||
job={job}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
disabled={
|
||||
(job && !job.converted) ||
|
||||
@@ -441,15 +496,6 @@ export function JobLinesComponent({
|
||||
technician
|
||||
}
|
||||
onClick={() => {
|
||||
// setPartsOrderContext({
|
||||
// actions: { refetch: refetch },
|
||||
// context: {
|
||||
// jobId: job.id,
|
||||
// job: job,
|
||||
// linesToOrder: selectedLines,
|
||||
// },
|
||||
// });
|
||||
|
||||
setBillEnterContext({
|
||||
actions: {refetch: refetch},
|
||||
context: {
|
||||
@@ -556,6 +602,9 @@ export function JobLinesComponent({
|
||||
>
|
||||
{t("joblines.actions.new")}
|
||||
</Button>
|
||||
{bodyshop.region_config.toLowerCase().startsWith("us") && (
|
||||
<JobSendPartPriceChangeComponent job={job}/>
|
||||
)}
|
||||
<JobCreateIOU job={job} selectedJobLines={selectedLines}/>
|
||||
<Input.Search
|
||||
placeholder={t("general.labels.search")}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
import React, {useState} from "react";
|
||||
|
||||
import {useMutation} from "@apollo/client";
|
||||
import {Button, Form, notification, Popover, Select, Space} 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>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
import React, {useState} from "react";
|
||||
|
||||
import {useMutation} from "@apollo/client";
|
||||
import {Button, Form, notification, Popover, Select, Space} from "antd";
|
||||
import day from "../../utils/day";
|
||||
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: day(),
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import {useMutation} from "@apollo/client";
|
||||
import {notification, Select} from "antd";
|
||||
import {notification, Select, Space} from "antd";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
@@ -77,7 +77,10 @@ export function JobLineLocationPopup({bodyshop, jobline, disabled}) {
|
||||
style={{width: "100%", minHeight: "2rem", cursor: "pointer"}}
|
||||
onClick={() => !disabled && setEditing(true)}
|
||||
>
|
||||
{jobline.location}
|
||||
<Space wrap>
|
||||
{jobline.location}
|
||||
{jobline.parts_dispatch_lines?.length > 0 && "-Disp"}
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
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);
|
||||
@@ -0,0 +1,18 @@
|
||||
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;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import React, {useState} from "react";
|
||||
import JobReconciliationBillsTable from "../job-reconciliation-bills-table/job-reconciliation-bills-table.component";
|
||||
import JobReconciliationPartsTable from "../job-reconciliation-parts-table/job-reconciliation-parts-table.component";
|
||||
import JobReconciliationTotals from "../job-reconciliation-totals/job-reconciliation-totals.component";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
|
||||
export default function JobReconciliationModalComponent({job, bills}) {
|
||||
const jobLineState = useState([]);
|
||||
@@ -17,14 +18,25 @@ export default function JobReconciliationModalComponent({job, bills}) {
|
||||
)
|
||||
.flat() || [];
|
||||
|
||||
const jobLineData = job.joblines.filter(
|
||||
(j) =>
|
||||
(j.part_type !== null && j.part_type !== "PAE") ||
|
||||
(j.line_desc &&
|
||||
j.line_desc.toLowerCase().includes("towing") &&
|
||||
j.lbr_op === "OP13") ||
|
||||
j.db_ref === "936004" //ADD SHIPPING LINE.
|
||||
);
|
||||
const filterFunction = InstanceRenderManager({
|
||||
imex: (j) =>
|
||||
(j.part_type !== null && j.part_type !== "PAE") ||
|
||||
(j.line_desc &&
|
||||
j.line_desc.toLowerCase().includes("towing") &&
|
||||
j.lbr_op === "OP13") ||
|
||||
j.db_ref === "936004", //ADD SHIPPING LINE.
|
||||
rome: (j) =>
|
||||
(j.part_type !== "PAE" &&
|
||||
j.act_price !== 0 &&
|
||||
j.part_qty !== 0) ||
|
||||
j.misc_amt !== 0 ||
|
||||
(j.line_desc &&
|
||||
j.line_desc.toLowerCase().includes("towing") &&
|
||||
j.lbr_op === "OP13") ||
|
||||
j.db_ref === "936004", //ADD SHIPPING LINE.
|
||||
});
|
||||
|
||||
const jobLineData = job.joblines.filter((j) => filterFunction(j));
|
||||
|
||||
return (
|
||||
<div style={{flex: 1, display: "flex", flexDirection: "column"}}>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -67,7 +67,8 @@ export function JobsTotalsTableComponent({jobRO, currentUser, job}) {
|
||||
<JobTotalsTableTotals job={job}/>
|
||||
</Card>
|
||||
</Col>
|
||||
{currentUser.email.includes("@imex.") && (
|
||||
{(currentUser.email.includes("@imex.") ||
|
||||
currentUser.email.includes("@rome.")) && (
|
||||
<Col span={24}>
|
||||
<Card title="DEVELOPMENT USE ONLY">
|
||||
<JobCalculateTotals job={job} disabled={jobRO}/>
|
||||
|
||||
@@ -4,6 +4,7 @@ import React, {useMemo, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import {alphaSort} from "../../utils/sorters";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
|
||||
export default function JobTotalsTableLabor({job}) {
|
||||
const {t} = useTranslation();
|
||||
@@ -103,9 +104,11 @@ export default function JobTotalsTableLabor({job}) {
|
||||
<>
|
||||
<Table.Summary.Row>
|
||||
<Table.Summary.Cell>
|
||||
<strong>{t("jobs.labels.labor_rates_subtotal")}</strong>
|
||||
<strong>
|
||||
{t("jobs.labels.labor_rates_subtotal")}
|
||||
</strong>
|
||||
</Table.Summary.Cell>
|
||||
<Table.Summary.Cell/>
|
||||
<Table.Summary.Cell />
|
||||
<Table.Summary.Cell>
|
||||
{(
|
||||
job.job_totals.rates.mapa.hours +
|
||||
@@ -114,7 +117,9 @@ export default function JobTotalsTableLabor({job}) {
|
||||
</Table.Summary.Cell>
|
||||
<Table.Summary.Cell align="right">
|
||||
<strong>
|
||||
{Dinero(job.job_totals.rates.rates_subtotal).toFormat()}
|
||||
{Dinero(
|
||||
job.job_totals.rates.rates_subtotal
|
||||
).toFormat()}
|
||||
</strong>
|
||||
</Table.Summary.Cell>
|
||||
</Table.Summary.Row>
|
||||
@@ -122,13 +127,26 @@ export default function JobTotalsTableLabor({job}) {
|
||||
<Table.Summary.Cell>
|
||||
<Space>
|
||||
{t("jobs.labels.mapa")}
|
||||
{job.materials &&
|
||||
job.materials.mapa &&
|
||||
job.materials.mapa.cal_maxdlr &&
|
||||
job.materials.mapa.cal_maxdlr > 0 &&
|
||||
t("jobs.labels.threshhold", {
|
||||
amount: job.materials.mapa.cal_maxdlr,
|
||||
})}
|
||||
{InstanceRenderManager({
|
||||
imex:
|
||||
job.materials &&
|
||||
job.materials.mapa &&
|
||||
job.materials.mapa.cal_maxdlr &&
|
||||
job.materials.mapa.cal_maxdlr > 0 &&
|
||||
t("jobs.labels.threshhold", {
|
||||
amount: job.materials.mapa
|
||||
.cal_maxdlr,
|
||||
}),
|
||||
rome:
|
||||
job.materials &&
|
||||
job.materials.MAPA &&
|
||||
job.materials.MAPA.cal_maxdlr !==
|
||||
undefined &&
|
||||
t("jobs.labels.threshhold", {
|
||||
amount: job.materials.MAPA
|
||||
.cal_maxdlr,
|
||||
}),
|
||||
})}
|
||||
</Space>
|
||||
</Table.Summary.Cell>
|
||||
<Table.Summary.Cell align="right">
|
||||
@@ -147,13 +165,24 @@ export default function JobTotalsTableLabor({job}) {
|
||||
<Table.Summary.Cell>
|
||||
<Space wrap>
|
||||
{t("jobs.labels.mash")}
|
||||
{job.materials &&
|
||||
job.materials.mash &&
|
||||
job.materials.mash.cal_maxdlr &&
|
||||
job.materials.mash.cal_maxdlr > 0 &&
|
||||
t("jobs.labels.threshhold", {
|
||||
amount: job.materials.mash.cal_maxdlr,
|
||||
})}
|
||||
{
|
||||
InstanceRenderManager({
|
||||
imex:job.materials &&
|
||||
job.materials.mash &&
|
||||
job.materials.mash.cal_maxdlr &&
|
||||
job.materials.mash.cal_maxdlr > 0 &&
|
||||
t("jobs.labels.threshhold", {
|
||||
amount: job.materials.mash.cal_maxdlr,
|
||||
}),
|
||||
rome: job.materials &&
|
||||
job.materials.MASH &&
|
||||
job.materials.MASH.cal_maxdlr !==
|
||||
undefined &&
|
||||
t("jobs.labels.threshhold", {
|
||||
amount: job.materials.MASH.cal_maxdlr,
|
||||
})
|
||||
})
|
||||
}
|
||||
</Space>
|
||||
</Table.Summary.Cell>
|
||||
<Table.Summary.Cell align="right">
|
||||
@@ -172,11 +201,13 @@ export default function JobTotalsTableLabor({job}) {
|
||||
<Table.Summary.Cell>
|
||||
<strong>{t("jobs.labels.rates_subtotal")}</strong>
|
||||
</Table.Summary.Cell>
|
||||
<Table.Summary.Cell/>
|
||||
<Table.Summary.Cell/>
|
||||
<Table.Summary.Cell />
|
||||
<Table.Summary.Cell />
|
||||
<Table.Summary.Cell align="right">
|
||||
<strong>
|
||||
{Dinero(job.job_totals.rates.subtotal).toFormat()}
|
||||
{Dinero(
|
||||
job.job_totals.rates.subtotal
|
||||
).toFormat()}
|
||||
</strong>
|
||||
</Table.Summary.Cell>
|
||||
</Table.Summary.Row>
|
||||
|
||||
@@ -11,6 +11,22 @@ export default function JobTotalsTableParts({job}) {
|
||||
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(() => {
|
||||
return Object.keys(job.job_totals.parts.parts.list)
|
||||
.filter(
|
||||
@@ -74,11 +90,11 @@ export default function JobTotalsTableParts({job}) {
|
||||
<Table.Summary.Cell>
|
||||
{t("jobs.labels.prt_dsmk_total")}
|
||||
</Table.Summary.Cell>
|
||||
|
||||
<Table.Summary.Cell align="right">
|
||||
{Dinero(job.job_totals.parts.parts.prt_dsmk_total).toFormat()}
|
||||
</Table.Summary.Cell>
|
||||
</Table.Summary.Row>
|
||||
|
||||
<Table.Summary.Row>
|
||||
<Table.Summary.Cell>
|
||||
<strong>{t("jobs.labels.partstotal")}</strong>
|
||||
@@ -90,6 +106,26 @@ export default function JobTotalsTableParts({job}) {
|
||||
</strong>
|
||||
</Table.Summary.Cell>
|
||||
</Table.Summary.Row>
|
||||
{
|
||||
//TODO:AIO This shoudl only be in the US version. need to verify whether this causes problems for the CA version.
|
||||
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>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -29,26 +29,109 @@ export function JobTotalsTableTotals({bodyshop, job}) {
|
||||
total: job.job_totals.totals.subtotal,
|
||||
bold: true,
|
||||
},
|
||||
{
|
||||
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"
|
||||
|
||||
...(job.job_totals.totals.us_sales_tax_breakdown
|
||||
? [
|
||||
{
|
||||
key: t("jobs.fields.ca_bc_pvrt"),
|
||||
total: job.job_totals.additional.pvrt,
|
||||
key: `${
|
||||
bodyshop.md_responsibility_centers.taxes.tax_ty1?.tax_type1 ||
|
||||
"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: t("jobs.labels.federal_tax_amt"),
|
||||
total: job.job_totals.totals.federal_tax,
|
||||
},
|
||||
{
|
||||
key: `${
|
||||
bodyshop.md_responsibility_centers.taxes.tax_ty2?.tax_type2 ||
|
||||
"T2"
|
||||
} - ${[
|
||||
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"),
|
||||
total: job.job_totals.totals.total_repairs,
|
||||
@@ -58,10 +141,10 @@ export function JobTotalsTableTotals({bodyshop, job}) {
|
||||
key: t("jobs.fields.ded_amt"),
|
||||
total: job.job_totals.totals.custPayable.deductible,
|
||||
},
|
||||
{
|
||||
key: t("jobs.fields.federal_tax_payable"),
|
||||
total: job.job_totals.totals.custPayable.federal_tax,
|
||||
},
|
||||
// {
|
||||
// key: t("jobs.fields.federal_tax_payable"),
|
||||
// total: job.job_totals.totals.custPayable.federal_tax,
|
||||
// },
|
||||
{
|
||||
key: t("jobs.fields.other_amount_payable"),
|
||||
total: job.job_totals.totals.custPayable.other_customer_amount,
|
||||
@@ -82,7 +165,7 @@ export function JobTotalsTableTotals({bodyshop, job}) {
|
||||
bold: true,
|
||||
},
|
||||
];
|
||||
}, [job.job_totals, t, bodyshop.region_config]);
|
||||
}, [job.job_totals, job.cieca_pft, t, bodyshop.md_responsibility_centers]);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
|
||||
@@ -2,8 +2,7 @@ import {useMutation} from "@apollo/client";
|
||||
import {Button, Space, notification} from "antd";
|
||||
import React, {useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
|
||||
import dayjs from "../../utils/day";
|
||||
import dayjs from '../../utils/day';
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {INSERT_EXPORT_LOG} from "../../graphql/accounting.queries";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {useMutation} from "@apollo/client";
|
||||
import {notification, Switch} from "antd";
|
||||
import { notification, Switch} from "antd";
|
||||
import React, {useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
|
||||
@@ -25,7 +25,7 @@ export const GetSupplementDelta = async (client, jobId, newLines) => {
|
||||
//Found a relevant matching line. Add it to lines to update.
|
||||
linesToUpdate.push({
|
||||
id: existingLines[matchingIndex].id,
|
||||
newData: {...newLine, removed: false},
|
||||
newData: {...newLine, removed: false, act_price_before_ppc: null},
|
||||
});
|
||||
|
||||
//Splice out item we found for performance.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {gql, useApolloClient, useLazyQuery, useMutation, useQuery,} from "@apollo/client";
|
||||
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||
import {Col, notification, Row} from "antd";
|
||||
import {Button, Col, notification, Row} from "antd";
|
||||
import Axios from "axios";
|
||||
import Dinero from "dinero.js";
|
||||
import _ from "lodash";
|
||||
import dayjs from "../../utils/day";
|
||||
import queryString from "query-string";
|
||||
import React, {useCallback, useEffect, useState} from "react";
|
||||
@@ -32,6 +32,7 @@ import OwnerFindModalContainer from "../owner-find-modal/owner-find-modal.contai
|
||||
import {GetSupplementDelta} from "./jobs-available-supplement.estlines.util";
|
||||
import HeaderFields from "./jobs-available-supplement.headerfields";
|
||||
import JobsAvailableTableComponent from "./jobs-available-table.component";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -82,14 +83,15 @@ export function JobsAvailableContainer({bodyshop, currentUser, insertAuditTrail,
|
||||
const modalSearchState = useState("");
|
||||
|
||||
//Import Scenario
|
||||
const onOwnerFindModalOk = async () => {
|
||||
const onOwnerFindModalOk = async (lazyData) => {
|
||||
logImEXEvent("job_import_new");
|
||||
|
||||
setOwnerModalVisible(false);
|
||||
|
||||
setInsertLoading(true);
|
||||
|
||||
const estData = replaceEmpty(estDataRaw.data.available_jobs_by_pk);
|
||||
const estData = replaceEmpty(
|
||||
lazyData?.available_jobs_by_pk || estDataRaw.data.available_jobs_by_pk
|
||||
);
|
||||
|
||||
if (!(estData && estData.est_data)) {
|
||||
//We don't have the right data. Error!
|
||||
@@ -99,17 +101,24 @@ export function JobsAvailableContainer({bodyshop, currentUser, insertAuditTrail,
|
||||
});
|
||||
return;
|
||||
}
|
||||
//IO-539 Check for Parts Rate on PAL for SGI use case.
|
||||
await CheckTaxRates(estData.est_data, bodyshop);
|
||||
// if (process.env.REACT_APP_COUNTRY === "USA") {
|
||||
//Massage the CCC file set to remove duplicate UNQ_SEQ.
|
||||
InstanceRenderManager({executeFunction:true,rome: ResolveCCCLineIssues(estData.est_data, bodyshop) })
|
||||
|
||||
const newTotals = (
|
||||
await Axios.post("/job/totals", {
|
||||
job: {
|
||||
...estData.est_data,
|
||||
joblines: estData.est_data.joblines.data,
|
||||
},
|
||||
})
|
||||
).data;
|
||||
// } else {
|
||||
//IO-539 Check for Parts Rate on PAL for SGI use case.
|
||||
//TODO:AIO Check that the async function is actually waiting before moving on.
|
||||
InstanceRenderManager({executeFunction: true, imex: await CheckTaxRates(estData.est_data, bodyshop), rome: await CheckTaxRatesUSA(estData.est_data, bodyshop)})
|
||||
|
||||
// }
|
||||
// const newTotals = (
|
||||
// await Axios.post("/job/totals", {
|
||||
// job: {
|
||||
// ...estData.est_data,
|
||||
// joblines: estData.est_data.joblines.data,
|
||||
// },
|
||||
// })
|
||||
// ).data;
|
||||
|
||||
let existingVehicles;
|
||||
if (estData.est_data.v_vin) {
|
||||
@@ -124,9 +133,9 @@ export function JobsAvailableContainer({bodyshop, currentUser, insertAuditTrail,
|
||||
|
||||
const newJob = {
|
||||
...estData.est_data,
|
||||
clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
|
||||
owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"),
|
||||
job_totals: newTotals,
|
||||
// clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
|
||||
// owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"),
|
||||
// job_totals: newTotals,
|
||||
date_open: dayjs(),
|
||||
status: bodyshop.md_ro_statuses.default_imported,
|
||||
notes: {
|
||||
@@ -150,17 +159,23 @@ export function JobsAvailableContainer({bodyshop, currentUser, insertAuditTrail,
|
||||
delete newJob.vehicle;
|
||||
}
|
||||
|
||||
if (typeof newJob.kmin === "string") {
|
||||
newJob.kmin = null;
|
||||
}
|
||||
|
||||
try {
|
||||
const r = await insertNewJob({
|
||||
variables: {
|
||||
job: newJob,
|
||||
},
|
||||
});
|
||||
await Axios.post("/job/totalsssu", {
|
||||
id: r.data.insert_jobs.returning[0].id,
|
||||
});
|
||||
|
||||
if (CriticalPartsScanning.treatment === "on") {
|
||||
CriticalPartsScan(r.data.insert_jobs.returning[0].id);
|
||||
}
|
||||
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.created"),
|
||||
onClick: () => {
|
||||
@@ -174,7 +189,7 @@ export function JobsAvailableContainer({bodyshop, currentUser, insertAuditTrail,
|
||||
operation: AuditTrailMapping.jobimported(),
|
||||
});
|
||||
|
||||
deleteJob({
|
||||
await deleteJob({
|
||||
variables: {id: estData.id},
|
||||
}).then((r) => {
|
||||
refetch();
|
||||
@@ -182,18 +197,15 @@ export function JobsAvailableContainer({bodyshop, currentUser, insertAuditTrail,
|
||||
});
|
||||
|
||||
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
||||
} catch (err) {
|
||||
} catch (r) {
|
||||
//error while inserting
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.creating", {error: err.message}),
|
||||
});
|
||||
refetch().catch(e => {
|
||||
console.error(`Something went wrong in jobs available table container - ${err.message || ''}`)
|
||||
message: t("jobs.errors.creating", {error: r.message}),
|
||||
});
|
||||
refetch();
|
||||
setInsertLoading(false);
|
||||
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//Supplement scenario
|
||||
@@ -215,7 +227,9 @@ export function JobsAvailableContainer({bodyshop, currentUser, insertAuditTrail,
|
||||
//create upsert job
|
||||
let supp = replaceEmpty({...estData.est_data});
|
||||
//IO-539 Check for Parts Rate on PAL for SGI use case.
|
||||
await CheckTaxRates(supp, bodyshop);
|
||||
|
||||
InstanceRenderManager({executeFunction:true, imex: await CheckTaxRates(supp, bodyshop), rome: await CheckTaxRatesUSA(supp, bodyshop)})
|
||||
InstanceRenderManager({executeFunction:true ,rome: ResolveCCCLineIssues(supp, bodyshop) })
|
||||
|
||||
delete supp.owner;
|
||||
delete supp.vehicle;
|
||||
@@ -391,6 +405,28 @@ export function JobsAvailableContainer({bodyshop, currentUser, insertAuditTrail,
|
||||
partsQueueToggle={partsQueueToggle}
|
||||
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]}>
|
||||
<Col span={24}>
|
||||
<JobsAvailableTableComponent
|
||||
@@ -422,116 +458,159 @@ function replaceEmpty(someObj, replaceValue = null) {
|
||||
return JSON.parse(temp);
|
||||
}
|
||||
|
||||
async function CheckTaxRatesUSA(estData,bodyshop){
|
||||
if (!estData.parts_tax_rates?.PAM) {
|
||||
estData.parts_tax_rates.PAM = estData.parts_tax_rates.PAC;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function CheckTaxRates(estData, bodyshop) {
|
||||
//LKQ Check
|
||||
if (
|
||||
!estData.parts_tax_rates?.PAL ||
|
||||
estData.parts_tax_rates?.PAL?.prt_tax_rt === null ||
|
||||
estData.parts_tax_rates?.PAL?.prt_tax_rt === 0
|
||||
!estData.parts_tax_rates?.PAL ||
|
||||
estData.parts_tax_rates?.PAL?.prt_tax_rt === null ||
|
||||
estData.parts_tax_rates?.PAL?.prt_tax_rt === 0
|
||||
) {
|
||||
const res = await confirmDialog(
|
||||
`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.`
|
||||
const res = await confirmDialog(
|
||||
`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 (!estData.parts_tax_rates.PAL) {
|
||||
estData.parts_tax_rates.PAL = {
|
||||
prt_discp: 0,
|
||||
prt_mktyp: true,
|
||||
prt_mkupp: 0,
|
||||
prt_type: "PAL",
|
||||
};
|
||||
}
|
||||
if (res) {
|
||||
if (!estData.parts_tax_rates.PAL) {
|
||||
estData.parts_tax_rates.PAL = {
|
||||
prt_discp: 0,
|
||||
prt_mktyp: true,
|
||||
prt_mkupp: 0,
|
||||
prt_type: "PAL",
|
||||
};
|
||||
}
|
||||
estData.parts_tax_rates.PAL.prt_tax_rt =
|
||||
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||
estData.parts_tax_rates.PAL.prt_tax_in = true;
|
||||
}
|
||||
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||
estData.parts_tax_rates.PAL.prt_tax_in = true;
|
||||
}
|
||||
}
|
||||
//PAC Check
|
||||
if (
|
||||
!estData.parts_tax_rates?.PAC ||
|
||||
estData.parts_tax_rates?.PAC?.prt_tax_rt === null ||
|
||||
estData.parts_tax_rates?.PAC?.prt_tax_rt === 0
|
||||
!estData.parts_tax_rates?.PAC ||
|
||||
estData.parts_tax_rates?.PAC?.prt_tax_rt === null ||
|
||||
estData.parts_tax_rates?.PAC?.prt_tax_rt === 0
|
||||
) {
|
||||
const res = await confirmDialog(
|
||||
`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.`
|
||||
const res = await confirmDialog(
|
||||
`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 (!estData.parts_tax_rates.PAC) {
|
||||
estData.parts_tax_rates.PAC = {
|
||||
prt_discp: 0,
|
||||
prt_mktyp: true,
|
||||
prt_mkupp: 0,
|
||||
prt_type: "PAC",
|
||||
};
|
||||
}
|
||||
estData.parts_tax_rates.PAC.prt_tax_rt =
|
||||
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||
estData.parts_tax_rates.PAC.prt_tax_in = true;
|
||||
}
|
||||
if (res) {
|
||||
if (!estData.parts_tax_rates.PAC) {
|
||||
estData.parts_tax_rates.PAC = {
|
||||
prt_discp: 0,
|
||||
prt_mktyp: true,
|
||||
prt_mkupp: 0,
|
||||
prt_type: "PAC",
|
||||
};
|
||||
}
|
||||
//PAM Check
|
||||
if (
|
||||
!estData.parts_tax_rates?.PAM ||
|
||||
estData.parts_tax_rates?.PAM?.prt_tax_rt === null ||
|
||||
estData.parts_tax_rates?.PAM?.prt_tax_rt === 0
|
||||
estData.parts_tax_rates.PAC.prt_tax_rt =
|
||||
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||
estData.parts_tax_rates.PAC.prt_tax_in = true;
|
||||
}
|
||||
}
|
||||
//PAM Check
|
||||
if (
|
||||
!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",
|
||||
};
|
||||
}
|
||||
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;
|
||||
}
|
||||
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||
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
|
||||
!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(
|
||||
`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.`
|
||||
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.`
|
||||
);
|
||||
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",
|
||||
};
|
||||
}
|
||||
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;
|
||||
}
|
||||
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.
|
||||
//Currently limited to SK shops only.
|
||||
//if (bodyshop.region_config === "CA_SK") {
|
||||
estData.joblines.data.forEach((jl, index) => {
|
||||
if (
|
||||
(jl.part_type === "PASL" || jl.part_type === "PAS") &&
|
||||
jl.lbr_op !== "OP11"
|
||||
) {
|
||||
estData.joblines.data[index].tax_part = jl.lbr_tax;
|
||||
}
|
||||
estData.joblines.data.forEach((jl, index) => {
|
||||
if (
|
||||
(jl.part_type === "PASL" || jl.part_type === "PAS") &&
|
||||
jl.lbr_op !== "OP11"
|
||||
) {
|
||||
estData.joblines.data[index].tax_part = jl.lbr_tax;
|
||||
}
|
||||
|
||||
//Set markup lines and tax lines as taxable.
|
||||
//900510 is a mark up. 900510 is a discount.
|
||||
if (jl.db_ref === "900510") {
|
||||
estData.joblines.data[index].tax_part = true;
|
||||
}
|
||||
});
|
||||
//Set markup lines and tax lines as taxable.
|
||||
//900510 is a mark up. 900510 is a discount.
|
||||
if (jl.db_ref === "900510") {
|
||||
estData.joblines.data[index].tax_part = true;
|
||||
}
|
||||
});
|
||||
//}
|
||||
}
|
||||
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,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ export function JobsCloseAutoAllocate({bodyshop, joblines, form, disabled}) {
|
||||
ret.profitcenter_part = defaults.profits["MAPA"];
|
||||
} else if (lineDesc.includes("ats amount")) {
|
||||
ret.profitcenter_part = defaults.profits["ATS"];
|
||||
} else if (jl.act_price > 0) {
|
||||
ret.profitcenter_part = defaults.profits["PAO"];
|
||||
} else {
|
||||
ret.profitcenter_part = null;
|
||||
}
|
||||
|
||||
@@ -9,10 +9,7 @@ import {createStructuredSelector} from "reselect";
|
||||
import {auth, logImEXEvent} from "../../firebase/firebase.utils";
|
||||
import {INSERT_EXPORT_LOG} from "../../graphql/accounting.queries";
|
||||
import {UPDATE_JOB} from "../../graphql/jobs.queries";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
|
||||
import client from "../../utils/GraphQLClient";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
|
||||
@@ -10,8 +10,15 @@ import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
||||
import FormItemPhone, {PhoneItemFormatterValidation,} from "../form-items-formatted/phone-form-item.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 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 LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
@@ -257,26 +264,31 @@ export function JobsCreateJobsInfo({bodyshop, form, selected}) {
|
||||
<CurrencyInput/>
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.federal_tax_rate")}
|
||||
name="federal_tax_rate"
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.state_tax_rate")}
|
||||
name="state_tax_rate"
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.local_tax_rate")}
|
||||
name="local_tax_rate"
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2}/>
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
{
|
||||
InstanceRenderManager({imex:
|
||||
<LayoutFormRow>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.federal_tax_rate")}
|
||||
name="federal_tax_rate"
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.state_tax_rate")}
|
||||
name="state_tax_rate"
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.local_tax_rate")}
|
||||
name="local_tax_rate"
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
})
|
||||
}
|
||||
|
||||
<LayoutFormRow>
|
||||
<Form.Item label={t("jobs.fields.rate_lab")} name="rate_lab">
|
||||
<CurrencyInput/>
|
||||
@@ -354,7 +366,15 @@ export function JobsCreateJobsInfo({bodyshop, form, selected}) {
|
||||
expanded
|
||||
required={selected && true}
|
||||
form={form}
|
||||
/>
|
||||
/>{
|
||||
InstanceRenderManager({rome:
|
||||
<>
|
||||
<JobsDetailRatesLabor form={form}/>
|
||||
<JobsDetailRatesMaterials form={form}/>
|
||||
<JobsDetailRatesOther form={form}/>
|
||||
<JobsDetailRatesTaxes form={form}/>
|
||||
</>})
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,5 +43,4 @@ export default function AddToProduction(
|
||||
|
||||
//insert the new job. call the callback with the returned ID when done.
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
dispatch(setModalContext({context: context, modal: "cardPayment"})),
|
||||
insertAuditTrail: ({jobid, operation}) =>
|
||||
dispatch(insertAuditTrail({jobid, operation})),
|
||||
setTimeTicketTaskContext: (context) =>
|
||||
dispatch(setModalContext({context: context, modal: "timeTicketTask"})),
|
||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
||||
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
|
||||
setMessage: (text) => dispatch(setMessage(text)),
|
||||
@@ -70,7 +72,8 @@ export function JobsDetailHeaderActions({
|
||||
insertAuditTrail,
|
||||
setEmailOptions,
|
||||
openChatByPhone,
|
||||
setMessage
|
||||
setMessage,
|
||||
setTimeTicketTaskContext,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
const client = useApolloClient();
|
||||
@@ -678,7 +681,23 @@ export function JobsDetailHeaderActions({
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
}];
|
||||
|
||||
if (bodyshop.md_tasks_presets.enable_tasks) {
|
||||
menuItems.push({
|
||||
key: 'claimtimetickettasks',
|
||||
disabled: !job.converted || (!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced),
|
||||
onClick: () => {
|
||||
setTimeTicketTaskContext({
|
||||
actions: {},
|
||||
context: {jobid: job.id},
|
||||
});
|
||||
},
|
||||
label: t("timetickets.actions.claimtasks")
|
||||
});
|
||||
}
|
||||
|
||||
menuItems.push(
|
||||
{
|
||||
key: 'enterpayments',
|
||||
disabled: !job.converted,
|
||||
@@ -691,7 +710,7 @@ export function JobsDetailHeaderActions({
|
||||
context: {jobid: job.id},
|
||||
});
|
||||
}
|
||||
}];
|
||||
});
|
||||
|
||||
if (ImEXPay.treatment === "on") {
|
||||
menuItems.push({
|
||||
|
||||
@@ -59,7 +59,7 @@ export default async function DuplicateJob(
|
||||
|
||||
//insert the new job. call the callback with the returned ID when done.
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
export async function CreateIouForJob(
|
||||
|
||||
@@ -5,9 +5,13 @@ import {createStructuredSelector} from "reselect";
|
||||
import {selectJobReadOnly} from "../../redux/application/application.selectors";
|
||||
import LaborAllocationsTableComponent from "../labor-allocations-table/labor-allocations-table.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 {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, null)(JobsDetailLaborContainer);
|
||||
@@ -48,6 +52,7 @@ const adjSpan = {
|
||||
};
|
||||
|
||||
export function JobsDetailLaborContainer({
|
||||
bodyshop,
|
||||
jobRO,
|
||||
job,
|
||||
jobId,
|
||||
@@ -58,6 +63,13 @@ export function JobsDetailLaborContainer({
|
||||
techConsole,
|
||||
adjustments,
|
||||
}) {
|
||||
const {treatments: {Enhanced_Payroll}} = useSplitTreatments({
|
||||
attributes: {},
|
||||
names: ["Enhanced_Payroll"],
|
||||
splitKey: bodyshop.imexshopid,
|
||||
});
|
||||
|
||||
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col {...ticketSpan}>
|
||||
@@ -70,14 +82,28 @@ export function JobsDetailLaborContainer({
|
||||
jobId={jobId}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...adjSpan}>
|
||||
<LaborAllocationsTableComponent
|
||||
jobId={jobId}
|
||||
joblines={joblines}
|
||||
timetickets={timetickets}
|
||||
adjustments={adjustments}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
{Enhanced_Payroll.treatment === "on" ? (
|
||||
<Col {...adjSpan}>
|
||||
<PayrollLaborAllocationsTable
|
||||
jobId={jobId}
|
||||
joblines={joblines}
|
||||
timetickets={timetickets}
|
||||
refetch={refetch}
|
||||
adjustments={adjustments}
|
||||
/>
|
||||
</Col>
|
||||
) : (
|
||||
<Col {...adjSpan}>
|
||||
<LaborAllocationsTableComponent
|
||||
jobId={jobId}
|
||||
joblines={joblines}
|
||||
timetickets={timetickets}
|
||||
refetch={refetch}
|
||||
adjustments={adjustments}
|
||||
/>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,12 +6,14 @@ import BillsListTable from "../bills-list-table/bills-list-table.component";
|
||||
import JobBillsTotal from "../job-bills-total/job-bills-total.component";
|
||||
import PartsOrderListTableComponent from "../parts-order-list-table/parts-order-list-table.component";
|
||||
import PartsOrderModal from "../parts-order-modal/parts-order-modal.container";
|
||||
import PartsDispatchTable from "../parts-dispatch-table/parts-dispatch-table.component";
|
||||
|
||||
export default function JobsDetailPliComponent({
|
||||
job,
|
||||
billsQuery,
|
||||
handleBillOnRowClick,
|
||||
handlePartsOrderOnRowClick,
|
||||
handlePartsDispatchOnRowClick,
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
@@ -43,6 +45,13 @@ export default function JobsDetailPliComponent({
|
||||
billsQuery={billsQuery}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<PartsDispatchTable
|
||||
job={job}
|
||||
handleOnRowClick={handlePartsDispatchOnRowClick}
|
||||
billsQuery={billsQuery}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -39,12 +39,24 @@ 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 (
|
||||
<JobsDetailPliComponent
|
||||
job={job}
|
||||
billsQuery={billsQuery}
|
||||
handleBillOnRowClick={handleBillOnRowClick}
|
||||
handlePartsOrderOnRowClick={handlePartsOrderOnRowClick}
|
||||
handlePartsDispatchOnRowClick={handlePartsDispatchOnRowClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,13 @@ 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 JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.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 JobsDetailRatesTaxes from "./jobs-detail-rates.taxes.component";
|
||||
import JobsDetailRatesProfileOVerride from "./jobs-detail-rates.profile-override.component";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly,
|
||||
@@ -111,41 +117,44 @@ export function JobsDetailRates({jobRO, form, job, bodyshop}) {
|
||||
}}
|
||||
</Form.Item>
|
||||
</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>
|
||||
{
|
||||
InstanceRenderManager({imex:
|
||||
<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
|
||||
orientation="left"
|
||||
type="horizontal"
|
||||
@@ -233,8 +242,19 @@ export function JobsDetailRates({jobRO, form, job, bodyshop}) {
|
||||
<CurrencyInput min={0} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
{
|
||||
InstanceRenderManager({rome: <>
|
||||
<Divider orientation="left">Tax Profile</Divider>
|
||||
<JobsDetailRatesProfileOVerride form={form}/>
|
||||
<JobsDetailRatesParts form={form}/>
|
||||
</div>
|
||||
<JobsDetailRatesLabor form={form}/>
|
||||
<JobsDetailRatesMaterials form={form}/>
|
||||
<JobsDetailRatesOther form={form}/>
|
||||
<JobsDetailRatesTaxes form={form}/>
|
||||
|
||||
</>})
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,429 @@
|
||||
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);
|
||||
@@ -0,0 +1,147 @@
|
||||
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);
|
||||
@@ -0,0 +1,106 @@
|
||||
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);
|
||||
@@ -30,7 +30,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAA", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -43,7 +43,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAA", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -69,18 +69,58 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
</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 header={t("joblines.fields.part_types.PAC")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAC", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -93,7 +133,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAC", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -119,18 +159,58 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
</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 header={t("joblines.fields.part_types.PAL")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAL", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -143,7 +223,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAL", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -169,18 +249,58 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
</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 header={t("joblines.fields.part_types.PAG")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAG", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -193,7 +313,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAG", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -219,18 +339,58 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
</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 header={t("joblines.fields.part_types.PAM")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAM", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -243,7 +403,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAM", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -269,18 +429,58 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
</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 header={t("joblines.fields.part_types.PAN")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAN", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -293,7 +493,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAN", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -319,18 +519,58 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
</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 header={t("joblines.fields.part_types.PAO")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAO", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -343,7 +583,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAO", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -369,18 +609,58 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
</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 header={t("joblines.fields.part_types.PAP")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAP", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -393,7 +673,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAP", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -419,18 +699,58 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
</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 header={t("joblines.fields.part_types.PAR")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAR", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -443,7 +763,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAR", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -469,18 +789,58 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
</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 header={t("joblines.fields.part_types.PAS")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PAS", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -493,7 +853,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PAS", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -519,18 +879,58 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
</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 header={t("joblines.fields.part_types.PASL")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "PASL", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -543,7 +943,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "PASL", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -569,18 +969,58 @@ export function JobsDetailRatesParts({
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
precision={4}
|
||||
disabled={jobRO}
|
||||
/>
|
||||
</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 header={t("joblines.fields.part_types.CCDR")}>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "CCDR", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -593,7 +1033,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "CCDR", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -606,7 +1046,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
||||
name={["parts_tax_rates", "CCDR", "prt_tax_rt"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.CCF")}>
|
||||
@@ -614,7 +1054,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "CCF", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -627,7 +1067,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "CCF", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -640,7 +1080,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
||||
name={["parts_tax_rates", "CCF", "prt_tax_rt"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.CCM")}>
|
||||
@@ -648,7 +1088,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "CCM", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -661,7 +1101,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "CCM", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -674,7 +1114,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
||||
name={["parts_tax_rates", "CCM", "prt_tax_rt"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.CCC")}>
|
||||
@@ -682,7 +1122,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "CCC", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -695,7 +1135,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "CCC", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -708,7 +1148,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
||||
name={["parts_tax_rates", "CCC", "prt_tax_rt"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("joblines.fields.part_types.CCD")}>
|
||||
@@ -716,7 +1156,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||
name={["parts_tax_rates", "CCD", "prt_discp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||
@@ -729,7 +1169,7 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||
name={["parts_tax_rates", "CCD", "prt_mkupp"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||
@@ -742,39 +1182,39 @@ export function JobsDetailRatesParts({
|
||||
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
||||
name={["parts_tax_rates", "CCD", "prt_tax_rt"]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow>
|
||||
<Form.Item label={t("jobs.fields.tax_tow_rt")} name="tax_tow_rt">
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.tax_str_rt")} name="tax_str_rt">
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.tax_paint_mat_rt")}
|
||||
name="tax_paint_mat_rt"
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.tax_shop_mat_rt")}
|
||||
name="tax_shop_mat_rt"
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.tax_sub_rt")} name="tax_sub_rt">
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.tax_lbr_rt")} name="tax_lbr_rt">
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.tax_levies_rt")}
|
||||
name="tax_levies_rt"
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO}/>
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
</Collapse.Panel>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
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 wrap>
|
||||
{section}
|
||||
</Space>
|
||||
<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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Popconfirm, Button} from "antd";
|
||||
import {Button, Popconfirm} from "antd";
|
||||
import React from "react";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {selectJobReadOnly} from "../../redux/application/application.selectors";
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
import {useMutation} from "@apollo/client";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Form,
|
||||
InputNumber,
|
||||
notification,
|
||||
Popover,
|
||||
Select,
|
||||
} from "antd";
|
||||
import {Button, Card, Form, InputNumber, notification, Popover, Select,} from "antd";
|
||||
import React, {useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {UPDATE_JOB} from "../../graphql/jobs.queries";
|
||||
|
||||
@@ -0,0 +1,325 @@
|
||||
import {Button, Card, Col, notification, Row, Space, Table, Typography,} 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);
|
||||
@@ -0,0 +1,81 @@
|
||||
import {useMutation} from "@apollo/client";
|
||||
import {Button, Card, Col, notification, Row, Table} from "antd";
|
||||
import day from "../../utils/day";
|
||||
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 = day();
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
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);
|
||||
@@ -5,10 +5,7 @@ import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {INSERT_EXPORT_LOG} from "../../graphql/accounting.queries";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -37,16 +34,16 @@ export function BillMarkSelectedExported({
|
||||
const [open, setOpen] = useState(false);
|
||||
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
|
||||
const [updateBill] = useMutation(gql`
|
||||
mutation UPDATE_BILL($billIds: [uuid!]!) {
|
||||
update_bills(where: { id: { _in: $billIds } }, _set: { exported: true }) {
|
||||
returning {
|
||||
id
|
||||
exported
|
||||
exported_at
|
||||
mutation UPDATE_BILL($billIds: [uuid!]!) {
|
||||
update_bills(where: { id: { _in: $billIds } }, _set: { exported: true }) {
|
||||
returning {
|
||||
id
|
||||
exported
|
||||
exported_at
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`);
|
||||
`);
|
||||
|
||||
const handleUpdate = async () => {
|
||||
setLoading(true);
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
import {useMutation, useQuery} from "@apollo/client";
|
||||
import {
|
||||
Button,
|
||||
Descriptions,
|
||||
InputNumber,
|
||||
Modal,
|
||||
Space,
|
||||
notification,
|
||||
} from "antd";
|
||||
import {Button, Descriptions, InputNumber, Modal, notification, Space,} from "antd";
|
||||
import axios from "axios";
|
||||
import dayjs from "../../utils/day";
|
||||
import React, {useState} from "react";
|
||||
|
||||
@@ -5,10 +5,7 @@ import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {INSERT_EXPORT_LOG} from "../../graphql/accounting.queries";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -38,18 +35,18 @@ export function PaymentMarkSelectedExported({
|
||||
|
||||
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
|
||||
const [updatePayments] = useMutation(gql`
|
||||
mutation UPDATE_PAYMENTS($paymentIds: [uuid!]!, $exportedat: timestamptz!) {
|
||||
update_payments(
|
||||
where: { id: { _in: $paymentIds } }
|
||||
_set: { exportedat: $exportedat }
|
||||
) {
|
||||
returning {
|
||||
id
|
||||
exportedat
|
||||
mutation UPDATE_PAYMENTS($paymentIds: [uuid!]!, $exportedat: timestamptz!) {
|
||||
update_payments(
|
||||
where: { id: { _in: $paymentIds } }
|
||||
_set: { exportedat: $exportedat }
|
||||
) {
|
||||
returning {
|
||||
id
|
||||
exportedat
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`);
|
||||
`);
|
||||
|
||||
const handleUpdate = async () => {
|
||||
setLoading(true);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||
import {Card, Col, Input, Row, Space, Typography} from "antd";
|
||||
import _ from "lodash";
|
||||
import React, {useState} from "react";
|
||||
@@ -24,8 +25,14 @@ export function PrintCenterJobsComponent({printCenterModal, bodyshop}) {
|
||||
const {id: jobId, job} = printCenterModal.context;
|
||||
const tempList = TemplateList("job", {});
|
||||
const {t} = useTranslation();
|
||||
const {treatments: {Enhanced_Payroll}} = useSplitTreatments({
|
||||
attributes: {},
|
||||
names: ["Enhanced_Payroll"],
|
||||
splitKey: bodyshop.imexshopid,
|
||||
});
|
||||
|
||||
const JobsReportsList =
|
||||
|
||||
const Templates =
|
||||
bodyshop.cdk_dealerid === null && bodyshop.pbs_serialnumber === null
|
||||
? Object.keys(tempList)
|
||||
.map((key) => {
|
||||
@@ -52,7 +59,26 @@ export function PrintCenterJobsComponent({printCenterModal, bodyshop}) {
|
||||
bodyshop.region_config.includes(Object.keys(temp.regions)) ===
|
||||
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 =
|
||||
search !== ""
|
||||
? JobsReportsList.filter((r) =>
|
||||
|
||||
@@ -9,9 +9,11 @@ export default function PrintWrapperComponent({
|
||||
children,
|
||||
id,
|
||||
emailOnly = false,
|
||||
disabled,
|
||||
}) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const handlePrint = async (type) => {
|
||||
if (disabled) return;
|
||||
setLoading(true);
|
||||
await GenerateDocument(templateObject, messageObject, type, id);
|
||||
setLoading(false);
|
||||
@@ -20,8 +22,18 @@ export default function PrintWrapperComponent({
|
||||
return (
|
||||
<Space>
|
||||
{children || null}
|
||||
{!emailOnly && <PrinterFilled onClick={() => handlePrint("p")}/>}
|
||||
<MailFilled onClick={() => handlePrint("e")}/>
|
||||
{!emailOnly && (
|
||||
<PrinterFilled
|
||||
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/>}
|
||||
</Space>
|
||||
);
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
import {useMutation} from "@apollo/client";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
Form,
|
||||
notification,
|
||||
Popover,
|
||||
Row,
|
||||
Switch,
|
||||
} from "antd";
|
||||
import {Button, Card, Col, Form, notification, Popover, Row, Switch,} from "antd";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {UPDATE_KANBAN_SETTINGS} from "../../graphql/user.queries";
|
||||
|
||||
@@ -25,6 +25,8 @@ import ProductionListColumnNote from "./production-list-columns.productionnote.c
|
||||
import ProductionListColumnCategory from "./production-list-columns.status.category";
|
||||
import ProductionListColumnStatus from "./production-list-columns.status.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, data, bodyshop}) => {
|
||||
return [
|
||||
@@ -39,6 +41,29 @@ const r = ({technician, state, activeStatuses, data, bodyshop}) => {
|
||||
</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"),
|
||||
dataIndex: "ro_number",
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
import {DeleteFilled, PlusCircleFilled} from "@ant-design/icons";
|
||||
import {useMutation} from "@apollo/client";
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
notification,
|
||||
Popover,
|
||||
Row,
|
||||
Select,
|
||||
Space,
|
||||
Spin,
|
||||
} from "antd";
|
||||
import {Button, Col, notification, Popover, Row, Select, Space, Spin,} from "antd";
|
||||
import React, {useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {useMutation} from "@apollo/client";
|
||||
import {Button, Card, Dropdown, Form, Input, notification, Space} from "antd";
|
||||
import dayjs from "../../utils/day";
|
||||
import React, {useState, useEffect} from "react";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
|
||||
@@ -98,7 +98,7 @@ export function ProductionListDetail({
|
||||
/>
|
||||
}
|
||||
placement="right"
|
||||
width={"33%"}
|
||||
width={"50%"}
|
||||
onClose={handleClose}
|
||||
open={selected}
|
||||
>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {useLazyQuery} from "@apollo/client";
|
||||
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||
import {Button, Card, Col, DatePicker, Form, Input, Radio, Row, Typography,} from "antd";
|
||||
import _ from "lodash";
|
||||
import dayjs from "../../utils/day";
|
||||
@@ -9,6 +10,7 @@ import {createStructuredSelector} from "reselect";
|
||||
import {QUERY_ACTIVE_EMPLOYEES} from "../../graphql/employees.queries";
|
||||
import {QUERY_ALL_VENDORS} from "../../graphql/vendors.queries";
|
||||
import {selectReportCenter} from "../../redux/modals/modals.selectors";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import DatePIckerRanges from "../../utils/DatePickerRanges";
|
||||
import {GenerateDocument} from "../../utils/RenderTemplate";
|
||||
import {TemplateList} from "../../utils/TemplateConstants";
|
||||
@@ -18,6 +20,7 @@ import "./report-center-modal.styles.scss";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
reportCenterModal: selectReportCenter,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
@@ -27,16 +30,39 @@ export default connect(
|
||||
mapDispatchToProps
|
||||
)(ReportCenterModalComponent);
|
||||
|
||||
export function ReportCenterModalComponent({reportCenterModal}) {
|
||||
export function ReportCenterModalComponent({reportCenterModal, bodyshop}) {
|
||||
const [form] = Form.useForm();
|
||||
const [search, setSearch] = useState("");
|
||||
const {treatments: {Enhanced_Payroll}} = useSplitTreatments({
|
||||
attributes: {},
|
||||
names: ["Enhanced_Payroll"],
|
||||
splitKey: bodyshop.imexshopid,
|
||||
});
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const {t} = useTranslation();
|
||||
const Templates = TemplateList("report_center");
|
||||
const ReportsList = Object.keys(Templates).map((key) => {
|
||||
return Templates[key];
|
||||
});
|
||||
const ReportsList =
|
||||
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 {open} = reportCenterModal;
|
||||
|
||||
const [callVendorQuery, {data: vendorData, called: vendorCalled}] =
|
||||
|
||||
@@ -1,13 +1,4 @@
|
||||
import {
|
||||
Card,
|
||||
Col,
|
||||
Form,
|
||||
Row,
|
||||
Space,
|
||||
Statistic,
|
||||
Switch,
|
||||
Typography,
|
||||
} from "antd";
|
||||
import {Card, Col, Form, Row, Space, Statistic, Switch, Typography,} from "antd";
|
||||
import dayjs from "../../utils/day";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
|
||||
@@ -26,6 +26,7 @@ import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.c
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import ShopEmployeeAddVacation from "./shop-employees-add-vacation.component";
|
||||
import queryString from "query-string";
|
||||
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -47,6 +48,12 @@ export function ShopEmployeesFormComponent({bodyshop}) {
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
const {treatments: {Enhanced_Payroll}} = useSplitTreatments({
|
||||
attributes: {},
|
||||
names: ["Enhanced_Payroll"],
|
||||
splitKey: bodyshop.imexshopid,
|
||||
});
|
||||
|
||||
const client = useApolloClient();
|
||||
useEffect(() => {
|
||||
if (data && data.employees_by_pk) form.setFieldsValue(data.employees_by_pk);
|
||||
@@ -352,7 +359,7 @@ export function ShopEmployeesFormComponent({bodyshop}) {
|
||||
{t("timetickets.labels.shift")}
|
||||
</Select.Option>
|
||||
|
||||
{bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
|
||||
{bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || Enhanced_Payroll.treatment === "on"
|
||||
? CiecaSelect(false, true)
|
||||
: bodyshop.md_responsibility_centers.costs.map(
|
||||
(c) => (
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user