Compare commits
328 Commits
feature/IO
...
rome/maste
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07f74ca5c2 | ||
|
|
4fd18b54bd | ||
|
|
0ef1090224 | ||
|
|
f8b7d2c4a7 | ||
|
|
6f09064f05 | ||
|
|
a74b340d23 | ||
|
|
388446f46c | ||
|
|
10516e44bc | ||
|
|
10b3f82619 | ||
|
|
737d6232c6 | ||
|
|
1f2da44a3f | ||
|
|
67bcae3c5b | ||
|
|
da65f39d28 | ||
|
|
9e216bb3ad | ||
|
|
4bfee3c135 | ||
|
|
54df4a7f8c | ||
|
|
771f75788a | ||
|
|
b8880e8cb8 | ||
|
|
618107fc63 | ||
|
|
0879cb602d | ||
|
|
d5198edfc0 | ||
|
|
647c796497 | ||
|
|
39bba3623a | ||
|
|
8687aeb9bf | ||
|
|
62dac2193f | ||
|
|
22feaf6c77 | ||
|
|
72da0734c8 | ||
|
|
e72f42bda2 | ||
|
|
69b4b76501 | ||
|
|
ae56e27e5f | ||
|
|
12d0a9ec78 | ||
|
|
d6bb6a891d | ||
|
|
2f83b0baa7 | ||
|
|
04728690ca | ||
|
|
2a31d740d5 | ||
|
|
ff7523ecb8 | ||
|
|
b53fdadaee | ||
|
|
93390bef63 | ||
|
|
485e2ef4f1 | ||
|
|
9de3b5efb6 | ||
|
|
2632282bba | ||
|
|
7379f50792 | ||
|
|
9ae53d090f | ||
|
|
677c7bb4cc | ||
|
|
4fdc241b9c | ||
|
|
06ca7603b9 | ||
|
|
28dc4867db | ||
|
|
1cf13da413 | ||
|
|
3919efaa77 | ||
|
|
a76fc780cb | ||
|
|
d915c123db | ||
|
|
151c9c0add | ||
|
|
f14dc497cf | ||
|
|
89339e1033 | ||
|
|
146a61c9cf | ||
|
|
5250d97935 | ||
|
|
18dce9795d | ||
|
|
7add2bb12e | ||
|
|
756fc07193 | ||
|
|
71a5722b0e | ||
|
|
4507135a1b | ||
|
|
7f2479f597 | ||
|
|
66b97be9d2 | ||
|
|
57909ebc61 | ||
|
|
d1090670a8 | ||
|
|
f4bcad1b06 | ||
|
|
6656708c8e | ||
|
|
bfe3fb2ed0 | ||
|
|
ec21521281 | ||
|
|
e71e671d7d | ||
|
|
df6b0fae21 | ||
|
|
69723a8bf4 | ||
|
|
76d907987a | ||
|
|
241caa6132 | ||
|
|
13b93f6d9d | ||
|
|
77555366f4 | ||
|
|
f3e8132bfc | ||
|
|
7510385cf4 | ||
|
|
e05b72615e | ||
|
|
b3f69d4088 | ||
|
|
8796c8fe06 | ||
|
|
b9a6a98fee | ||
|
|
acc9145d52 | ||
|
|
f9c8f53474 | ||
|
|
004e96517f | ||
|
|
957265b1c8 | ||
|
|
8807e282f4 | ||
|
|
c394974dc8 | ||
|
|
28386a4234 | ||
|
|
6f16c47d4f | ||
|
|
25062e37f4 | ||
|
|
2b1f8e4335 | ||
|
|
02690b6796 | ||
|
|
3897281015 | ||
|
|
8d0e5b93ed | ||
|
|
ef146032df | ||
|
|
1dc025fb36 | ||
|
|
30c0c84b93 | ||
|
|
4bd139f93b | ||
|
|
f59787add0 | ||
|
|
20c304a2db | ||
|
|
53526d9c80 | ||
|
|
68b4bc66ff | ||
|
|
805149daea | ||
|
|
b2529207e1 | ||
|
|
d2fe9b0590 | ||
|
|
eff4f82ad7 | ||
|
|
57327332c9 | ||
|
|
a2144ccb61 | ||
|
|
aa478fc510 | ||
|
|
659a0bc0fd | ||
|
|
f1ef28e544 | ||
|
|
fef680fb99 | ||
|
|
91a45a621a | ||
|
|
fe9512ca9c | ||
|
|
ff318599f5 | ||
|
|
d1ba90408d | ||
|
|
536f8d9cb9 | ||
|
|
6a9e871b08 | ||
|
|
d163d145d0 | ||
|
|
fdf0506976 | ||
|
|
c9d8fc3072 | ||
|
|
0bcf67a5f5 | ||
|
|
3e48753329 | ||
|
|
96441dbb33 | ||
|
|
6a0b63b185 | ||
|
|
9dea43fd34 | ||
|
|
465980dd8c | ||
|
|
03d04fd8d1 | ||
|
|
7d18c9b160 | ||
|
|
c097f98959 | ||
|
|
213d4ad928 | ||
|
|
710b3b00f5 | ||
|
|
d041d03fbf | ||
|
|
24b90e9888 | ||
|
|
4b83e798ac | ||
|
|
468f93f3df | ||
|
|
78d9dd5acb | ||
|
|
6ef7d4653d | ||
|
|
645eb637f2 | ||
|
|
268fdce5ac | ||
|
|
f9f3a05a43 | ||
|
|
8a92919b2e | ||
|
|
dbcd675300 | ||
|
|
4628af0e43 | ||
|
|
b1fedf5904 | ||
|
|
45ec03e615 | ||
|
|
570d36b695 | ||
|
|
b36697054e | ||
|
|
17149fe853 | ||
|
|
15c837f745 | ||
|
|
3f43ff05d0 | ||
|
|
d4dee21383 | ||
|
|
79da904767 | ||
|
|
0821797044 | ||
|
|
ad7ff62b56 | ||
|
|
f148d7d0d0 | ||
|
|
e19e3865e7 | ||
|
|
35f062d4e0 | ||
|
|
01328ba33c | ||
|
|
7caa138184 | ||
|
|
1b8c3f3081 | ||
|
|
3e4f36fb6f | ||
|
|
aedcb973f5 | ||
|
|
26dc720929 | ||
|
|
b5cc1c06df | ||
|
|
786c790307 | ||
|
|
3e8660bb61 | ||
|
|
997aed4ab3 | ||
|
|
f64ea058b9 | ||
|
|
96485ba252 | ||
|
|
a9bdcbcdd4 | ||
|
|
1d7f1cccba | ||
|
|
b002477c0d | ||
|
|
27f1447469 | ||
|
|
ff153cdd81 | ||
|
|
fe16329443 | ||
|
|
744e1cf2be | ||
|
|
ab2cf8c8c7 | ||
|
|
fbd001b797 | ||
|
|
4610be6f15 | ||
|
|
d5e34b649f | ||
|
|
7b49a94edd | ||
|
|
8dfcda6c5e | ||
|
|
756d97a9cb | ||
|
|
c0887dbeb9 | ||
|
|
b8f0ff217f | ||
|
|
cc805781e3 | ||
|
|
9531eca7a7 | ||
|
|
d7a1d5bbd2 | ||
|
|
c214168dcd | ||
|
|
dd085be5c7 | ||
|
|
bd7fbfff37 | ||
|
|
52a25fc720 | ||
|
|
384153d914 | ||
|
|
511ac5068b | ||
|
|
67e490ab53 | ||
|
|
b84cde1633 | ||
|
|
3350f7bd56 | ||
|
|
7510419836 | ||
|
|
7da3ef4e0c | ||
|
|
4c32171fbb | ||
|
|
470f178cde | ||
|
|
f9633505a2 | ||
|
|
de102d9898 | ||
|
|
9d503b12e6 | ||
|
|
4dc3bc1532 | ||
|
|
50da8cfbc6 | ||
|
|
bccfc127f3 | ||
|
|
2332d8756d | ||
|
|
a4abaed30a | ||
|
|
d47e18df1f | ||
|
|
c2d094da35 | ||
|
|
6e54b10239 | ||
|
|
e6c44ec52a | ||
|
|
39b8ecf785 | ||
|
|
d87f871334 | ||
|
|
07cea56e2b | ||
|
|
dbf3226f97 | ||
|
|
4b926edf24 | ||
|
|
ce0fd42995 | ||
|
|
bdf91443e0 | ||
|
|
68f4237e15 | ||
|
|
81fc747c03 | ||
|
|
3a25ed13b9 | ||
|
|
21c5eb6786 | ||
|
|
e85203e429 | ||
|
|
6b2d10d589 | ||
|
|
3d03de193e | ||
|
|
a56de72a6b | ||
|
|
0849bbbba6 | ||
|
|
0cd41d2036 | ||
|
|
a93bcd6ab6 | ||
|
|
77b72e160b | ||
|
|
b32c37f4af | ||
|
|
d0beea5cb3 | ||
|
|
30df6887c5 | ||
|
|
bc7823dda1 | ||
|
|
cbb195a313 | ||
|
|
097dc06c65 | ||
|
|
b06fbd25a0 | ||
|
|
89bbf274e5 | ||
|
|
d854177e5a | ||
|
|
aa9f82f2d4 | ||
|
|
2b45264628 | ||
|
|
4da299f431 | ||
|
|
9f9aa447a6 | ||
|
|
6a9f1597cb | ||
|
|
cddc48b851 | ||
|
|
84ab6d300c | ||
|
|
3c5026c2fb | ||
|
|
c3c8959d2e | ||
|
|
2c459d2636 | ||
|
|
a33c481dd5 | ||
|
|
d6b2f93e54 | ||
|
|
802e945829 | ||
|
|
a4a885f33a | ||
|
|
3380cebb28 | ||
|
|
21ed415f4f | ||
|
|
51dd89d36a | ||
|
|
bad96f231c | ||
|
|
184d72c444 | ||
|
|
6ca773050f | ||
|
|
648e8aaae0 | ||
|
|
ad9868b575 | ||
|
|
d4d10998f8 | ||
|
|
ba22c31deb | ||
|
|
f6be133a78 | ||
|
|
3768164f1f | ||
|
|
8465e7539f | ||
|
|
542eca5867 | ||
|
|
27a67c5f4a | ||
|
|
e00c40f2d5 | ||
|
|
b664e231dc | ||
|
|
92d6977f9e | ||
|
|
4059700468 | ||
|
|
196bdd83ba | ||
|
|
fbd52bc14e | ||
|
|
8013c988d0 | ||
|
|
460d6fcf12 | ||
|
|
92353c4853 | ||
|
|
8ca2a89f0f | ||
|
|
f54c2367f3 | ||
|
|
27452085e9 | ||
|
|
2bff7d9567 | ||
|
|
c3749f62fe | ||
|
|
b82c04a16a | ||
|
|
0ec099cdf5 | ||
|
|
d3f49094d8 | ||
|
|
4e2ad3bc62 | ||
|
|
05c287fcf7 | ||
|
|
c11cef4119 | ||
|
|
d6126cc5ce | ||
|
|
d88c925a68 | ||
|
|
372a572400 | ||
|
|
623ee8fbb1 | ||
|
|
729e0fc5ca | ||
|
|
d052cde6ca | ||
|
|
b52bbab903 | ||
|
|
9d42135ebf | ||
|
|
9ce5aa2189 | ||
|
|
e277292194 | ||
|
|
649961c831 | ||
|
|
e00cde2fbd | ||
|
|
25b89d191b | ||
|
|
0a60f77bfc | ||
|
|
66a80e439f | ||
|
|
d8cc25a54f | ||
|
|
39b9cfb348 | ||
|
|
c6087574be | ||
|
|
41c63cbfe9 | ||
|
|
f1afc81e7d | ||
|
|
018f8a19bb | ||
|
|
cccd007ba6 | ||
|
|
6d5b8baadf | ||
|
|
07e36415e4 | ||
|
|
8dc480826b | ||
|
|
533a5b87ec | ||
|
|
7bd83557c1 | ||
|
|
ef290e79b1 | ||
|
|
713b3f4f6d | ||
|
|
8fd9614f69 | ||
|
|
6682a98770 | ||
|
|
392b405978 | ||
|
|
be61850d18 | ||
|
|
f4208a2212 | ||
|
|
0b7278a2a8 | ||
|
|
bee078fe18 |
@@ -59,6 +59,139 @@ jobs:
|
|||||||
to: "s3://imex-online-production/"
|
to: "s3://imex-online-production/"
|
||||||
arguments: "--exclude '*.map'"
|
arguments: "--exclude '*.map'"
|
||||||
- jira/notify
|
- jira/notify
|
||||||
|
|
||||||
|
rome-api-deploy:
|
||||||
|
docker:
|
||||||
|
- image: "cimg/base:stable"
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- eb/setup
|
||||||
|
- run:
|
||||||
|
command: |
|
||||||
|
eb init romeonline-productionapi -r us-east-2 -p "Node.js 18 running on 64bit Amazon Linux 2"
|
||||||
|
eb status --verbose
|
||||||
|
eb deploy
|
||||||
|
eb status
|
||||||
|
- jira/notify
|
||||||
|
|
||||||
|
rome-hasura-migrate:
|
||||||
|
docker:
|
||||||
|
- image: cimg/node:16.15.0
|
||||||
|
parameters:
|
||||||
|
secret:
|
||||||
|
type: string
|
||||||
|
default: $HASURA_PROD_SECRET
|
||||||
|
working_directory: ~/repo/hasura
|
||||||
|
steps:
|
||||||
|
- checkout:
|
||||||
|
path: ~/repo
|
||||||
|
- run:
|
||||||
|
name: Execute migration
|
||||||
|
command: |
|
||||||
|
npm install hasura-cli -g
|
||||||
|
hasura migrate apply --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
|
||||||
|
hasura metadata apply --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
|
||||||
|
hasura metadata reload --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
|
||||||
|
|
||||||
|
rome-app-build:
|
||||||
|
docker:
|
||||||
|
- image: cimg/node:16.15.0
|
||||||
|
resource_class: large
|
||||||
|
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://rome-online-production/"
|
||||||
|
- jira/notify
|
||||||
|
|
||||||
|
test-rome-hasura-migrate:
|
||||||
|
docker:
|
||||||
|
- image: cimg/node:16.15.0
|
||||||
|
parameters:
|
||||||
|
secret:
|
||||||
|
type: string
|
||||||
|
default: $HASURA_ROME_TEST_SECRET
|
||||||
|
working_directory: ~/repo/hasura
|
||||||
|
steps:
|
||||||
|
- checkout:
|
||||||
|
path: ~/repo
|
||||||
|
- run:
|
||||||
|
name: Execute migration
|
||||||
|
command: |
|
||||||
|
npm install hasura-cli -g
|
||||||
|
echo ${HASURA_TEST_SECRET}
|
||||||
|
hasura migrate apply --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
|
||||||
|
hasura metadata apply --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
|
||||||
|
hasura metadata reload --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
|
||||||
|
|
||||||
|
test-rome-app-build:
|
||||||
|
docker:
|
||||||
|
- image: cimg/node:16.15.0
|
||||||
|
resource_class: large
|
||||||
|
working_directory: ~/repo/client
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- checkout:
|
||||||
|
path: ~/repo
|
||||||
|
- run:
|
||||||
|
name: Install Dependencies
|
||||||
|
command: npm i
|
||||||
|
|
||||||
|
- run: npm run build:test
|
||||||
|
|
||||||
|
- aws-s3/sync:
|
||||||
|
from: build
|
||||||
|
to: "s3://rome-online-test/"
|
||||||
|
- jira/notify
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
- aws-s3/sync:
|
||||||
|
from: build
|
||||||
|
to: "s3://imex-online-beta/"
|
||||||
|
- jira/notify
|
||||||
|
|
||||||
|
rome-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
|
||||||
|
|
||||||
|
- aws-s3/sync:
|
||||||
|
from: build
|
||||||
|
to: "s3://rome-online-production-beta/"
|
||||||
|
- jira/notify
|
||||||
|
|
||||||
test-hasura-migrate:
|
test-hasura-migrate:
|
||||||
docker:
|
docker:
|
||||||
@@ -101,6 +234,49 @@ jobs:
|
|||||||
arguments: "--exclude '*.map'"
|
arguments: "--exclude '*.map'"
|
||||||
- jira/notify
|
- jira/notify
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
- aws-s3/sync:
|
||||||
|
from: build
|
||||||
|
to: "s3://imex-online-test-beta/"
|
||||||
|
- jira/notify
|
||||||
|
|
||||||
|
rome-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
|
||||||
|
|
||||||
|
- aws-s3/sync:
|
||||||
|
from: build
|
||||||
|
to: "s3://rome-online-test-beta/"
|
||||||
|
- jira/notify
|
||||||
|
|
||||||
|
|
||||||
admin-app-build:
|
admin-app-build:
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/node:16.15.0
|
- image: cimg/node:16.15.0
|
||||||
@@ -142,20 +318,58 @@ workflows:
|
|||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
only: master
|
only: master
|
||||||
|
- app-beta-build:
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only: master-beta
|
||||||
|
- rome-app-beta-build:
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only: rome/master-beta
|
||||||
- hasura-migrate:
|
- hasura-migrate:
|
||||||
secret: ${HASURA_PROD_SECRET}
|
secret: ${HASURA_PROD_SECRET}
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
only: master
|
only: master
|
||||||
|
- rome-api-deploy:
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only: rome/master
|
||||||
|
- rome-app-build:
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only: rome/master
|
||||||
|
- rome-hasura-migrate:
|
||||||
|
secret: ${HASURA_PROD_SECRET}
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only: rome/master
|
||||||
- test-app-build:
|
- test-app-build:
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
only: test
|
only: test
|
||||||
|
- rome-test-app-beta-build:
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only: rome/test-beta
|
||||||
|
- test-app-beta-build:
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only: test-beta
|
||||||
- test-hasura-migrate:
|
- test-hasura-migrate:
|
||||||
secret: ${HASURA_TEST_SECRET}
|
secret: ${HASURA_TEST_SECRET}
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
only: test
|
only: test
|
||||||
|
- test-rome-app-build:
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only: rome/test
|
||||||
|
- test-rome-hasura-migrate:
|
||||||
|
secret: ${HASURA_ROME_TEST_SECRET}
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only: rome/test
|
||||||
#- admin-app-build:
|
#- admin-app-build:
|
||||||
#filters:
|
#filters:
|
||||||
#branches:
|
#branches:
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,14 @@
|
|||||||
REACT_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
|
REACT_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
|
||||||
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
|
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
|
||||||
REACT_APP_GA_CODE=231099835
|
REACT_APP_GA_CODE=231099835
|
||||||
REACT_APP_FIREBASE_CONFIG={"apiKey":"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_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_API=https://api.cloudinary.com/v1_1/io-test
|
||||||
REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/io-test
|
REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/io-test
|
||||||
REACT_APP_CLOUDINARY_API_KEY=957865933348715
|
REACT_APP_CLOUDINARY_API_KEY=957865933348715
|
||||||
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
|
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
|
||||||
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BG3tzU7L2BXlGZ_3VLK4PNaRceoEXEnmHfxcVbRMF5o5g05ejslhVPki9kBM9cBBT-08Ad9kN3HSpS6JmrWD6h4'
|
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo'
|
||||||
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
||||||
REACT_APP_AXIOS_BASE_API_URL=http://localhost:4000
|
REACT_APP_AXIOS_BASE_API_URL=http://localhost:4000
|
||||||
REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
|
REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
|
||||||
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
|
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
|
||||||
|
REACT_APP_COUNTRY=USA
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
GENERATE_SOURCEMAP=true
|
REACT_APP_GRAPHQL_ENDPOINT=https://db.romeonline.io/v1/graphql
|
||||||
REACT_APP_GRAPHQL_ENDPOINT=https://db.imex.online/v1/graphql
|
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.romeonline.io/v1/graphql
|
||||||
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.imex.online/v1/graphql
|
|
||||||
REACT_APP_GA_CODE=231103507
|
REACT_APP_GA_CODE=231103507
|
||||||
REACT_APP_FIREBASE_CONFIG={"apiKey":"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_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_API=https://api.cloudinary.com/v1_1/bodyshop
|
||||||
REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/bodyshop
|
REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/bodyshop
|
||||||
REACT_APP_CLOUDINARY_API_KEY=473322739956866
|
REACT_APP_CLOUDINARY_API_KEY=473322739956866
|
||||||
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
|
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
|
||||||
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BMgZT1NZztW2DsJl8Mg2L04hgY9FzAg6b8fbzgNAfww2VDzH3VE63Ot9EaP_U7KWS2JT-7HPHaw0T_Tw_5vkZc8'
|
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo'
|
||||||
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
||||||
REACT_APP_AXIOS_BASE_API_URL=https://api.imex.online/
|
REACT_APP_AXIOS_BASE_API_URL=https://api.romeonline.io/
|
||||||
REACT_APP_REPORTS_SERVER_URL=https://reports.imex.online
|
REACT_APP_REPORTS_SERVER_URL=https://reports.romeonline.io
|
||||||
REACT_APP_SPLIT_API=et9pjkik6bn67he5evpmpr1agoo7gactphgk
|
REACT_APP_SPLIT_API=et9pjkik6bn67he5evpmpr1agoo7gactphgk
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
REACT_APP_GRAPHQL_ENDPOINT=https://db.test.bodyshop.app/v1/graphql
|
REACT_APP_GRAPHQL_ENDPOINT=https://db.test.romeonline.io/v1/graphql
|
||||||
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.test.bodyshop.app/v1/graphql
|
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.test.romeonline.io/v1/graphql
|
||||||
REACT_APP_GA_CODE=231099835
|
REACT_APP_GA_CODE=231103507
|
||||||
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_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_API=https://api.cloudinary.com/v1_1/bodyshop
|
||||||
REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/bodyshop
|
REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/bodyshop
|
||||||
REACT_APP_CLOUDINARY_API_KEY=473322739956866
|
REACT_APP_CLOUDINARY_API_KEY=473322739956866
|
||||||
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
|
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
|
||||||
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BN2GcDPjipR5MTEosO5dT4CfQ3cmrdBIsI4juoOQrRijn_5aRiHlwj1mlq0W145mOusx6xynEKl_tvYJhpCc9lo'
|
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo'
|
||||||
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
||||||
REACT_APP_AXIOS_BASE_API_URL=https://api.test.imex.online/
|
REACT_APP_AXIOS_BASE_API_URL=https://api.test.romeonline.io/
|
||||||
REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
|
REACT_APP_REPORTS_SERVER_URL=https://reports.test.romeonline.io
|
||||||
REACT_APP_IS_TEST=true
|
REACT_APP_IS_TEST=true
|
||||||
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
|
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
|
||||||
@@ -27,7 +27,7 @@ module.exports = {
|
|||||||
lessOptions: {
|
lessOptions: {
|
||||||
modifyVars: {
|
modifyVars: {
|
||||||
...(process.env.NODE_ENV === "development"
|
...(process.env.NODE_ENV === "development"
|
||||||
? { "@primary-color": "#a51d1d" }
|
? { "@primary-color": "#B22234" }
|
||||||
: {
|
: {
|
||||||
//"@primary-color": "#1DA57A"
|
//"@primary-color": "#1DA57A"
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -28,6 +28,17 @@ switch (this.location.hostname) {
|
|||||||
// measurementId: "${config.measurementId}",
|
// measurementId: "${config.measurementId}",
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
case "romeonline.io":
|
||||||
|
firebaseConfig = {
|
||||||
|
apiKey: "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE",
|
||||||
|
authDomain: "rome-prod-1.firebaseapp.com",
|
||||||
|
projectId: "rome-prod-1",
|
||||||
|
storageBucket: "rome-prod-1.appspot.com",
|
||||||
|
messagingSenderId: "147786367145",
|
||||||
|
appId: "1:147786367145:web:9d4cba68071c3f29a8a9b8",
|
||||||
|
measurementId: "G-G8Z9DRHTZS",
|
||||||
|
};
|
||||||
|
break;
|
||||||
case "imex.online":
|
case "imex.online":
|
||||||
default:
|
default:
|
||||||
firebaseConfig = {
|
firebaseConfig = {
|
||||||
|
|||||||
@@ -2,23 +2,81 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.png" />
|
<link rel="icon" href="%PUBLIC_URL%/ro-favicon.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#002366" />
|
<meta name="theme-color" content="#002366" />
|
||||||
<meta name="description" content="ImEX Online" />
|
<meta name="description" content="Rome Online" />
|
||||||
<!-- <link rel="apple-touch-icon" href="logo192.png" /> -->
|
<!-- <link rel="apple-touch-icon" href="logo192.png" /> -->
|
||||||
|
|
||||||
|
<!--Use the below code snippet to provide real time updates to the live chat plugin without the need of copying and paste each time to your website when changes are made via PBX-->
|
||||||
|
|
||||||
|
<call-us-selector phonesystem-url=https://rometech.east.3cx.us:5001 party="LiveChat528346"></call-us-selector>
|
||||||
|
|
||||||
|
<!--Incase you don't want real time updates to the live chat plugin when options are changed, use the below code snippet. Please note that each time you change the settings you will need to copy and paste the snippet code to your website-->
|
||||||
|
|
||||||
|
<!--<call-us
|
||||||
|
|
||||||
|
phonesystem-url=https://rometech.east.3cx.us:5001
|
||||||
|
|
||||||
|
style="position:fixed;font-size:16px;line-height:17px;z-index: 99999;right: 20px; bottom: 20px;"
|
||||||
|
|
||||||
|
id="wp-live-chat-by-3CX"
|
||||||
|
|
||||||
|
minimized="true"
|
||||||
|
|
||||||
|
animation-style="noanimation"
|
||||||
|
|
||||||
|
party="LiveChat528346"
|
||||||
|
|
||||||
|
minimized-style="bubbleright"
|
||||||
|
|
||||||
|
allow-call="true"
|
||||||
|
|
||||||
|
allow-video="false"
|
||||||
|
|
||||||
|
allow-soundnotifications="true"
|
||||||
|
|
||||||
|
enable-mute="true"
|
||||||
|
|
||||||
|
enable-onmobile="true"
|
||||||
|
|
||||||
|
offline-enabled="true"
|
||||||
|
|
||||||
|
enable="true"
|
||||||
|
|
||||||
|
ignore-queueownership="false"
|
||||||
|
|
||||||
|
authentication="both"
|
||||||
|
|
||||||
|
show-operator-actual-name="true"
|
||||||
|
|
||||||
|
aknowledge-received="true"
|
||||||
|
|
||||||
|
gdpr-enabled="false"
|
||||||
|
|
||||||
|
message-userinfo-format="name"
|
||||||
|
|
||||||
|
message-dateformat="both"
|
||||||
|
|
||||||
|
lang="browser"
|
||||||
|
|
||||||
|
button-icon-type="default"
|
||||||
|
|
||||||
|
greeting-visibility="none"
|
||||||
|
|
||||||
|
greeting-offline-visibility="none"
|
||||||
|
|
||||||
|
chat-delay="2000"
|
||||||
|
|
||||||
|
enable-direct-call="true"
|
||||||
|
|
||||||
|
enable-ga="false"
|
||||||
|
|
||||||
|
></call-us>-->
|
||||||
|
|
||||||
|
<script defer src=https://downloads-global.3cx.com/downloads/livechatandtalk/v1/callus.js id="tcx-callus-js" charset="utf-8"></script>
|
||||||
|
|
||||||
<link rel="apple-touch-icon" href="logo192.png" />
|
<link rel="apple-touch-icon" href="logo192.png" />
|
||||||
<script type="text/javascript">
|
|
||||||
window.$crisp = [];
|
|
||||||
window.CRISP_WEBSITE_ID = "36724f62-2eb0-4b29-9cdd-9905fb99913e";
|
|
||||||
(function () {
|
|
||||||
d = document;
|
|
||||||
s = d.createElement("script");
|
|
||||||
s.src = "https://client.crisp.chat/l.js";
|
|
||||||
s.async = 1;
|
|
||||||
d.getElementsByTagName("head")[0].appendChild(s);
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
<script>
|
<script>
|
||||||
!(function () {
|
!(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
@@ -77,7 +135,7 @@
|
|||||||
work correctly both with client-side routing and a non-root public URL.
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
-->
|
-->
|
||||||
<title>ImEX Online</title>
|
<title>Rome Online</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"short_name": "ImEX Online",
|
"short_name": "Rome Online",
|
||||||
"name": "ImEX Online",
|
"name": "Rome Online",
|
||||||
"description": "The ultimate bodyshop management system.",
|
"description": "The ultimate bodyshop management system.",
|
||||||
"icons": [
|
"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 |
@@ -28,6 +28,12 @@ function AppContainer() {
|
|||||||
//componentSize="small"
|
//componentSize="small"
|
||||||
input={{ autoComplete: "new-password" }}
|
input={{ autoComplete: "new-password" }}
|
||||||
locale={enLocale}
|
locale={enLocale}
|
||||||
|
theme={{
|
||||||
|
token: {
|
||||||
|
colorPrimary: "#326ade",
|
||||||
|
colorInfo: "#326ade"
|
||||||
|
},
|
||||||
|
}}
|
||||||
form={{
|
form={{
|
||||||
validateMessages: {
|
validateMessages: {
|
||||||
// eslint-disable-next-line no-template-curly-in-string
|
// eslint-disable-next-line no-template-curly-in-string
|
||||||
|
|||||||
@@ -78,9 +78,9 @@ export function App({
|
|||||||
if (currentUser.authorized && bodyshop) {
|
if (currentUser.authorized && bodyshop) {
|
||||||
client.setAttribute("imexshopid", bodyshop.imexshopid);
|
client.setAttribute("imexshopid", bodyshop.imexshopid);
|
||||||
|
|
||||||
|
LogRocket.init("rome-online/rome-online");
|
||||||
if (client.getTreatment("LogRocket_Tracking") === "on") {
|
if (client.getTreatment("LogRocket_Tracking") === "on") {
|
||||||
console.log("LR Start");
|
LogRocket.init("rome-online/rome-online");
|
||||||
LogRocket.init("gvfvfw/bodyshopapp");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [bodyshop, client, currentUser.authorized]);
|
}, [bodyshop, client, currentUser.authorized]);
|
||||||
@@ -112,7 +112,7 @@ export function App({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Suspense fallback={<LoadingSpinner message="ImEX Online" />}>
|
<Suspense fallback={<LoadingSpinner />}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<Route exact path="/" component={LandingPage} />
|
<Route exact path="/" component={LandingPage} />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
|||||||
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 { useApolloClient, useMutation } from "@apollo/client";
|
||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Button, Checkbox, Form, Modal, Space, notification } from "antd";
|
import { Button, Checkbox, Form, Modal, Space, notification } from "antd";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
@@ -63,9 +64,19 @@ function BillEnterModalContainer({
|
|||||||
"enter_bill_generate_label",
|
"enter_bill_generate_label",
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
const { Enhanced_Payroll } = useTreatments(
|
||||||
|
["Enhanced_Payroll"],
|
||||||
|
{},
|
||||||
|
bodyshop.imexshopid
|
||||||
|
);
|
||||||
const formValues = useMemo(() => {
|
const formValues = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
...billEnterModal.context.bill,
|
...billEnterModal.context.bill,
|
||||||
|
//Added as a part of IO-2436 for capturing parts price changes.
|
||||||
|
billlines: billEnterModal.context?.bill?.billlines?.map((line) => ({
|
||||||
|
...line,
|
||||||
|
original_actual_price: line.actual_price,
|
||||||
|
})),
|
||||||
jobid:
|
jobid:
|
||||||
(billEnterModal.context.job && billEnterModal.context.job.id) || null,
|
(billEnterModal.context.job && billEnterModal.context.job.id) || null,
|
||||||
federal_tax_rate:
|
federal_tax_rate:
|
||||||
@@ -99,6 +110,7 @@ function BillEnterModalContainer({
|
|||||||
} = values;
|
} = values;
|
||||||
|
|
||||||
let adjustmentsToInsert = {};
|
let adjustmentsToInsert = {};
|
||||||
|
let payrollAdjustmentsToInsert = [];
|
||||||
|
|
||||||
const r1 = await insertBill({
|
const r1 = await insertBill({
|
||||||
variables: {
|
variables: {
|
||||||
@@ -114,14 +126,33 @@ function BillEnterModalContainer({
|
|||||||
lbr_adjustment,
|
lbr_adjustment,
|
||||||
location: lineLocation,
|
location: lineLocation,
|
||||||
part_type,
|
part_type,
|
||||||
|
create_ppc,
|
||||||
|
original_actual_price,
|
||||||
...restI
|
...restI
|
||||||
} = i;
|
} = i;
|
||||||
|
|
||||||
if (deductedfromlbr) {
|
if (Enhanced_Payroll.treatment === "on") {
|
||||||
adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] =
|
if (
|
||||||
(adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] || 0) -
|
deductedfromlbr &&
|
||||||
restI.actual_price / lbr_adjustment.rate;
|
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 {
|
return {
|
||||||
...restI,
|
...restI,
|
||||||
deductedfromlbr: deductedfromlbr,
|
deductedfromlbr: deductedfromlbr,
|
||||||
@@ -147,6 +178,20 @@ function BillEnterModalContainer({
|
|||||||
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID", "GET_JOB_BY_PK"],
|
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID", "GET_JOB_BY_PK"],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
payrollAdjustmentsToInsert.map((li) => {
|
||||||
|
return updateJobLines({
|
||||||
|
variables: {
|
||||||
|
lineId: li.id,
|
||||||
|
line: {
|
||||||
|
convertedtolbr: li.convertedtolbr,
|
||||||
|
convertedtolbr_data: li.convertedtolbr_data,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const adjKeys = Object.keys(adjustmentsToInsert);
|
const adjKeys = Object.keys(adjustmentsToInsert);
|
||||||
if (adjKeys.length > 0) {
|
if (adjKeys.length > 0) {
|
||||||
//Query the adjustments, merge, and update them.
|
//Query the adjustments, merge, and update them.
|
||||||
@@ -255,6 +300,14 @@ function BillEnterModalContainer({
|
|||||||
location: li.location || location,
|
location: li.location || location,
|
||||||
status:
|
status:
|
||||||
bodyshop.md_order_statuses.default_received || "Received*",
|
bodyshop.md_order_statuses.default_received || "Received*",
|
||||||
|
//Added parts price changes.
|
||||||
|
...(li.create_ppc &&
|
||||||
|
li.original_actual_price !== li.actual_price
|
||||||
|
? {
|
||||||
|
act_price_before_ppc: li.original_actual_price,
|
||||||
|
act_price: li.actual_price,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -323,12 +376,12 @@ function BillEnterModalContainer({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (enterAgain) {
|
if (enterAgain) {
|
||||||
form.resetFields();
|
// form.resetFields();
|
||||||
form.resetFields();
|
|
||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
...formValues,
|
...formValues,
|
||||||
billlines: [],
|
billlines: [],
|
||||||
});
|
});
|
||||||
|
form.resetFields();
|
||||||
} else {
|
} else {
|
||||||
toggleModalVisible();
|
toggleModalVisible();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,19 +79,19 @@ export function BillFormComponent({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFederalTaxExemptSwitchToggle = (checked) => {
|
// const handleFederalTaxExemptSwitchToggle = (checked) => {
|
||||||
// Early gate
|
// // Early gate
|
||||||
if (!checked) return;
|
// if (!checked) return;
|
||||||
const values = form.getFieldsValue("billlines");
|
// const values = form.getFieldsValue("billlines");
|
||||||
// Gate bill lines
|
// // Gate bill lines
|
||||||
if (!values?.billlines?.length) return;
|
// if (!values?.billlines?.length) return;
|
||||||
|
|
||||||
const billlines = values.billlines.map((b) => {
|
// const billlines = values.billlines.map((b) => {
|
||||||
b.applicable_taxes.federal = false;
|
// b.applicable_taxes.federal = false;
|
||||||
return b;
|
// return b;
|
||||||
});
|
// });
|
||||||
form.setFieldsValue({ billlines });
|
// form.setFieldsValue({ billlines });
|
||||||
};
|
// };
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (job) form.validateFields(["is_credit_memo"]);
|
if (job) form.validateFields(["is_credit_memo"]);
|
||||||
@@ -380,13 +380,15 @@ export function BillFormComponent({
|
|||||||
)}
|
)}
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow>
|
<LayoutFormRow>
|
||||||
<Form.Item
|
{
|
||||||
span={3}
|
// <Form.Item
|
||||||
label={t("bills.fields.federal_tax_rate")}
|
// span={3}
|
||||||
name="federal_tax_rate"
|
// label={t("bills.fields.federal_tax_rate")}
|
||||||
>
|
// name="federal_tax_rate"
|
||||||
<CurrencyInput min={0} disabled={disabled} />
|
// >
|
||||||
</Form.Item>
|
// <CurrencyInput min={0} disabled={disabled} />
|
||||||
|
// </Form.Item>
|
||||||
|
}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
span={3}
|
span={3}
|
||||||
label={t("bills.fields.state_tax_rate")}
|
label={t("bills.fields.state_tax_rate")}
|
||||||
@@ -394,22 +396,27 @@ export function BillFormComponent({
|
|||||||
>
|
>
|
||||||
<CurrencyInput min={0} disabled={disabled} />
|
<CurrencyInput min={0} disabled={disabled} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
{
|
||||||
span={3}
|
// <Form.Item
|
||||||
label={t("bills.fields.local_tax_rate")}
|
// span={3}
|
||||||
name="local_tax_rate"
|
// label={t("bills.fields.local_tax_rate")}
|
||||||
>
|
// name="local_tax_rate"
|
||||||
<CurrencyInput min={0} />
|
// >
|
||||||
</Form.Item>
|
// <CurrencyInput min={0} />
|
||||||
{bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? (
|
// </Form.Item>
|
||||||
<Form.Item
|
}
|
||||||
span={2}
|
{
|
||||||
label={t("bills.labels.federal_tax_exempt")}
|
//Removed as a part of the merge to Rome Online. Federal tax not applicable.
|
||||||
name="federal_tax_exempt"
|
// bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? (
|
||||||
>
|
// <Form.Item
|
||||||
<Switch onChange={handleFederalTaxExemptSwitchToggle} />
|
// span={2}
|
||||||
</Form.Item>
|
// label={t("bills.labels.federal_tax_exempt")}
|
||||||
) : null}
|
// name="federal_tax_exempt"
|
||||||
|
// >
|
||||||
|
// <Switch onChange={handleFederalTaxExemptSwitchToggle} />
|
||||||
|
// </Form.Item>
|
||||||
|
// ) : null
|
||||||
|
}
|
||||||
<Form.Item shouldUpdate span={13}>
|
<Form.Item shouldUpdate span={13}>
|
||||||
{() => {
|
{() => {
|
||||||
const values = form.getFieldsValue([
|
const values = form.getFieldsValue([
|
||||||
@@ -435,21 +442,25 @@ export function BillFormComponent({
|
|||||||
value={totals.subtotal.toFormat()}
|
value={totals.subtotal.toFormat()}
|
||||||
precision={2}
|
precision={2}
|
||||||
/>
|
/>
|
||||||
<Statistic
|
{
|
||||||
title={t("bills.labels.federal_tax")}
|
// <Statistic
|
||||||
value={totals.federalTax.toFormat()}
|
// title={t("bills.labels.federal_tax")}
|
||||||
precision={2}
|
// value={totals.federalTax.toFormat()}
|
||||||
/>
|
// precision={2}
|
||||||
|
// />
|
||||||
|
}
|
||||||
<Statistic
|
<Statistic
|
||||||
title={t("bills.labels.state_tax")}
|
title={t("bills.labels.state_tax")}
|
||||||
value={totals.stateTax.toFormat()}
|
value={totals.stateTax.toFormat()}
|
||||||
precision={2}
|
precision={2}
|
||||||
/>
|
/>
|
||||||
<Statistic
|
{
|
||||||
title={t("bills.labels.local_tax")}
|
// <Statistic
|
||||||
value={totals.localTax.toFormat()}
|
// title={t("bills.labels.local_tax")}
|
||||||
precision={2}
|
// value={totals.localTax.toFormat()}
|
||||||
/>
|
// precision={2}
|
||||||
|
// />
|
||||||
|
}
|
||||||
<Statistic
|
<Statistic
|
||||||
title={t("bills.labels.entered_total")}
|
title={t("bills.labels.entered_total")}
|
||||||
value={totals.enteredTotal.toFormat()}
|
value={totals.enteredTotal.toFormat()}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
|
|||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
Checkbox,
|
||||||
Form,
|
Form,
|
||||||
Input,
|
Input,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
@@ -46,6 +47,13 @@ export function BillEnterModalLinesComponent({
|
|||||||
{},
|
{},
|
||||||
bodyshop && bodyshop.imexshopid
|
bodyshop && bodyshop.imexshopid
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { Enhanced_Payroll } = useTreatments(
|
||||||
|
["Enhanced_Payroll"],
|
||||||
|
{},
|
||||||
|
bodyshop.imexshopid
|
||||||
|
);
|
||||||
|
|
||||||
const columns = (remove) => {
|
const columns = (remove) => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -94,6 +102,7 @@ export function BillEnterModalLinesComponent({
|
|||||||
line_desc: opt.line_desc,
|
line_desc: opt.line_desc,
|
||||||
quantity: opt.part_qty || 1,
|
quantity: opt.part_qty || 1,
|
||||||
actual_price: opt.cost,
|
actual_price: opt.cost,
|
||||||
|
original_actual_price: opt.cost,
|
||||||
cost_center: opt.part_type
|
cost_center: opt.part_type
|
||||||
? bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid
|
? bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid
|
||||||
? opt.part_type !== "PAE"
|
? opt.part_type !== "PAE"
|
||||||
@@ -220,6 +229,43 @@ export function BillEnterModalLinesComponent({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
additional: (record, index) => (
|
||||||
|
<Form.Item
|
||||||
|
dependencies={["billlines", record.name, "actual_price"]}
|
||||||
|
noStyle
|
||||||
|
>
|
||||||
|
{() => {
|
||||||
|
const billLine = getFieldValue(["billlines", record.name]);
|
||||||
|
const jobLine = lineData.find(
|
||||||
|
(line) => line.id === billLine?.joblineid
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!billEdit &&
|
||||||
|
billLine &&
|
||||||
|
jobLine &&
|
||||||
|
billLine?.actual_price !== jobLine?.act_price
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<Space size="small">
|
||||||
|
<Form.Item
|
||||||
|
noStyle
|
||||||
|
label={t("joblines.fields.create_ppc")}
|
||||||
|
key={`${index}ppc`}
|
||||||
|
valuePropName="checked"
|
||||||
|
name={[record.name, "create_ppc"]}
|
||||||
|
>
|
||||||
|
<Checkbox />
|
||||||
|
</Form.Item>
|
||||||
|
{t("joblines.fields.create_ppc")}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("billlines.fields.actual_cost"),
|
title: t("billlines.fields.actual_cost"),
|
||||||
@@ -361,7 +407,7 @@ export function BillEnterModalLinesComponent({
|
|||||||
},
|
},
|
||||||
formInput: (record, index) => <Switch disabled={disabled} />,
|
formInput: (record, index) => <Switch disabled={disabled} />,
|
||||||
additional: (record, index) => (
|
additional: (record, index) => (
|
||||||
<Form.Item shouldUpdate style={{ display: "inline-block" }}>
|
<Form.Item shouldUpdate noStyle style={{ display: "inline-block" }}>
|
||||||
{() => {
|
{() => {
|
||||||
const price = getFieldValue([
|
const price = getFieldValue([
|
||||||
"billlines",
|
"billlines",
|
||||||
@@ -376,12 +422,31 @@ export function BillEnterModalLinesComponent({
|
|||||||
"rate",
|
"rate",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const billline = getFieldValue(["billlines", record.name]);
|
||||||
|
|
||||||
|
const jobline = lineData.find(
|
||||||
|
(line) => line.id === billline?.joblineid
|
||||||
|
);
|
||||||
|
|
||||||
|
const employeeTeamName = bodyshop.employee_teams.find(
|
||||||
|
(team) => team.id === jobline?.assigned_team
|
||||||
|
);
|
||||||
|
|
||||||
if (getFieldValue(["billlines", record.name, "deductedfromlbr"]))
|
if (getFieldValue(["billlines", record.name, "deductedfromlbr"]))
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<Space>
|
||||||
|
{t("joblines.fields.assigned_team", {
|
||||||
|
name: employeeTeamName?.name,
|
||||||
|
})}
|
||||||
|
{`${jobline.mod_lb_hrs} units/${t(
|
||||||
|
`joblines.fields.lbr_types.${jobline.mod_lbr_ty}`
|
||||||
|
)}`}
|
||||||
|
</Space>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("joblines.fields.mod_lbr_ty")}
|
label={t("joblines.fields.mod_lbr_ty")}
|
||||||
key={`${index}modlbrty`}
|
key={`${index}modlbrty`}
|
||||||
|
initialValue={jobline ? jobline.mod_lbr_ty : null}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
@@ -435,22 +500,44 @@ export function BillEnterModalLinesComponent({
|
|||||||
</Select.Option>
|
</Select.Option>
|
||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
{Enhanced_Payroll.treatment === "on" ? (
|
||||||
label={t("jobs.labels.adjustmentrate")}
|
<Form.Item
|
||||||
name={[record.name, "lbr_adjustment", "rate"]}
|
label={t("billlines.labels.mod_lbr_adjustment")}
|
||||||
initialValue={bodyshop.default_adjustment_rate}
|
name={[record.name, "lbr_adjustment", "mod_lb_hrs"]}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<InputNumber precision={2} min={0.01} />
|
<InputNumber
|
||||||
</Form.Item>
|
precision={5}
|
||||||
{price &&
|
min={0.01}
|
||||||
adjustmentRate &&
|
max={jobline ? jobline.mod_lb_hrs : 0}
|
||||||
`${(price / adjustmentRate).toFixed(1)} hrs`}
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
) : (
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.labels.adjustmentrate")}
|
||||||
|
name={[record.name, "lbr_adjustment", "rate"]}
|
||||||
|
initialValue={bodyshop.default_adjustment_rate}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber precision={2} min={0.01} />
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Space>
|
||||||
|
{price &&
|
||||||
|
adjustmentRate &&
|
||||||
|
`${(price / adjustmentRate).toFixed(1)} hrs`}
|
||||||
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
return <></>;
|
return <></>;
|
||||||
@@ -458,22 +545,21 @@ export function BillEnterModalLinesComponent({
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
title: t("billlines.fields.federal_tax_applicable"),
|
// title: t("billlines.fields.federal_tax_applicable"),
|
||||||
dataIndex: "applicable_taxes.federal",
|
// dataIndex: "applicable_taxes.federal",
|
||||||
editable: true,
|
// editable: true,
|
||||||
|
|
||||||
formItemProps: (field) => {
|
// formItemProps: (field) => {
|
||||||
return {
|
// return {
|
||||||
key: `${field.index}fedtax`,
|
// key: `${field.index}fedtax`,
|
||||||
valuePropName: "checked",
|
// valuePropName: "checked",
|
||||||
initialValue:
|
// // initialValue: true,
|
||||||
form.getFieldValue("federal_tax_exempt") === true ? false : true,
|
// name: [field.name, "applicable_taxes", "federal"],
|
||||||
name: [field.name, "applicable_taxes", "federal"],
|
// };
|
||||||
};
|
// },
|
||||||
},
|
// formInput: (record, index) => <Switch disabled={disabled} />,
|
||||||
formInput: (record, index) => <Switch disabled={disabled} />,
|
// },
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: t("billlines.fields.state_tax_applicable"),
|
title: t("billlines.fields.state_tax_applicable"),
|
||||||
dataIndex: "applicable_taxes.state",
|
dataIndex: "applicable_taxes.state",
|
||||||
@@ -488,20 +574,20 @@ export function BillEnterModalLinesComponent({
|
|||||||
},
|
},
|
||||||
formInput: (record, index) => <Switch disabled={disabled} />,
|
formInput: (record, index) => <Switch disabled={disabled} />,
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
title: t("billlines.fields.local_tax_applicable"),
|
// title: t("billlines.fields.local_tax_applicable"),
|
||||||
dataIndex: "applicable_taxes.local",
|
// dataIndex: "applicable_taxes.local",
|
||||||
editable: true,
|
// editable: true,
|
||||||
|
|
||||||
formItemProps: (field) => {
|
// formItemProps: (field) => {
|
||||||
return {
|
// return {
|
||||||
key: `${field.index}localtax`,
|
// key: `${field.index}localtax`,
|
||||||
valuePropName: "checked",
|
// valuePropName: "checked",
|
||||||
name: [field.name, "applicable_taxes", "local"],
|
// name: [field.name, "applicable_taxes", "local"],
|
||||||
};
|
// };
|
||||||
},
|
// },
|
||||||
formInput: (record, index) => <Switch disabled={disabled} />,
|
// formInput: (record, index) => <Switch disabled={disabled} />,
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
title: t("general.labels.actions"),
|
title: t("general.labels.actions"),
|
||||||
|
|
||||||
@@ -626,7 +712,7 @@ const EditableCell = ({
|
|||||||
if (additional)
|
if (additional)
|
||||||
return (
|
return (
|
||||||
<td {...restProps}>
|
<td {...restProps}>
|
||||||
<Space size="small">
|
<div size="small">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={dataIndex}
|
name={dataIndex}
|
||||||
labelCol={{ span: 0 }}
|
labelCol={{ span: 0 }}
|
||||||
@@ -635,7 +721,7 @@ const EditableCell = ({
|
|||||||
{(formInput && formInput(record, record.name)) || children}
|
{(formInput && formInput(record, record.name)) || children}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{additional && additional(record, record.name)}
|
{additional && additional(record, record.name)}
|
||||||
</Space>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
);
|
);
|
||||||
if (wrapper)
|
if (wrapper)
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ export const CalculateBillTotal = (invoice) => {
|
|||||||
}).multiply(i.quantity || 1);
|
}).multiply(i.quantity || 1);
|
||||||
|
|
||||||
subtotal = subtotal.add(itemTotal);
|
subtotal = subtotal.add(itemTotal);
|
||||||
if (i.applicable_taxes.federal) {
|
if (i.applicable_taxes?.federal) {
|
||||||
federalTax = federalTax.add(
|
federalTax = federalTax.add(
|
||||||
itemTotal.percentage(federal_tax_rate || 0)
|
itemTotal.percentage(federal_tax_rate || 0)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (i.applicable_taxes.state)
|
if (i.applicable_taxes?.state)
|
||||||
stateTax = stateTax.add(itemTotal.percentage(state_tax_rate || 0));
|
stateTax = stateTax.add(itemTotal.percentage(state_tax_rate || 0));
|
||||||
if (i.applicable_taxes.local)
|
if (i.applicable_taxes?.local)
|
||||||
localTax = localTax.add(itemTotal.percentage(local_tax_rate || 0));
|
localTax = localTax.add(itemTotal.percentage(local_tax_rate || 0));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -63,6 +63,12 @@ const BillLineSearchSelect = (
|
|||||||
item.oem_partno ? ` - ${item.oem_partno}` : ""
|
item.oem_partno ? ` - ${item.oem_partno}` : ""
|
||||||
}${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim()}
|
}${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim()}
|
||||||
</span>
|
</span>
|
||||||
|
{item.act_price === 0 && item.mod_lb_hrs > 0 && (
|
||||||
|
<span style={{ float: "right", paddingleft: "1rem" }}>
|
||||||
|
{`${item.mod_lb_hrs} units`}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
|
||||||
<span style={{ float: "right", paddingleft: "1rem" }}>
|
<span style={{ float: "right", paddingleft: "1rem" }}>
|
||||||
{item.act_price
|
{item.act_price
|
||||||
? `$${item.act_price && item.act_price.toFixed(2)}`
|
? `$${item.act_price && item.act_price.toFixed(2)}`
|
||||||
|
|||||||
@@ -21,7 +21,13 @@ export function DmsCdkMakesRefetch({ currentUser, bodyshop, form, socket }) {
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
if (!currentUser.email.includes("@imex.")) return null;
|
if (
|
||||||
|
!(
|
||||||
|
currentUser.email.includes("@imex.") ||
|
||||||
|
currentUser.email.includes("@rome.")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return null;
|
||||||
|
|
||||||
const handleRefetch = async () => {
|
const handleRefetch = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Select, Space, Tag } from "antd";
|
import { Select, Space, Tag } from "antd";
|
||||||
import React, { forwardRef } from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
//To be used as a form element only.
|
//To be used as a form element only.
|
||||||
|
|
||||||
const EmployeeSearchSelect = ({ options, ...props }, ref) => {
|
const EmployeeSearchSelect = ({ options, ...props }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -39,4 +39,4 @@ const EmployeeSearchSelect = ({ options, ...props }, ref) => {
|
|||||||
</Select>
|
</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);
|
||||||
@@ -40,22 +40,22 @@ class ErrorBoundary extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleErrorSubmit = () => {
|
handleErrorSubmit = () => {
|
||||||
window.$crisp.push([
|
// window.$crisp.push([
|
||||||
"do",
|
// "do",
|
||||||
"message:send",
|
// "message:send",
|
||||||
[
|
// [
|
||||||
"text",
|
// "text",
|
||||||
`I hit the following error: \n\n
|
// `I hit the following error: \n\n
|
||||||
${this.state.error.message}\n\n
|
// ${this.state.error.message}\n\n
|
||||||
${this.state.error.stack}\n\n
|
// ${this.state.error.stack}\n\n
|
||||||
URL:${window.location} as ${this.props.currentUser.email} for ${
|
// URL:${window.location} as ${this.props.currentUser.email} for ${
|
||||||
this.props.bodyshop && this.props.bodyshop.name
|
// this.props.bodyshop && this.props.bodyshop.name
|
||||||
}
|
// }
|
||||||
`,
|
// `,
|
||||||
],
|
// ],
|
||||||
]);
|
// ]);
|
||||||
|
|
||||||
window.$crisp.push(["do", "chat:open"]);
|
// window.$crisp.push(["do", "chat:open"]);
|
||||||
// const errorDescription = `**Please add relevant details about what you were doing before you encountered this issue**
|
// const errorDescription = `**Please add relevant details about what you were doing before you encountered this issue**
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
|
|||||||
@@ -1,9 +1,26 @@
|
|||||||
import Dinero from "dinero.js";
|
import Dinero from "dinero.js";
|
||||||
import React, { forwardRef } from "react";
|
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;
|
if (!value) return null;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
case "employee":
|
||||||
|
const emp = bodyshop.employees.find((e) => e.id === value);
|
||||||
|
return `${emp?.first_name} ${emp?.last_name}`;
|
||||||
|
|
||||||
case "text":
|
case "text":
|
||||||
return <div>{value}</div>;
|
return <div>{value}</div>;
|
||||||
case "currency":
|
case "currency":
|
||||||
@@ -14,4 +31,8 @@ const ReadOnlyFormItem = ({ value, type = "text", onChange }, ref) => {
|
|||||||
return <div>{value}</div>;
|
return <div>{value}</div>;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export default forwardRef(ReadOnlyFormItem);
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(forwardRef(ReadOnlyFormItem));
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import Icon, {
|
|||||||
FileFilled,
|
FileFilled,
|
||||||
//GlobalOutlined,
|
//GlobalOutlined,
|
||||||
HomeFilled,
|
HomeFilled,
|
||||||
ImportOutlined, InfoCircleOutlined,
|
ImportOutlined,
|
||||||
|
// InfoCircleOutlined,
|
||||||
LineChartOutlined,
|
LineChartOutlined,
|
||||||
PaperClipOutlined,
|
PaperClipOutlined,
|
||||||
PhoneOutlined,
|
PhoneOutlined,
|
||||||
@@ -26,8 +27,11 @@ import Icon, {
|
|||||||
UserOutlined,
|
UserOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
import {Layout, Menu, Switch, Tooltip} from "antd";
|
import {
|
||||||
import React, {useEffect, useState} from "react";
|
Layout,
|
||||||
|
Menu, // Switch, Tooltip
|
||||||
|
} from "antd";
|
||||||
|
import React from "react"; //, { useEffect, useState }
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { BsKanban } from "react-icons/bs";
|
import { BsKanban } from "react-icons/bs";
|
||||||
import {
|
import {
|
||||||
@@ -52,7 +56,7 @@ import {
|
|||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
selectCurrentUser,
|
selectCurrentUser,
|
||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
import {handleBeta, setBeta, checkBeta} from "../../utils/handleBeta";
|
//import { handleBeta, setBeta, checkBeta } from "../../utils/handleBeta";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
@@ -103,20 +107,20 @@ function Header({
|
|||||||
{},
|
{},
|
||||||
bodyshop && bodyshop.imexshopid
|
bodyshop && bodyshop.imexshopid
|
||||||
);
|
);
|
||||||
const [betaSwitch, setBetaSwitch] = useState(false);
|
//const [betaSwitch, setBetaSwitch] = useState(false);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
const isBeta = checkBeta();
|
// const isBeta = checkBeta();
|
||||||
setBetaSwitch(isBeta);
|
// setBetaSwitch(isBeta);
|
||||||
}, []);
|
// }, []);
|
||||||
|
|
||||||
const betaSwitchChange = (checked) => {
|
// const betaSwitchChange = (checked) => {
|
||||||
setBeta(checked);
|
// setBeta(checked);
|
||||||
setBetaSwitch(checked);
|
// setBetaSwitch(checked);
|
||||||
handleBeta();
|
// handleBeta();
|
||||||
}
|
// };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.Header>
|
<Layout.Header>
|
||||||
@@ -281,6 +285,13 @@ function Header({
|
|||||||
{t("menus.header.timetickets")}
|
{t("menus.header.timetickets")}
|
||||||
</Link>
|
</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
{bodyshop?.md_tasks_presets?.use_approvals && (
|
||||||
|
<Menu.Item key="ttapprovals" icon={<FieldTimeOutlined />}>
|
||||||
|
<Link to="/manage/ttapprovals">
|
||||||
|
{t("menus.header.ttapprovals")}
|
||||||
|
</Link>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
key="entertimetickets"
|
key="entertimetickets"
|
||||||
icon={<Icon component={GiPlayerTime} />}
|
icon={<Icon component={GiPlayerTime} />}
|
||||||
@@ -395,20 +406,12 @@ function Header({
|
|||||||
<Menu.Item
|
<Menu.Item
|
||||||
key="help"
|
key="help"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.open("https://help.imex.online/", "_blank");
|
window.open("https://rometech.com/", "_blank");
|
||||||
}}
|
}}
|
||||||
icon={<Icon component={QuestionCircleFilled} />}
|
icon={<Icon component={QuestionCircleFilled} />}
|
||||||
>
|
>
|
||||||
{t("menus.header.help")}
|
{t("menus.header.help")}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
|
||||||
key="rescue"
|
|
||||||
onClick={() => {
|
|
||||||
window.open("https://imexrescue.com/", "_blank");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t("menus.header.rescueme")}
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Item key="shiftclock">
|
<Menu.Item key="shiftclock">
|
||||||
<Link to="/manage/shiftclock">{t("menus.header.shiftclock")}</Link>
|
<Link to="/manage/shiftclock">{t("menus.header.shiftclock")}</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
@@ -444,17 +447,18 @@ function Header({
|
|||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
))}
|
))}
|
||||||
</Menu.SubMenu>
|
</Menu.SubMenu>
|
||||||
<Menu.Item style={{marginLeft: 'auto'}} key="profile">
|
{
|
||||||
<Tooltip title="A more modern ImEX Online is ready for you to try! You can switch back at any time.">
|
// <Menu.Item style={{marginLeft: 'auto'}} key="profile">
|
||||||
<InfoCircleOutlined/>
|
// <Tooltip title="A more modern ImEX Online is ready for you to try! You can switch back at any time.">
|
||||||
<span style={{marginRight: 8}}>Try the new ImEX Online</span>
|
// <InfoCircleOutlined/>
|
||||||
<Switch
|
// <span style={{marginRight: 8}}>Try the new ImEX Online</span>
|
||||||
checked={betaSwitch}
|
// <Switch
|
||||||
onChange={betaSwitchChange}
|
// checked={betaSwitch}
|
||||||
/>
|
// onChange={betaSwitchChange}
|
||||||
</Tooltip>
|
// />
|
||||||
</Menu.Item>
|
// </Tooltip>
|
||||||
|
// </Menu.Item>
|
||||||
|
}
|
||||||
</Menu>
|
</Menu>
|
||||||
</Layout.Header>
|
</Layout.Header>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -87,7 +87,8 @@ export default function JobBillsTotalComponent({
|
|||||||
const totalPartsSublet = Dinero(totals.parts.parts.total)
|
const totalPartsSublet = Dinero(totals.parts.parts.total)
|
||||||
.add(Dinero(totals.parts.sublets.total))
|
.add(Dinero(totals.parts.sublets.total))
|
||||||
.add(Dinero(totals.additional.shipping))
|
.add(Dinero(totals.additional.shipping))
|
||||||
.add(Dinero(totals.additional.towing));
|
.add(Dinero(totals.additional.towing))
|
||||||
|
.add(Dinero(totals.additional.additionalCosts));
|
||||||
|
|
||||||
const discrepancy = totalPartsSublet.subtract(billTotals);
|
const discrepancy = totalPartsSublet.subtract(billTotals);
|
||||||
|
|
||||||
|
|||||||
@@ -1,47 +1,52 @@
|
|||||||
import { Button, notification } from "antd";
|
import { Button, notification } from "antd";
|
||||||
import Axios from "axios";
|
import Axios from "axios";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useMutation } from "@apollo/client";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
export default function JobCalculateTotals({ job, disabled, refetch }) {
|
||||||
import Dinero from "dinero.js";
|
|
||||||
export default function JobCalculateTotals({ job, disabled }) {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [updateJob] = useMutation(UPDATE_JOB);
|
|
||||||
|
|
||||||
const handleCalculate = async () => {
|
const handleCalculate = async () => {
|
||||||
setLoading(true);
|
try {
|
||||||
const newTotals = (
|
setLoading(true);
|
||||||
await Axios.post("/job/totals", {
|
|
||||||
job: job,
|
|
||||||
})
|
|
||||||
).data;
|
|
||||||
|
|
||||||
const result = await updateJob({
|
await Axios.post("/job/totalsssu", {
|
||||||
refetchQueries: ["GET_JOB_BY_PK"],
|
id: job.id,
|
||||||
awaitRefetchQueries: true,
|
});
|
||||||
variables: {
|
|
||||||
jobId: job.id,
|
if (refetch) refetch();
|
||||||
job: {
|
// const result = await updateJob({
|
||||||
job_totals: newTotals,
|
// refetchQueries: ["GET_JOB_BY_PK"],
|
||||||
clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
|
// awaitRefetchQueries: true,
|
||||||
owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat(
|
// variables: {
|
||||||
"0.00"
|
// 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(
|
||||||
if (!!!result.errors) {
|
// "0.00"
|
||||||
notification["success"]({ message: t("jobs.successes.updated") });
|
// ),
|
||||||
} else {
|
// },
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// if (!!!result.errors) {
|
||||||
|
// notification["success"]({ message: t("jobs.successes.updated") });
|
||||||
|
// } else {
|
||||||
|
// notification["error"]({
|
||||||
|
// message: t("jobs.errors.updating", {
|
||||||
|
// error: JSON.stringify(result.errors),
|
||||||
|
// }),
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
} catch (error) {
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
message: t("jobs.errors.updating", {
|
message: t("jobs.errors.updating", {
|
||||||
error: JSON.stringify(result.errors),
|
error: JSON.stringify(error),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
}
|
}
|
||||||
setLoading(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const span = {
|
const span = {
|
||||||
sm: { span: 24 },
|
lg: { span: 24 },
|
||||||
md: { span: 12 },
|
xl: { span: 12 },
|
||||||
lg: { span: 8 },
|
xxl: { span: 8 },
|
||||||
};
|
};
|
||||||
|
|
||||||
export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
|
export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
|
||||||
@@ -137,12 +137,6 @@ export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
|
|||||||
data={data ? data.jobs_by_pk : null}
|
data={data ? data.jobs_by_pk : null}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col {...span}>
|
|
||||||
<JobDetailCardsPartsComponent
|
|
||||||
loading={loading}
|
|
||||||
data={data ? data.jobs_by_pk : null}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col {...span}>
|
<Col {...span}>
|
||||||
<JobDetailCardsNotesComponent
|
<JobDetailCardsNotesComponent
|
||||||
loading={loading}
|
loading={loading}
|
||||||
@@ -163,6 +157,12 @@ export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
|
|||||||
data={data ? data.jobs_by_pk : null}
|
data={data ? data.jobs_by_pk : null}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
|
<Col span={24}>
|
||||||
|
<JobDetailCardsPartsComponent
|
||||||
|
loading={loading}
|
||||||
|
data={data ? data.jobs_by_pk : null}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Card>
|
</Card>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -1,16 +1,119 @@
|
|||||||
|
import { Table } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import JobLineNotePopup from "../job-line-note-popup/job-line-note-popup.component";
|
||||||
import PartsStatusPie from "../parts-status-pie/parts-status-pie.component";
|
import PartsStatusPie from "../parts-status-pie/parts-status-pie.component";
|
||||||
import CardTemplate from "./job-detail-cards.template.component";
|
import CardTemplate from "./job-detail-cards.template.component";
|
||||||
|
|
||||||
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 { t } = useTranslation();
|
||||||
const { joblines_status } = data;
|
const { joblines_status } = data;
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.line_desc"),
|
||||||
|
dataIndex: "line_desc",
|
||||||
|
fixed: "left",
|
||||||
|
key: "line_desc",
|
||||||
|
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
||||||
|
onCell: (record) => ({
|
||||||
|
className: record.manual_line && "job-line-manual",
|
||||||
|
style: {
|
||||||
|
...(record.critical ? { boxShadow: " -.5em 0 0 #FFC107" } : {}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
width: "30%",
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.part_type"),
|
||||||
|
dataIndex: "part_type",
|
||||||
|
key: "part_type",
|
||||||
|
width: "15%",
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(
|
||||||
|
t(`joblines.fields.part_types.${a.part_type}`),
|
||||||
|
t(`joblines.fields.part_types.${b.part_type}`)
|
||||||
|
),
|
||||||
|
render: (text, record) =>
|
||||||
|
record.part_type
|
||||||
|
? t(`joblines.fields.part_types.${record.part_type}`)
|
||||||
|
: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.part_qty"),
|
||||||
|
dataIndex: "part_qty",
|
||||||
|
key: "part_qty",
|
||||||
|
width: "10%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.notes"),
|
||||||
|
dataIndex: "notes",
|
||||||
|
key: "notes",
|
||||||
|
render: (text, record) => (
|
||||||
|
<JobLineNotePopup disabled={jobRO} jobline={record} />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.location"),
|
||||||
|
dataIndex: "location",
|
||||||
|
key: "location",
|
||||||
|
sorter: (a, b) => alphaSort(a.location, b.location),
|
||||||
|
render: (text, record) => (
|
||||||
|
<JobLineLocationPopup jobline={record} disabled={jobRO} />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.status"),
|
||||||
|
dataIndex: "status",
|
||||||
|
key: "status",
|
||||||
|
sorter: (a, b) => alphaSort(a.status, b.status),
|
||||||
|
filters:
|
||||||
|
(data &&
|
||||||
|
data.joblines
|
||||||
|
?.map((l) => l.status)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s || "No Status*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.status),
|
||||||
|
render: (text, record) => (
|
||||||
|
<JobLineStatusPopup jobline={record} disabled={jobRO} />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<CardTemplate loading={loading} title={t("jobs.labels.cards.parts")}>
|
<CardTemplate loading={loading} title={t("jobs.labels.cards.parts")}>
|
||||||
<PartsStatusPie joblines_status={joblines_status} />
|
<PartsStatusPie joblines_status={joblines_status} />
|
||||||
|
<Table
|
||||||
|
key="id"
|
||||||
|
columns={columns}
|
||||||
|
dataSource={data ? data.joblines : []}
|
||||||
|
/>
|
||||||
</CardTemplate>
|
</CardTemplate>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,7 +8,18 @@ import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
|||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
|
||||||
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 { t } = useTranslation();
|
||||||
const { loading, error, data } = useQuery(GET_JOB_LINE_ORDERS, {
|
const { loading, error, data } = useQuery(GET_JOB_LINE_ORDERS, {
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
@@ -23,7 +34,7 @@ export default function JobLinesExpander({ jobline, jobid }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={24} lg={12}>
|
<Col md={24} lg={8}>
|
||||||
<Typography.Title level={4}>
|
<Typography.Title level={4}>
|
||||||
{t("parts_orders.labels.parts_orders")}
|
{t("parts_orders.labels.parts_orders")}
|
||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
@@ -49,7 +60,7 @@ export default function JobLinesExpander({ jobline, jobid }) {
|
|||||||
)}
|
)}
|
||||||
</Timeline>
|
</Timeline>
|
||||||
</Col>
|
</Col>
|
||||||
<Col md={24} lg={12}>
|
<Col md={24} lg={8}>
|
||||||
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
|
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
|
||||||
<Timeline>
|
<Timeline>
|
||||||
{data.billlines.length > 0 ? (
|
{data.billlines.length > 0 ? (
|
||||||
@@ -71,7 +82,7 @@ export default function JobLinesExpander({ jobline, jobid }) {
|
|||||||
</Col>
|
</Col>
|
||||||
<Col span={4}>
|
<Col span={4}>
|
||||||
<span>
|
<span>
|
||||||
{`${t("billlines.fields.actual_cost")}: `}
|
{`${t("billlines.fields.actual_cost")}: `}
|
||||||
<CurrencyFormatter>{line.actual_cost}</CurrencyFormatter>
|
<CurrencyFormatter>{line.actual_cost}</CurrencyFormatter>
|
||||||
</span>
|
</span>
|
||||||
</Col>
|
</Col>
|
||||||
@@ -89,6 +100,37 @@ export default function JobLinesExpander({ jobline, jobid }) {
|
|||||||
)}
|
)}
|
||||||
</Timeline>
|
</Timeline>
|
||||||
</Col>
|
</Col>
|
||||||
|
<Col md={24} lg={8}>
|
||||||
|
<Typography.Title level={4}>
|
||||||
|
{t("parts_dispatch.labels.parts_dispatch")}
|
||||||
|
</Typography.Title>
|
||||||
|
<Timeline>
|
||||||
|
{data.parts_dispatch_lines.length > 0 ? (
|
||||||
|
data.parts_dispatch_lines.map((line) => (
|
||||||
|
<Timeline.Item key={line.id}>
|
||||||
|
<Space split={<Divider type="vertical" />} wrap>
|
||||||
|
<Link to={`/manage/jobs/${jobid}?partsorderid=${line.id}`}>
|
||||||
|
{line.parts_dispatch.number}
|
||||||
|
</Link>
|
||||||
|
{
|
||||||
|
bodyshop.employees.find(
|
||||||
|
(e) => e.id === line.parts_dispatch.employeeid
|
||||||
|
)?.first_name
|
||||||
|
}
|
||||||
|
<Space>
|
||||||
|
{t("parts_dispatch_lines.fields.accepted_at")}
|
||||||
|
<DateFormatter>{line.accepted_at}</DateFormatter>
|
||||||
|
</Space>
|
||||||
|
</Space>
|
||||||
|
</Timeline.Item>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Timeline.Item>
|
||||||
|
{t("parts_orders.labels.notyetordered")}
|
||||||
|
</Timeline.Item>
|
||||||
|
)}
|
||||||
|
</Timeline>
|
||||||
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import { useMutation } from "@apollo/client";
|
||||||
|
import { Button, Form, notification, Popover, Tooltip } from "antd";
|
||||||
|
import { t } from "i18next";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { UPDATE_LINE_PPC } from "../../graphql/jobs-lines.queries";
|
||||||
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
|
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
|
||||||
|
import JobLineConvertToLabor from "../job-line-convert-to-labor/job-line-convert-to-labor.component";
|
||||||
|
import axios from "axios";
|
||||||
|
export default function JobLinesPartPriceChange({ job, line, refetch }) {
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [updatePartPrice] = useMutation(UPDATE_LINE_PPC);
|
||||||
|
|
||||||
|
const handleFinish = async (values) => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const result = await updatePartPrice({
|
||||||
|
variables: {
|
||||||
|
id: line.id,
|
||||||
|
jobline: {
|
||||||
|
act_price_before_ppc: line.act_price_before_ppc
|
||||||
|
? line.act_price_before_ppc
|
||||||
|
: line.act_price,
|
||||||
|
act_price: values.act_price,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await axios.post("/job/totalsssu", {
|
||||||
|
id: job.id,
|
||||||
|
});
|
||||||
|
if (result.errors) {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("joblines.errors.saving", {
|
||||||
|
error: JSON.stringify(result.errors),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (refetch) refetch();
|
||||||
|
} else {
|
||||||
|
notification.open({
|
||||||
|
type: "success",
|
||||||
|
message: t("joblines.successes.saved"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("joblines.errors.saving", { error: JSON.stringify(error) }),
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const popcontent = (
|
||||||
|
<Form layout="vertical" onFinish={handleFinish} initialValues={{ act_price: line.act_price }}>
|
||||||
|
<Form.Item
|
||||||
|
name="act_price"
|
||||||
|
label={t("jobs.labels.act_price_ppc")}
|
||||||
|
rules={[{ required: true }]}
|
||||||
|
>
|
||||||
|
<CurrencyFormItemComponent />
|
||||||
|
</Form.Item>
|
||||||
|
<Button loading={loading} htmlType="primary">
|
||||||
|
{t("general.actions.save")}
|
||||||
|
</Button>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<JobLineConvertToLabor jobline={line} job={job}>
|
||||||
|
<Popover trigger="click" disabled={line.manual_line} content={popcontent}>
|
||||||
|
<CurrencyFormatter>
|
||||||
|
{line.db_ref === "900510" || line.db_ref === "900511"
|
||||||
|
? line.prt_dsmk_m
|
||||||
|
: line.act_price}
|
||||||
|
</CurrencyFormatter>
|
||||||
|
{line.prt_dsmk_p && line.prt_dsmk_p !== 0 ? (
|
||||||
|
<span style={{ marginLeft: ".2rem" }}>{`(${line.prt_dsmk_p}%)`}</span>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
{line.act_price_before_ppc && line.act_price_before_ppc !== 0 ? (
|
||||||
|
<Tooltip title={t("jobs.labels.ppc")}>
|
||||||
|
<span style={{ marginLeft: ".2rem", color: "tomato" }}>
|
||||||
|
(
|
||||||
|
<CurrencyFormatter>{line.act_price_before_ppc}</CurrencyFormatter>
|
||||||
|
)
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
</Popover>
|
||||||
|
</JobLineConvertToLabor>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
DeleteFilled,
|
DeleteFilled,
|
||||||
|
EditFilled,
|
||||||
FilterFilled,
|
FilterFilled,
|
||||||
|
HomeOutlined,
|
||||||
|
MinusCircleTwoTone,
|
||||||
|
PlusCircleTwoTone,
|
||||||
SyncOutlined,
|
SyncOutlined,
|
||||||
WarningFilled,
|
WarningFilled,
|
||||||
EditFilled,
|
|
||||||
PlusCircleTwoTone,
|
|
||||||
MinusCircleTwoTone,
|
|
||||||
HomeOutlined,
|
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import {
|
import {
|
||||||
@@ -29,7 +29,6 @@ import { selectJobReadOnly } from "../../redux/application/application.selectors
|
|||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||||
import { onlyUnique } from "../../utils/arrayHelper";
|
import { onlyUnique } from "../../utils/arrayHelper";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
|
||||||
import { alphaSort } from "../../utils/sorters";
|
import { alphaSort } from "../../utils/sorters";
|
||||||
import JobLineLocationPopup from "../job-line-location-popup/job-line-location-popup.component";
|
import JobLineLocationPopup from "../job-line-location-popup/job-line-location-popup.component";
|
||||||
import JobLineNotePopup from "../job-line-note-popup/job-line-note-popup.component";
|
import JobLineNotePopup from "../job-line-note-popup/job-line-note-popup.component";
|
||||||
@@ -38,13 +37,18 @@ import JobLinesBillRefernece from "../job-lines-bill-reference/job-lines-bill-re
|
|||||||
// import AllocationsAssignmentContainer from "../allocations-assignment/allocations-assignment.container";
|
// import AllocationsAssignmentContainer from "../allocations-assignment/allocations-assignment.container";
|
||||||
// import AllocationsBulkAssignmentContainer from "../allocations-bulk-assignment/allocations-bulk-assignment.container";
|
// import AllocationsBulkAssignmentContainer from "../allocations-bulk-assignment/allocations-bulk-assignment.container";
|
||||||
// import AllocationsEmployeeLabelContainer from "../allocations-employee-label/allocations-employee-label.container";
|
// import AllocationsEmployeeLabelContainer from "../allocations-employee-label/allocations-employee-label.container";
|
||||||
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
|
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import JobCreateIOU from "../job-create-iou/job-create-iou.component";
|
|
||||||
import JobLinesExpander from "./job-lines-expander.component";
|
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import JobLineConvertToLabor from "../job-line-convert-to-labor/job-line-convert-to-labor.component";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import JobCreateIOU from "../job-create-iou/job-create-iou.component";
|
||||||
|
import JobSendPartPriceChangeComponent from "../job-send-parts-price-change/job-send-parts-price-change.component";
|
||||||
|
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
|
||||||
|
import JobLinesExpander from "./job-lines-expander.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 { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -76,8 +80,16 @@ export function JobLinesComponent({
|
|||||||
setBillEnterContext,
|
setBillEnterContext,
|
||||||
}) {
|
}) {
|
||||||
const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK);
|
const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK);
|
||||||
|
const { Enhanced_Payroll } = useTreatments(
|
||||||
|
["Enhanced_Payroll"],
|
||||||
|
{},
|
||||||
|
bodyshop.imexshopid
|
||||||
|
);
|
||||||
const [selectedLines, setSelectedLines] = useState([]);
|
const [selectedLines, setSelectedLines] = useState([]);
|
||||||
|
console.log(
|
||||||
|
"🚀 ~ file: job-lines.component.jsx:89 ~ selectedLines:",
|
||||||
|
selectedLines
|
||||||
|
);
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
sortedInfo: {},
|
sortedInfo: {},
|
||||||
filteredInfo: {},
|
filteredInfo: {},
|
||||||
@@ -121,10 +133,21 @@ export function JobLinesComponent({
|
|||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "oem_partno" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "oem_partno" && state.sortedInfo.order,
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
render: (text, record) =>
|
onCell: (record) => ({
|
||||||
`${record.oem_partno || ""} ${
|
className: record.manual_line && "job-line-manual",
|
||||||
record.alt_partno ? `(${record.alt_partno})` : ""
|
style: {
|
||||||
}`.trim(),
|
...(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"),
|
title: t("joblines.fields.op_code_desc"),
|
||||||
@@ -220,20 +243,7 @@ export function JobLinesComponent({
|
|||||||
state.sortedInfo.columnKey === "act_price" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "act_price" && state.sortedInfo.order,
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<JobLineConvertToLabor jobline={record} job={job}>
|
<JobLinesPartPriceChange line={record} job={job} refetch={refetch} />
|
||||||
<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>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -286,6 +296,23 @@ export function JobLinesComponent({
|
|||||||
state.sortedInfo.columnKey === "line_ind" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "line_ind" && state.sortedInfo.order,
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
},
|
},
|
||||||
|
...(Enhanced_Payroll.treatment === "on"
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.assigned_team"),
|
||||||
|
dataIndex: "assigned_team",
|
||||||
|
key: "assigned_team",
|
||||||
|
render: (text, record) => (
|
||||||
|
<JoblineTeamAssignment
|
||||||
|
disabled={jobRO}
|
||||||
|
jobline={record}
|
||||||
|
jobId={job.id}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
|
||||||
{
|
{
|
||||||
title: t("joblines.fields.notes"),
|
title: t("joblines.fields.notes"),
|
||||||
dataIndex: "notes",
|
dataIndex: "notes",
|
||||||
@@ -404,7 +431,11 @@ export function JobLinesComponent({
|
|||||||
setSelectedLines((selectedLines) =>
|
setSelectedLines((selectedLines) =>
|
||||||
_.uniq([
|
_.uniq([
|
||||||
...selectedLines,
|
...selectedLines,
|
||||||
...jobLines.filter((item) => markedTypes.includes(item.part_type)),
|
...jobLines.filter(
|
||||||
|
(item) =>
|
||||||
|
markedTypes.includes(item.part_type) ||
|
||||||
|
markedTypes.includes(item.mod_lbr_ty)
|
||||||
|
),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -417,6 +448,21 @@ export function JobLinesComponent({
|
|||||||
<Menu.Item key="PAL">{t("joblines.fields.part_types.PAL")}</Menu.Item>
|
<Menu.Item key="PAL">{t("joblines.fields.part_types.PAL")}</Menu.Item>
|
||||||
<Menu.Item key="PAS">{t("joblines.fields.part_types.PAS")}</Menu.Item>
|
<Menu.Item key="PAS">{t("joblines.fields.part_types.PAS")}</Menu.Item>
|
||||||
<Menu.Divider />
|
<Menu.Divider />
|
||||||
|
<Menu.Item key="LAA">{t("joblines.fields.lbr_types.LAA")}</Menu.Item>
|
||||||
|
<Menu.Item key="LAB">{t("joblines.fields.lbr_types.LAB")}</Menu.Item>
|
||||||
|
<Menu.Item key="LAD">{t("joblines.fields.lbr_types.LAD")}</Menu.Item>
|
||||||
|
<Menu.Item key="LAE">{t("joblines.fields.lbr_types.LAE")}</Menu.Item>
|
||||||
|
<Menu.Item key="LAF">{t("joblines.fields.lbr_types.LAF")}</Menu.Item>
|
||||||
|
<Menu.Item key="LAG">{t("joblines.fields.lbr_types.LAG")}</Menu.Item>
|
||||||
|
<Menu.Item key="LAM">{t("joblines.fields.lbr_types.LAM")}</Menu.Item>
|
||||||
|
<Menu.Item key="LAR">{t("joblines.fields.lbr_types.LAR")}</Menu.Item>
|
||||||
|
<Menu.Item key="LAS">{t("joblines.fields.lbr_types.LAS")}</Menu.Item>
|
||||||
|
<Menu.Item key="LAU">{t("joblines.fields.lbr_types.LAU")}</Menu.Item>
|
||||||
|
<Menu.Item key="LA1">{t("joblines.fields.lbr_types.LA1")}</Menu.Item>
|
||||||
|
<Menu.Item key="LA2">{t("joblines.fields.lbr_types.LA2")}</Menu.Item>
|
||||||
|
<Menu.Item key="LA3">{t("joblines.fields.lbr_types.LA3")}</Menu.Item>
|
||||||
|
<Menu.Item key="LA4">{t("joblines.fields.lbr_types.LA2")}</Menu.Item>
|
||||||
|
<Menu.Divider />
|
||||||
<Menu.Item key="clear">{t("general.labels.clear")}</Menu.Item>
|
<Menu.Item key="clear">{t("general.labels.clear")}</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
@@ -440,6 +486,18 @@ export function JobLinesComponent({
|
|||||||
</Space>
|
</Space>
|
||||||
</Tag>
|
</Tag>
|
||||||
)}
|
)}
|
||||||
|
<JobLineDispatchButton
|
||||||
|
selectedLines={selectedLines}
|
||||||
|
setSelectedLines={setSelectedLines}
|
||||||
|
job={job}
|
||||||
|
/>
|
||||||
|
{Enhanced_Payroll.treatment === "on" && (
|
||||||
|
<JobLineBulkAssignComponent
|
||||||
|
selectedLines={selectedLines}
|
||||||
|
setSelectedLines={setSelectedLines}
|
||||||
|
job={job}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
disabled={
|
disabled={
|
||||||
(job && !job.converted) ||
|
(job && !job.converted) ||
|
||||||
@@ -448,15 +506,6 @@ export function JobLinesComponent({
|
|||||||
technician
|
technician
|
||||||
}
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// setPartsOrderContext({
|
|
||||||
// actions: { refetch: refetch },
|
|
||||||
// context: {
|
|
||||||
// jobId: job.id,
|
|
||||||
// job: job,
|
|
||||||
// linesToOrder: selectedLines,
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
|
|
||||||
setBillEnterContext({
|
setBillEnterContext({
|
||||||
actions: { refetch: refetch },
|
actions: { refetch: refetch },
|
||||||
context: {
|
context: {
|
||||||
@@ -563,6 +612,9 @@ export function JobLinesComponent({
|
|||||||
>
|
>
|
||||||
{t("joblines.actions.new")}
|
{t("joblines.actions.new")}
|
||||||
</Button>
|
</Button>
|
||||||
|
{bodyshop.region_config.toLowerCase().startsWith("us") && (
|
||||||
|
<JobSendPartPriceChangeComponent job={job} />
|
||||||
|
)}
|
||||||
<JobCreateIOU job={job} selectedJobLines={selectedLines} />
|
<JobCreateIOU job={job} selectedJobLines={selectedLines} />
|
||||||
<Input.Search
|
<Input.Search
|
||||||
placeholder={t("general.labels.search")}
|
placeholder={t("general.labels.search")}
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
|
||||||
|
import { useMutation } from "@apollo/client";
|
||||||
|
import { Button, Form, Popover, Select, Space, notification } from "antd";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { UPDATE_LINE_BULK_ASSIGN } from "../../graphql/jobs-lines.queries";
|
||||||
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
|
import {
|
||||||
|
selectBodyshop,
|
||||||
|
selectCurrentUser,
|
||||||
|
} from "../../redux/user/user.selectors";
|
||||||
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
jobRO: selectJobReadOnly,
|
||||||
|
currentUser: selectCurrentUser,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
insertAuditTrail: ({ jobid, operation }) =>
|
||||||
|
dispatch(insertAuditTrail({ jobid, operation })),
|
||||||
|
});
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(JoblineBulkAssign);
|
||||||
|
|
||||||
|
export function JoblineBulkAssign({
|
||||||
|
setSelectedLines,
|
||||||
|
selectedLines,
|
||||||
|
insertAuditTrail,
|
||||||
|
bodyshop,
|
||||||
|
jobRO,
|
||||||
|
job,
|
||||||
|
currentUser,
|
||||||
|
}) {
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [assignLines] = useMutation(UPDATE_LINE_BULK_ASSIGN);
|
||||||
|
|
||||||
|
const handleConvert = async (values) => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const result = await assignLines({
|
||||||
|
variables: {
|
||||||
|
jobline: {
|
||||||
|
assigned_team: values.assigned_team,
|
||||||
|
},
|
||||||
|
ids: selectedLines.map((l) => l.id),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (result.errors) {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("parts_dispatch.errors.creating", {
|
||||||
|
error: JSON.stringify(result.errors),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
//Insert the audit trail here.
|
||||||
|
|
||||||
|
const teamName = bodyshop.employee_teams.find(
|
||||||
|
(et) => et.id === values.assigned_team
|
||||||
|
)?.name;
|
||||||
|
|
||||||
|
const hours = selectedLines.reduce(
|
||||||
|
(acc, val) => (acc += val.mod_lb_hrs),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid: job.id,
|
||||||
|
operation: AuditTrailMapping.assignedlinehours(
|
||||||
|
teamName,
|
||||||
|
hours.toFixed(1)
|
||||||
|
),
|
||||||
|
});
|
||||||
|
setSelectedLines([]);
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("parts_dispatch.errors.creating", {
|
||||||
|
error: error,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const popMenu = (
|
||||||
|
<div>
|
||||||
|
<Form layout="vertical" form={form} onFinish={handleConvert}>
|
||||||
|
<Form.Item
|
||||||
|
name={"assigned_team"}
|
||||||
|
label={t("joblines.fields.assigned_team")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
showSearch
|
||||||
|
style={{ width: 200 }}
|
||||||
|
optionFilterProp="children"
|
||||||
|
filterOption={(input, option) =>
|
||||||
|
option.props.children
|
||||||
|
.toLowerCase()
|
||||||
|
.indexOf(input.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{bodyshop.employee_teams.map((team) => (
|
||||||
|
<Select.Option value={team.id} key={team.id} name={team.name}>
|
||||||
|
{team.name}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Space wrap>
|
||||||
|
<Button type="danger" onClick={() => form.submit()} loading={loading}>
|
||||||
|
{t("general.actions.save")}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => setVisible(false)}>
|
||||||
|
{t("general.actions.cancel")}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover open={visible} content={popMenu}>
|
||||||
|
<Button
|
||||||
|
disabled={selectedLines.length === 0 || jobRO}
|
||||||
|
loading={loading}
|
||||||
|
onClick={() => setVisible(true)}
|
||||||
|
>
|
||||||
|
{t("joblines.actions.assign_team", { count: selectedLines.length })}
|
||||||
|
</Button>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
|
||||||
|
import { useMutation } from "@apollo/client";
|
||||||
|
import { Button, Form, Popover, Select, Space, notification } from "antd";
|
||||||
|
import moment from "moment";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { INSERT_PARTS_DISPATCH } from "../../graphql/parts-dispatch.queries";
|
||||||
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
|
import {
|
||||||
|
selectBodyshop,
|
||||||
|
selectCurrentUser,
|
||||||
|
} from "../../redux/user/user.selectors";
|
||||||
|
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||||
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
jobRO: selectJobReadOnly,
|
||||||
|
currentUser: selectCurrentUser,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(JobLineDispatchButton);
|
||||||
|
|
||||||
|
export function JobLineDispatchButton({
|
||||||
|
setSelectedLines,
|
||||||
|
selectedLines,
|
||||||
|
|
||||||
|
bodyshop,
|
||||||
|
jobRO,
|
||||||
|
job,
|
||||||
|
currentUser,
|
||||||
|
}) {
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const Templates = TemplateList("job_special", {
|
||||||
|
ro_number: job.ro_number,
|
||||||
|
});
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [dispatchLines] = useMutation(INSERT_PARTS_DISPATCH);
|
||||||
|
|
||||||
|
const handleConvert = async (values) => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
//THIS HAS NOT YET BEEN TESTED. START BY FINISHING THIS FUNCTION.
|
||||||
|
const result = await dispatchLines({
|
||||||
|
variables: {
|
||||||
|
partsDispatch: {
|
||||||
|
dispatched_at: moment(),
|
||||||
|
employeeid: values.employeeid,
|
||||||
|
jobid: job.id,
|
||||||
|
dispatched_by: currentUser.email,
|
||||||
|
parts_dispatch_lines: {
|
||||||
|
data: selectedLines.map((l) => ({
|
||||||
|
joblineid: l.id,
|
||||||
|
quantity: l.part_qty,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
//joblineids: selectedLines.map((l) => l.id),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (result.errors) {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("parts_dispatch.errors.creating", {
|
||||||
|
error: result.errors,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setSelectedLines([]);
|
||||||
|
await GenerateDocument(
|
||||||
|
{
|
||||||
|
name: Templates.parts_dispatch.key,
|
||||||
|
variables: {
|
||||||
|
id: result.data.insert_parts_dispatch_one.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
"p"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
setVisible(false);
|
||||||
|
} catch (error) {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("parts_dispatch.errors.creating", {
|
||||||
|
error: error,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const popMenu = (
|
||||||
|
<div>
|
||||||
|
<Form layout="vertical" form={form} onFinish={handleConvert}>
|
||||||
|
<Form.Item
|
||||||
|
name={"employeeid"}
|
||||||
|
label={t("timetickets.fields.employee")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
showSearch
|
||||||
|
style={{ width: 200 }}
|
||||||
|
optionFilterProp="children"
|
||||||
|
filterOption={(input, option) =>
|
||||||
|
option.props.children
|
||||||
|
.toLowerCase()
|
||||||
|
.indexOf(input.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{bodyshop.employees
|
||||||
|
.filter((emp) => emp.active)
|
||||||
|
.map((emp) => (
|
||||||
|
<Select.Option
|
||||||
|
value={emp.id}
|
||||||
|
key={emp.id}
|
||||||
|
name={`${emp.first_name} ${emp.last_name}`}
|
||||||
|
>
|
||||||
|
{`${emp.first_name} ${emp.last_name}`}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Space wrap>
|
||||||
|
<Button
|
||||||
|
type="danger"
|
||||||
|
onClick={() => form.submit()}
|
||||||
|
loading={loading}
|
||||||
|
disabled={selectedLines.length === 0}
|
||||||
|
>
|
||||||
|
{t("general.actions.save")}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => setVisible(false)}>
|
||||||
|
{t("general.actions.cancel")}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover open={visible} content={popMenu}>
|
||||||
|
<Button
|
||||||
|
disabled={selectedLines.length === 0 || jobRO}
|
||||||
|
loading={loading}
|
||||||
|
onClick={() => setVisible(true)}
|
||||||
|
>
|
||||||
|
{t("joblines.actions.dispatchparts", { count: selectedLines.length })}
|
||||||
|
</Button>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import { notification, Select } from "antd";
|
import { notification, Select, Space } from "antd";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -77,7 +77,10 @@ export function JobLineLocationPopup({ bodyshop, jobline, disabled }) {
|
|||||||
style={{ width: "100%", minHeight: "2rem", cursor: "pointer" }}
|
style={{ width: "100%", minHeight: "2rem", cursor: "pointer" }}
|
||||||
onClick={() => !disabled && setEditing(true)}
|
onClick={() => !disabled && setEditing(true)}
|
||||||
>
|
>
|
||||||
{jobline.location}
|
<Space wrap>
|
||||||
|
{jobline.location}
|
||||||
|
{jobline.parts_dispatch_lines?.length > 0 && "-Disp"}
|
||||||
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -19,7 +19,8 @@ export default function JobReconciliationModalComponent({ job, bills }) {
|
|||||||
|
|
||||||
const jobLineData = job.joblines.filter(
|
const jobLineData = job.joblines.filter(
|
||||||
(j) =>
|
(j) =>
|
||||||
(j.part_type !== null && j.part_type !== "PAE") ||
|
(j.part_type !== "PAE" && j.act_price !== 0 && j.part_qty !== 0) ||
|
||||||
|
j.misc_amt !== 0 ||
|
||||||
(j.line_desc &&
|
(j.line_desc &&
|
||||||
j.line_desc.toLowerCase().includes("towing") &&
|
j.line_desc.toLowerCase().includes("towing") &&
|
||||||
j.lbr_op === "OP13") ||
|
j.lbr_op === "OP13") ||
|
||||||
|
|||||||
@@ -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} />
|
<JobTotalsTableTotals job={job} />
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
{currentUser.email.includes("@imex.") && (
|
{(currentUser.email.includes("@imex.") ||
|
||||||
|
currentUser.email.includes("@rome.")) && (
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Card title="DEVELOPMENT USE ONLY">
|
<Card title="DEVELOPMENT USE ONLY">
|
||||||
<JobCalculateTotals job={job} disabled={jobRO} />
|
<JobCalculateTotals job={job} disabled={jobRO} />
|
||||||
|
|||||||
@@ -123,11 +123,10 @@ export default function JobTotalsTableLabor({ job }) {
|
|||||||
<Space>
|
<Space>
|
||||||
{t("jobs.labels.mapa")}
|
{t("jobs.labels.mapa")}
|
||||||
{job.materials &&
|
{job.materials &&
|
||||||
job.materials.mapa &&
|
job.materials.MAPA &&
|
||||||
job.materials.mapa.cal_maxdlr &&
|
job.materials.MAPA.cal_maxdlr !== undefined &&
|
||||||
job.materials.mapa.cal_maxdlr > 0 &&
|
|
||||||
t("jobs.labels.threshhold", {
|
t("jobs.labels.threshhold", {
|
||||||
amount: job.materials.mapa.cal_maxdlr,
|
amount: job.materials.MAPA.cal_maxdlr,
|
||||||
})}
|
})}
|
||||||
</Space>
|
</Space>
|
||||||
</Table.Summary.Cell>
|
</Table.Summary.Cell>
|
||||||
@@ -148,11 +147,10 @@ export default function JobTotalsTableLabor({ job }) {
|
|||||||
<Space wrap>
|
<Space wrap>
|
||||||
{t("jobs.labels.mash")}
|
{t("jobs.labels.mash")}
|
||||||
{job.materials &&
|
{job.materials &&
|
||||||
job.materials.mash &&
|
job.materials.MASH &&
|
||||||
job.materials.mash.cal_maxdlr &&
|
job.materials.MASH.cal_maxdlr !== undefined &&
|
||||||
job.materials.mash.cal_maxdlr > 0 &&
|
|
||||||
t("jobs.labels.threshhold", {
|
t("jobs.labels.threshhold", {
|
||||||
amount: job.materials.mash.cal_maxdlr,
|
amount: job.materials.MASH.cal_maxdlr,
|
||||||
})}
|
})}
|
||||||
</Space>
|
</Space>
|
||||||
</Table.Summary.Cell>
|
</Table.Summary.Cell>
|
||||||
|
|||||||
@@ -11,6 +11,22 @@ export default function JobTotalsTableParts({ job }) {
|
|||||||
filteredInfo: {},
|
filteredInfo: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const insuranceAdjustments = useMemo(() => {
|
||||||
|
if (!job.job_totals) return [];
|
||||||
|
if (!job.job_totals?.parts?.adjustments) return [];
|
||||||
|
const adjs = [];
|
||||||
|
Object.keys(job.job_totals?.parts?.adjustments).forEach((key) => {
|
||||||
|
if (Dinero(job.job_totals?.parts?.adjustments[key]).getAmount() !== 0) {
|
||||||
|
adjs.push({
|
||||||
|
id: key,
|
||||||
|
amount: Dinero(job.job_totals.parts.adjustments[key]),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return adjs;
|
||||||
|
}, [job.job_totals]);
|
||||||
|
|
||||||
const data = useMemo(() => {
|
const data = useMemo(() => {
|
||||||
return Object.keys(job.job_totals.parts.parts.list)
|
return Object.keys(job.job_totals.parts.parts.list)
|
||||||
.filter(
|
.filter(
|
||||||
@@ -74,11 +90,11 @@ export default function JobTotalsTableParts({ job }) {
|
|||||||
<Table.Summary.Cell>
|
<Table.Summary.Cell>
|
||||||
{t("jobs.labels.prt_dsmk_total")}
|
{t("jobs.labels.prt_dsmk_total")}
|
||||||
</Table.Summary.Cell>
|
</Table.Summary.Cell>
|
||||||
|
|
||||||
<Table.Summary.Cell align="right">
|
<Table.Summary.Cell align="right">
|
||||||
{Dinero(job.job_totals.parts.parts.prt_dsmk_total).toFormat()}
|
{Dinero(job.job_totals.parts.parts.prt_dsmk_total).toFormat()}
|
||||||
</Table.Summary.Cell>
|
</Table.Summary.Cell>
|
||||||
</Table.Summary.Row>
|
</Table.Summary.Row>
|
||||||
|
|
||||||
<Table.Summary.Row>
|
<Table.Summary.Row>
|
||||||
<Table.Summary.Cell>
|
<Table.Summary.Cell>
|
||||||
<strong>{t("jobs.labels.partstotal")}</strong>
|
<strong>{t("jobs.labels.partstotal")}</strong>
|
||||||
@@ -90,6 +106,24 @@ export default function JobTotalsTableParts({ job }) {
|
|||||||
</strong>
|
</strong>
|
||||||
</Table.Summary.Cell>
|
</Table.Summary.Cell>
|
||||||
</Table.Summary.Row>
|
</Table.Summary.Row>
|
||||||
|
{insuranceAdjustments.length > 0 && (
|
||||||
|
<Table.Summary.Row>
|
||||||
|
<Table.Summary.Cell colSpan={24}>
|
||||||
|
{t("jobs.labels.profileadjustments")}
|
||||||
|
</Table.Summary.Cell>
|
||||||
|
</Table.Summary.Row>
|
||||||
|
)}
|
||||||
|
{insuranceAdjustments.map((adj, idx) => (
|
||||||
|
<Table.Summary.Row key={idx}>
|
||||||
|
<Table.Summary.Cell>
|
||||||
|
{t(`jobs.fields.${adj.id.toLowerCase()}`)}
|
||||||
|
</Table.Summary.Cell>
|
||||||
|
|
||||||
|
<Table.Summary.Cell align="right">
|
||||||
|
{adj.amount.toFormat()}
|
||||||
|
</Table.Summary.Cell>
|
||||||
|
</Table.Summary.Row>
|
||||||
|
))}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -28,26 +28,109 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
|||||||
total: job.job_totals.totals.subtotal,
|
total: job.job_totals.totals.subtotal,
|
||||||
bold: true,
|
bold: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
key: t("jobs.labels.local_tax_amt"),
|
...(job.job_totals.totals.us_sales_tax_breakdown
|
||||||
total: job.job_totals.totals.local_tax,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: t("jobs.labels.state_tax_amt"),
|
|
||||||
total: job.job_totals.totals.state_tax,
|
|
||||||
},
|
|
||||||
...(bodyshop.region_config === "CA_BC"
|
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: t("jobs.fields.ca_bc_pvrt"),
|
key: `${
|
||||||
total: job.job_totals.additional.pvrt,
|
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: `${
|
||||||
{
|
bodyshop.md_responsibility_centers.taxes.tax_ty2?.tax_type2 ||
|
||||||
key: t("jobs.labels.federal_tax_amt"),
|
"T2"
|
||||||
total: job.job_totals.totals.federal_tax,
|
} - ${[
|
||||||
},
|
job.cieca_pft.ty2_rate1,
|
||||||
|
job.cieca_pft.ty2_rate2,
|
||||||
|
job.cieca_pft.ty2_rate3,
|
||||||
|
job.cieca_pft.ty2_rate4,
|
||||||
|
job.cieca_pft.ty2_rate5,
|
||||||
|
]
|
||||||
|
.filter((i) => i > 0)
|
||||||
|
.join(", ")}%`,
|
||||||
|
total: job.job_totals.totals.us_sales_tax_breakdown.ty2Tax,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: `${
|
||||||
|
bodyshop.md_responsibility_centers.taxes.tax_ty3?.tax_type3 ||
|
||||||
|
"T3"
|
||||||
|
} - ${[
|
||||||
|
job.cieca_pft.ty3_rate1,
|
||||||
|
job.cieca_pft.ty3_rate2,
|
||||||
|
job.cieca_pft.ty3_rate3,
|
||||||
|
job.cieca_pft.ty3_rate4,
|
||||||
|
job.cieca_pft.ty3_rate5,
|
||||||
|
]
|
||||||
|
.filter((i) => i > 0)
|
||||||
|
.join(", ")}%`,
|
||||||
|
total: job.job_totals.totals.us_sales_tax_breakdown.ty3Tax,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: `${
|
||||||
|
bodyshop.md_responsibility_centers.taxes.tax_ty4?.tax_type4 ||
|
||||||
|
"T4"
|
||||||
|
} - ${[
|
||||||
|
job.cieca_pft.ty4_rate1,
|
||||||
|
job.cieca_pft.ty4_rate2,
|
||||||
|
job.cieca_pft.ty4_rate3,
|
||||||
|
job.cieca_pft.ty4_rate4,
|
||||||
|
job.cieca_pft.ty4_rate5,
|
||||||
|
]
|
||||||
|
.filter((i) => i > 0)
|
||||||
|
.join(", ")}%`,
|
||||||
|
total: job.job_totals.totals.us_sales_tax_breakdown.ty4Tax,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: `${
|
||||||
|
bodyshop.md_responsibility_centers.taxes.tax_ty5?.tax_type5 ||
|
||||||
|
"TT"
|
||||||
|
} - ${[
|
||||||
|
job.cieca_pft.ty5_rate1,
|
||||||
|
job.cieca_pft.ty5_rate2,
|
||||||
|
job.cieca_pft.ty5_rate3,
|
||||||
|
job.cieca_pft.ty5_rate4,
|
||||||
|
job.cieca_pft.ty5_rate5,
|
||||||
|
]
|
||||||
|
.filter((i) => i > 0)
|
||||||
|
.join(", ")}%`,
|
||||||
|
total: job.job_totals.totals.us_sales_tax_breakdown.ty5Tax,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: t("jobs.labels.total_sales_tax"),
|
||||||
|
bold: true,
|
||||||
|
total: Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty1Tax)
|
||||||
|
.add(
|
||||||
|
Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty2Tax)
|
||||||
|
)
|
||||||
|
.add(
|
||||||
|
Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty3Tax)
|
||||||
|
)
|
||||||
|
.add(
|
||||||
|
Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty4Tax)
|
||||||
|
)
|
||||||
|
.add(
|
||||||
|
Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty5Tax)
|
||||||
|
).toJSON(),
|
||||||
|
},
|
||||||
|
].filter((item) => item.total.amount !== 0)
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
key: t("jobs.labels.state_tax_amt"),
|
||||||
|
total: job.job_totals.totals.state_tax,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
|
||||||
{
|
{
|
||||||
key: t("jobs.labels.total_repairs"),
|
key: t("jobs.labels.total_repairs"),
|
||||||
total: job.job_totals.totals.total_repairs,
|
total: job.job_totals.totals.total_repairs,
|
||||||
@@ -57,10 +140,10 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
|||||||
key: t("jobs.fields.ded_amt"),
|
key: t("jobs.fields.ded_amt"),
|
||||||
total: job.job_totals.totals.custPayable.deductible,
|
total: job.job_totals.totals.custPayable.deductible,
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
key: t("jobs.fields.federal_tax_payable"),
|
// key: t("jobs.fields.federal_tax_payable"),
|
||||||
total: job.job_totals.totals.custPayable.federal_tax,
|
// total: job.job_totals.totals.custPayable.federal_tax,
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
key: t("jobs.fields.other_amount_payable"),
|
key: t("jobs.fields.other_amount_payable"),
|
||||||
total: job.job_totals.totals.custPayable.other_customer_amount,
|
total: job.job_totals.totals.custPayable.other_customer_amount,
|
||||||
@@ -81,7 +164,7 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
|||||||
bold: true,
|
bold: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}, [job.job_totals, t, bodyshop.region_config]);
|
}, [job.job_totals, job.cieca_pft, t, bodyshop.md_responsibility_centers]);
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export const GetSupplementDelta = async (client, jobId, newLines) => {
|
|||||||
//Found a relevant matching line. Add it to lines to update.
|
//Found a relevant matching line. Add it to lines to update.
|
||||||
linesToUpdate.push({
|
linesToUpdate.push({
|
||||||
id: existingLines[matchingIndex].id,
|
id: existingLines[matchingIndex].id,
|
||||||
newData: { ...newLine, removed: false },
|
newData: { ...newLine, removed: false, act_price_before_ppc: null },
|
||||||
});
|
});
|
||||||
|
|
||||||
//Splice out item we found for performance.
|
//Splice out item we found for performance.
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import {
|
|||||||
useQuery,
|
useQuery,
|
||||||
} from "@apollo/client";
|
} from "@apollo/client";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Col, Row, notification } from "antd";
|
import { Button, Col, Row, notification } from "antd";
|
||||||
import Axios from "axios";
|
import Axios from "axios";
|
||||||
import Dinero from "dinero.js";
|
import _ from "lodash";
|
||||||
import moment from "moment-business-days";
|
import moment from "moment-business-days";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
@@ -31,7 +31,7 @@ import {
|
|||||||
selectCurrentUser,
|
selectCurrentUser,
|
||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
import confirmDialog from "../../utils/asyncConfirm";
|
//import confirmDialog from "../../utils/asyncConfirm";
|
||||||
import CriticalPartsScan from "../../utils/criticalPartsScan";
|
import CriticalPartsScan from "../../utils/criticalPartsScan";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import JobsAvailableScan from "../jobs-available-scan/jobs-available-scan.component";
|
import JobsAvailableScan from "../jobs-available-scan/jobs-available-scan.component";
|
||||||
@@ -100,14 +100,15 @@ export function JobsAvailableContainer({
|
|||||||
const modalSearchState = useState("");
|
const modalSearchState = useState("");
|
||||||
|
|
||||||
//Import Scenario
|
//Import Scenario
|
||||||
const onOwnerFindModalOk = async () => {
|
const onOwnerFindModalOk = async (lazyData) => {
|
||||||
logImEXEvent("job_import_new");
|
logImEXEvent("job_import_new");
|
||||||
|
|
||||||
setOwnerModalVisible(false);
|
setOwnerModalVisible(false);
|
||||||
|
|
||||||
setInsertLoading(true);
|
setInsertLoading(true);
|
||||||
|
const estData = replaceEmpty(
|
||||||
const estData = replaceEmpty(estDataRaw.data.available_jobs_by_pk);
|
lazyData?.available_jobs_by_pk || estDataRaw.data.available_jobs_by_pk
|
||||||
|
);
|
||||||
|
|
||||||
if (!(estData && estData.est_data)) {
|
if (!(estData && estData.est_data)) {
|
||||||
//We don't have the right data. Error!
|
//We don't have the right data. Error!
|
||||||
@@ -117,17 +118,21 @@ export function JobsAvailableContainer({
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// if (process.env.REACT_APP_COUNTRY === "USA") {
|
||||||
|
//Massage the CCC file set to remove duplicate UNQ_SEQ.
|
||||||
|
await ResolveCCCLineIssues(estData.est_data, bodyshop);
|
||||||
|
// } else {
|
||||||
//IO-539 Check for Parts Rate on PAL for SGI use case.
|
//IO-539 Check for Parts Rate on PAL for SGI use case.
|
||||||
await CheckTaxRates(estData.est_data, bodyshop);
|
await CheckTaxRates(estData.est_data, bodyshop);
|
||||||
|
// }
|
||||||
const newTotals = (
|
// const newTotals = (
|
||||||
await Axios.post("/job/totals", {
|
// await Axios.post("/job/totals", {
|
||||||
job: {
|
// job: {
|
||||||
...estData.est_data,
|
// ...estData.est_data,
|
||||||
joblines: estData.est_data.joblines.data,
|
// joblines: estData.est_data.joblines.data,
|
||||||
},
|
// },
|
||||||
})
|
// })
|
||||||
).data;
|
// ).data;
|
||||||
|
|
||||||
let existingVehicles;
|
let existingVehicles;
|
||||||
if (estData.est_data.v_vin) {
|
if (estData.est_data.v_vin) {
|
||||||
@@ -142,9 +147,9 @@ export function JobsAvailableContainer({
|
|||||||
|
|
||||||
const newJob = {
|
const newJob = {
|
||||||
...estData.est_data,
|
...estData.est_data,
|
||||||
clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
|
// clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
|
||||||
owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"),
|
// owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"),
|
||||||
job_totals: newTotals,
|
// job_totals: newTotals,
|
||||||
date_open: moment(),
|
date_open: moment(),
|
||||||
status: bodyshop.md_ro_statuses.default_imported,
|
status: bodyshop.md_ro_statuses.default_imported,
|
||||||
notes: {
|
notes: {
|
||||||
@@ -168,17 +173,23 @@ export function JobsAvailableContainer({
|
|||||||
delete newJob.vehicle;
|
delete newJob.vehicle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof newJob.kmin === "string") {
|
||||||
|
newJob.kmin = null;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const r = await insertNewJob({
|
const r = await insertNewJob({
|
||||||
variables: {
|
variables: {
|
||||||
job: newJob,
|
job: newJob,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
await Axios.post("/job/totalsssu", {
|
||||||
|
id: r.data.insert_jobs.returning[0].id,
|
||||||
|
});
|
||||||
|
|
||||||
if (CriticalPartsScanning.treatment === "on") {
|
if (CriticalPartsScanning.treatment === "on") {
|
||||||
CriticalPartsScan(r.data.insert_jobs.returning[0].id);
|
CriticalPartsScan(r.data.insert_jobs.returning[0].id);
|
||||||
}
|
}
|
||||||
|
|
||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("jobs.successes.created"),
|
message: t("jobs.successes.created"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -192,7 +203,7 @@ export function JobsAvailableContainer({
|
|||||||
operation: AuditTrailMapping.jobimported(),
|
operation: AuditTrailMapping.jobimported(),
|
||||||
});
|
});
|
||||||
|
|
||||||
deleteJob({
|
await deleteJob({
|
||||||
variables: { id: estData.id },
|
variables: { id: estData.id },
|
||||||
}).then((r) => {
|
}).then((r) => {
|
||||||
refetch();
|
refetch();
|
||||||
@@ -200,12 +211,12 @@ export function JobsAvailableContainer({
|
|||||||
});
|
});
|
||||||
|
|
||||||
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
||||||
} catch (err) {
|
} catch (r) {
|
||||||
//error while inserting
|
//error while inserting
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
message: t("jobs.errors.creating", { error: err.message }),
|
message: t("jobs.errors.creating", { error: r.message }),
|
||||||
});
|
});
|
||||||
refetch().catch((e) => {
|
refetch().catch((err) => {
|
||||||
console.error(
|
console.error(
|
||||||
`Something went wrong in jobs available table container - ${
|
`Something went wrong in jobs available table container - ${
|
||||||
err.message || ""
|
err.message || ""
|
||||||
@@ -214,6 +225,7 @@ export function JobsAvailableContainer({
|
|||||||
});
|
});
|
||||||
setInsertLoading(false);
|
setInsertLoading(false);
|
||||||
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -237,6 +249,7 @@ export function JobsAvailableContainer({
|
|||||||
let supp = replaceEmpty({ ...estData.est_data });
|
let supp = replaceEmpty({ ...estData.est_data });
|
||||||
//IO-539 Check for Parts Rate on PAL for SGI use case.
|
//IO-539 Check for Parts Rate on PAL for SGI use case.
|
||||||
await CheckTaxRates(supp, bodyshop);
|
await CheckTaxRates(supp, bodyshop);
|
||||||
|
await ResolveCCCLineIssues(supp, bodyshop);
|
||||||
|
|
||||||
if (updateSchComp.checked === true) {
|
if (updateSchComp.checked === true) {
|
||||||
if (updateSchComp.automatic === true) {
|
if (updateSchComp.automatic === true) {
|
||||||
@@ -428,6 +441,25 @@ export function JobsAvailableContainer({
|
|||||||
updateSchComp={updateSchComp}
|
updateSchComp={updateSchComp}
|
||||||
setSchComp={setSchComp}
|
setSchComp={setSchComp}
|
||||||
/>
|
/>
|
||||||
|
{currentUser.email.includes("@rome.") ||
|
||||||
|
currentUser.email.includes("@imex.") ? (
|
||||||
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
for (const record of data.available_jobs) {
|
||||||
|
//Query the data
|
||||||
|
console.log("Start Job", record.id);
|
||||||
|
const { data } = await loadEstData({
|
||||||
|
variables: { id: record.id },
|
||||||
|
});
|
||||||
|
console.log("Query has been awaited and is complete");
|
||||||
|
await onOwnerFindModalOk(data);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add all jobs as new.
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<JobsAvailableTableComponent
|
<JobsAvailableTableComponent
|
||||||
@@ -459,115 +491,158 @@ function replaceEmpty(someObj, replaceValue = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function CheckTaxRates(estData, bodyshop) {
|
async function CheckTaxRates(estData, bodyshop) {
|
||||||
//LKQ Check
|
// //LKQ Check
|
||||||
if (
|
// if (
|
||||||
!estData.parts_tax_rates?.PAL ||
|
// !estData.parts_tax_rates?.PAL ||
|
||||||
estData.parts_tax_rates?.PAL?.prt_tax_rt === null ||
|
// estData.parts_tax_rates?.PAL?.prt_tax_rt === null ||
|
||||||
estData.parts_tax_rates?.PAL?.prt_tax_rt === 0
|
// estData.parts_tax_rates?.PAL?.prt_tax_rt === 0
|
||||||
) {
|
// ) {
|
||||||
const res = await confirmDialog(
|
// const res = await confirmDialog(
|
||||||
`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.`
|
// `Rome Online has detected that there is a missing tax rate for LKQ parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
||||||
);
|
// );
|
||||||
if (res) {
|
// if (res) {
|
||||||
if (!estData.parts_tax_rates.PAL) {
|
// if (!estData.parts_tax_rates.PAL) {
|
||||||
estData.parts_tax_rates.PAL = {
|
// estData.parts_tax_rates.PAL = {
|
||||||
prt_discp: 0,
|
// prt_discp: 0,
|
||||||
prt_mktyp: true,
|
// prt_mktyp: true,
|
||||||
prt_mkupp: 0,
|
// prt_mkupp: 0,
|
||||||
prt_type: "PAL",
|
// prt_type: "PAL",
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
estData.parts_tax_rates.PAL.prt_tax_rt =
|
// estData.parts_tax_rates.PAL.prt_tax_rt =
|
||||||
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
// bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||||
estData.parts_tax_rates.PAL.prt_tax_in = true;
|
// estData.parts_tax_rates.PAL.prt_tax_in = true;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
//PAC Check
|
// //PAC Check
|
||||||
if (
|
// if (
|
||||||
!estData.parts_tax_rates?.PAC ||
|
// !estData.parts_tax_rates?.PAC ||
|
||||||
estData.parts_tax_rates?.PAC?.prt_tax_rt === null ||
|
// estData.parts_tax_rates?.PAC?.prt_tax_rt === null ||
|
||||||
estData.parts_tax_rates?.PAC?.prt_tax_rt === 0
|
// estData.parts_tax_rates?.PAC?.prt_tax_rt === 0
|
||||||
) {
|
// ) {
|
||||||
const res = await confirmDialog(
|
// const res = await confirmDialog(
|
||||||
`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.`
|
// `Rome Online has detected that there is a missing tax rate for rechromed parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
||||||
);
|
// );
|
||||||
if (res) {
|
// if (res) {
|
||||||
if (!estData.parts_tax_rates.PAC) {
|
// if (!estData.parts_tax_rates.PAC) {
|
||||||
estData.parts_tax_rates.PAC = {
|
// estData.parts_tax_rates.PAC = {
|
||||||
prt_discp: 0,
|
// prt_discp: 0,
|
||||||
prt_mktyp: true,
|
// prt_mktyp: true,
|
||||||
prt_mkupp: 0,
|
// prt_mkupp: 0,
|
||||||
prt_type: "PAC",
|
// prt_type: "PAC",
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
estData.parts_tax_rates.PAC.prt_tax_rt =
|
// estData.parts_tax_rates.PAC.prt_tax_rt =
|
||||||
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
// bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||||
estData.parts_tax_rates.PAC.prt_tax_in = true;
|
// estData.parts_tax_rates.PAC.prt_tax_in = true;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
//PAM Check
|
//PAM Check
|
||||||
if (
|
if (!estData.parts_tax_rates?.PAM) {
|
||||||
!estData.parts_tax_rates?.PAM ||
|
estData.parts_tax_rates.PAM = estData.parts_tax_rates.PAC;
|
||||||
estData.parts_tax_rates?.PAM?.prt_tax_rt === null ||
|
|
||||||
estData.parts_tax_rates?.PAM?.prt_tax_rt === 0
|
|
||||||
) {
|
|
||||||
const res = await confirmDialog(
|
|
||||||
`ImEX Online has detected that there is a missing tax rate for remanufactured parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
|
||||||
);
|
|
||||||
if (res) {
|
|
||||||
if (!estData.parts_tax_rates.PAM) {
|
|
||||||
estData.parts_tax_rates.PAM = {
|
|
||||||
prt_discp: 0,
|
|
||||||
prt_mktyp: true,
|
|
||||||
prt_mkupp: 0,
|
|
||||||
prt_type: "PAM",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
estData.parts_tax_rates.PAM.prt_tax_rt =
|
|
||||||
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
|
||||||
estData.parts_tax_rates.PAM.prt_tax_in = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
// //PAM Check
|
||||||
!estData.parts_tax_rates?.PAR ||
|
// if (
|
||||||
estData.parts_tax_rates?.PAR?.prt_tax_rt === null ||
|
// !estData.parts_tax_rates?.PAM ||
|
||||||
estData.parts_tax_rates?.PAR?.prt_tax_rt === 0
|
// 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 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(
|
||||||
);
|
// `Rome Online has detected that there is a missing tax rate for remanufactured parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
||||||
if (res) {
|
// );
|
||||||
if (!estData.parts_tax_rates.PAR) {
|
// if (res) {
|
||||||
estData.parts_tax_rates.PAR = {
|
// if (!estData.parts_tax_rates.PAM) {
|
||||||
prt_discp: 0,
|
// estData.parts_tax_rates.PAM = {
|
||||||
prt_mktyp: true,
|
// prt_discp: 0,
|
||||||
prt_mkupp: 0,
|
// prt_mktyp: true,
|
||||||
prt_type: "PAR",
|
// prt_mkupp: 0,
|
||||||
};
|
// prt_type: "PAM",
|
||||||
}
|
// };
|
||||||
estData.parts_tax_rates.PAR.prt_tax_rt =
|
// }
|
||||||
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
// estData.parts_tax_rates.PAM.prt_tax_rt =
|
||||||
estData.parts_tax_rates.PAR.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
|
||||||
|
// ) {
|
||||||
|
// const res = await confirmDialog(
|
||||||
|
// `Rome Online has detected that there is a missing tax rate for recored parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
||||||
|
// );
|
||||||
|
// if (res) {
|
||||||
|
// if (!estData.parts_tax_rates.PAR) {
|
||||||
|
// estData.parts_tax_rates.PAR = {
|
||||||
|
// prt_discp: 0,
|
||||||
|
// prt_mktyp: true,
|
||||||
|
// prt_mkupp: 0,
|
||||||
|
// prt_type: "PAR",
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// estData.parts_tax_rates.PAR.prt_tax_rt =
|
||||||
|
// bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||||
|
// estData.parts_tax_rates.PAR.prt_tax_in = true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
//IO-1387 If a sublet line is NOT R&R, use the labor tax. If it is, use the sublet tax rate.
|
//IO-1387 If a sublet line is NOT R&R, use the labor tax. If it is, use the sublet tax rate.
|
||||||
//Currently limited to SK shops only.
|
//Currently limited to SK shops only.
|
||||||
//if (bodyshop.region_config === "CA_SK") {
|
if (bodyshop.region_config === "CA_SK") {
|
||||||
estData.joblines.data.forEach((jl, index) => {
|
estData.joblines.data.forEach((jl, index) => {
|
||||||
if (
|
if (
|
||||||
(jl.part_type === "PASL" || jl.part_type === "PAS") &&
|
(jl.part_type === "PASL" || jl.part_type === "PAS") &&
|
||||||
jl.lbr_op !== "OP11"
|
jl.lbr_op !== "OP11"
|
||||||
) {
|
) {
|
||||||
estData.joblines.data[index].tax_part = jl.lbr_tax;
|
estData.joblines.data[index].tax_part = jl.lbr_tax;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Set markup lines and tax lines as taxable.
|
//Set markup lines and tax lines as taxable.
|
||||||
//900510 is a mark up. 900510 is a discount.
|
//900510 is a mark up. 900510 is a discount.
|
||||||
if (jl.db_ref === "900510") {
|
if (jl.db_ref === "900510") {
|
||||||
estData.joblines.data[index].tax_part = true;
|
estData.joblines.data[index].tax_part = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ResolveCCCLineIssues(estData, bodyshop) {
|
||||||
|
//Find all misc amounts, populate them to the act price.
|
||||||
|
//TODO Ensure that this doesnt get violated
|
||||||
|
//This needs to be done before cleansing unq_seq since some misc prices could move over.
|
||||||
|
estData.joblines.data.forEach((line) => {
|
||||||
|
if (line.misc_amt && line.misc_amt !== 0) {
|
||||||
|
line.act_price = line.act_price + line.misc_amt;
|
||||||
|
line.tax_part = !!line.misc_tax;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
//}
|
|
||||||
|
//Generate the list of duplicated UNQ_SEQ that will feed into the next section to scrub the lines.
|
||||||
|
const unqSeqHash = _.groupBy(estData.joblines.data, "unq_seq");
|
||||||
|
const duplicatedUnqSeq = Object.keys(unqSeqHash).filter(
|
||||||
|
(key) => unqSeqHash[key].length > 1
|
||||||
|
);
|
||||||
|
|
||||||
|
duplicatedUnqSeq.forEach((unq_seq) => {
|
||||||
|
//Keys are strings, convert to int.
|
||||||
|
const int_unq_seq = parseInt(unq_seq);
|
||||||
|
|
||||||
|
//When line splitting, the first line is always the non-refinish line. We will keep it as is.
|
||||||
|
//We will cleanse the second line, which is always the next line.
|
||||||
|
const nonRefLineIndex = estData.joblines.data.findIndex(
|
||||||
|
(line) => line.unq_seq === int_unq_seq
|
||||||
|
);
|
||||||
|
estData.joblines.data[nonRefLineIndex + 1] = {
|
||||||
|
...estData.joblines.data[nonRefLineIndex + 1],
|
||||||
|
part_type: null,
|
||||||
|
act_price: 0,
|
||||||
|
db_price: 0,
|
||||||
|
prt_dsmk_p: 0,
|
||||||
|
prt_dsmk_m: 0,
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ export function JobsCloseAutoAllocate({ bodyshop, joblines, form, disabled }) {
|
|||||||
ret.profitcenter_part = defaults.profits["MAPA"];
|
ret.profitcenter_part = defaults.profits["MAPA"];
|
||||||
} else if (lineDesc.includes("ats amount")) {
|
} else if (lineDesc.includes("ats amount")) {
|
||||||
ret.profitcenter_part = defaults.profits["ATS"];
|
ret.profitcenter_part = defaults.profits["ATS"];
|
||||||
|
} else if (jl.act_price > 0) {
|
||||||
|
ret.profitcenter_part = defaults.profits["PAO"];
|
||||||
} else {
|
} else {
|
||||||
ret.profitcenter_part = null;
|
ret.profitcenter_part = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Collapse, Form, Input, InputNumber, Select, Switch } from "antd";
|
import { Collapse, Form, Input, Select, Switch } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -12,6 +12,12 @@ import FormItemPhone, {
|
|||||||
} from "../form-items-formatted/phone-form-item.component";
|
} from "../form-items-formatted/phone-form-item.component";
|
||||||
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
|
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
|
||||||
import JobsDetailRatesParts from "../jobs-detail-rates/jobs-detail-rates.parts.component";
|
import JobsDetailRatesParts from "../jobs-detail-rates/jobs-detail-rates.parts.component";
|
||||||
|
|
||||||
|
import JobsDetailRatesLabor from "../jobs-detail-rates/jobs-detail-rates.labor.component";
|
||||||
|
import JobsDetailRatesMaterials from "../jobs-detail-rates/jobs-detail-rates.materials.component";
|
||||||
|
import JobsDetailRatesOther from "../jobs-detail-rates/jobs-detail-rates.other.component";
|
||||||
|
import JobsDetailRatesTaxes from "../jobs-detail-rates/jobs-detail-rates.taxes.component";
|
||||||
|
|
||||||
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
|
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
@@ -258,26 +264,28 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
|||||||
<CurrencyInput />
|
<CurrencyInput />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow>
|
{
|
||||||
<Form.Item
|
// <LayoutFormRow>
|
||||||
label={t("jobs.fields.federal_tax_rate")}
|
// <Form.Item
|
||||||
name="federal_tax_rate"
|
// label={t("jobs.fields.federal_tax_rate")}
|
||||||
>
|
// name="federal_tax_rate"
|
||||||
<InputNumber min={0} max={1} precision={2} />
|
// >
|
||||||
</Form.Item>
|
// <InputNumber min={0} max={1} precision={2} />
|
||||||
<Form.Item
|
// </Form.Item>
|
||||||
label={t("jobs.fields.state_tax_rate")}
|
// <Form.Item
|
||||||
name="state_tax_rate"
|
// label={t("jobs.fields.state_tax_rate")}
|
||||||
>
|
// name="state_tax_rate"
|
||||||
<InputNumber min={0} max={1} precision={2} />
|
// >
|
||||||
</Form.Item>
|
// <InputNumber min={0} max={1} precision={2} />
|
||||||
<Form.Item
|
// </Form.Item>
|
||||||
label={t("jobs.fields.local_tax_rate")}
|
// <Form.Item
|
||||||
name="local_tax_rate"
|
// label={t("jobs.fields.local_tax_rate")}
|
||||||
>
|
// name="local_tax_rate"
|
||||||
<InputNumber min={0} max={1} precision={2} />
|
// >
|
||||||
</Form.Item>
|
// <InputNumber min={0} max={1} precision={2} />
|
||||||
</LayoutFormRow>
|
// </Form.Item>
|
||||||
|
// </LayoutFormRow>
|
||||||
|
}
|
||||||
<LayoutFormRow>
|
<LayoutFormRow>
|
||||||
<Form.Item label={t("jobs.fields.rate_lab")} name="rate_lab">
|
<Form.Item label={t("jobs.fields.rate_lab")} name="rate_lab">
|
||||||
<CurrencyInput />
|
<CurrencyInput />
|
||||||
@@ -356,6 +364,10 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
|||||||
required={selected && true}
|
required={selected && true}
|
||||||
form={form}
|
form={form}
|
||||||
/>
|
/>
|
||||||
|
<JobsDetailRatesLabor form={form} />
|
||||||
|
<JobsDetailRatesMaterials form={form} />
|
||||||
|
<JobsDetailRatesOther form={form} />
|
||||||
|
<JobsDetailRatesTaxes form={form} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
dispatch(setModalContext({ context: context, modal: "jobCosting" })),
|
dispatch(setModalContext({ context: context, modal: "jobCosting" })),
|
||||||
setTimeTicketContext: (context) =>
|
setTimeTicketContext: (context) =>
|
||||||
dispatch(setModalContext({ context: context, modal: "timeTicket" })),
|
dispatch(setModalContext({ context: context, modal: "timeTicket" })),
|
||||||
|
setTimeTicketTaskContext: (context) =>
|
||||||
|
dispatch(setModalContext({ context: context, modal: "timeTicketTask" })),
|
||||||
setCardPaymentContext: (context) =>
|
setCardPaymentContext: (context) =>
|
||||||
dispatch(setModalContext({ context: context, modal: "cardPayment" })),
|
dispatch(setModalContext({ context: context, modal: "cardPayment" })),
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
insertAuditTrail: ({ jobid, operation }) =>
|
||||||
@@ -67,6 +69,7 @@ export function JobsDetailHeaderActions({
|
|||||||
setJobCostingContext,
|
setJobCostingContext,
|
||||||
jobRO,
|
jobRO,
|
||||||
setTimeTicketContext,
|
setTimeTicketContext,
|
||||||
|
setTimeTicketTaskContext,
|
||||||
setCardPaymentContext,
|
setCardPaymentContext,
|
||||||
insertAuditTrail,
|
insertAuditTrail,
|
||||||
}) {
|
}) {
|
||||||
@@ -282,6 +285,24 @@ export function JobsDetailHeaderActions({
|
|||||||
>
|
>
|
||||||
{t("timetickets.actions.enter")}
|
{t("timetickets.actions.enter")}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
{bodyshop.md_tasks_presets.enable_tasks && (
|
||||||
|
<Menu.Item
|
||||||
|
key="claimtimetickettasks"
|
||||||
|
disabled={
|
||||||
|
!job.converted ||
|
||||||
|
(!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced)
|
||||||
|
}
|
||||||
|
onClick={() => {
|
||||||
|
setTimeTicketTaskContext({
|
||||||
|
actions: {},
|
||||||
|
context: { jobid: job.id },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("timetickets.actions.claimtasks")}
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
key="enterpayments"
|
key="enterpayments"
|
||||||
disabled={!job.converted}
|
disabled={!job.converted}
|
||||||
|
|||||||
@@ -5,9 +5,13 @@ import { createStructuredSelector } from "reselect";
|
|||||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
import LaborAllocationsTableComponent from "../labor-allocations-table/labor-allocations-table.component";
|
import LaborAllocationsTableComponent from "../labor-allocations-table/labor-allocations-table.component";
|
||||||
import TimeTicketList from "../time-ticket-list/time-ticket-list.component";
|
import TimeTicketList from "../time-ticket-list/time-ticket-list.component";
|
||||||
|
import PayrollLaborAllocationsTable from "../labor-allocations-table/labor-allocations-table.payroll.component";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly,
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(JobsDetailLaborContainer);
|
export default connect(mapStateToProps, null)(JobsDetailLaborContainer);
|
||||||
@@ -48,6 +52,7 @@ const adjSpan = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function JobsDetailLaborContainer({
|
export function JobsDetailLaborContainer({
|
||||||
|
bodyshop,
|
||||||
jobRO,
|
jobRO,
|
||||||
job,
|
job,
|
||||||
jobId,
|
jobId,
|
||||||
@@ -58,6 +63,12 @@ export function JobsDetailLaborContainer({
|
|||||||
techConsole,
|
techConsole,
|
||||||
adjustments,
|
adjustments,
|
||||||
}) {
|
}) {
|
||||||
|
const { Enhanced_Payroll } = useTreatments(
|
||||||
|
["Enhanced_Payroll"],
|
||||||
|
{},
|
||||||
|
bodyshop.imexshopid
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
<Col {...ticketSpan}>
|
<Col {...ticketSpan}>
|
||||||
@@ -70,14 +81,28 @@ export function JobsDetailLaborContainer({
|
|||||||
jobId={jobId}
|
jobId={jobId}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col {...adjSpan}>
|
|
||||||
<LaborAllocationsTableComponent
|
{Enhanced_Payroll.treatment === "on" ? (
|
||||||
jobId={jobId}
|
<Col {...adjSpan}>
|
||||||
joblines={joblines}
|
<PayrollLaborAllocationsTable
|
||||||
timetickets={timetickets}
|
jobId={jobId}
|
||||||
adjustments={adjustments}
|
joblines={joblines}
|
||||||
/>
|
timetickets={timetickets}
|
||||||
</Col>
|
refetch={refetch}
|
||||||
|
adjustments={adjustments}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
) : (
|
||||||
|
<Col {...adjSpan}>
|
||||||
|
<LaborAllocationsTableComponent
|
||||||
|
jobId={jobId}
|
||||||
|
joblines={joblines}
|
||||||
|
timetickets={timetickets}
|
||||||
|
refetch={refetch}
|
||||||
|
adjustments={adjustments}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
</Row>
|
</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 JobBillsTotal from "../job-bills-total/job-bills-total.component";
|
||||||
import PartsOrderListTableComponent from "../parts-order-list-table/parts-order-list-table.component";
|
import PartsOrderListTableComponent from "../parts-order-list-table/parts-order-list-table.component";
|
||||||
import PartsOrderModal from "../parts-order-modal/parts-order-modal.container";
|
import PartsOrderModal from "../parts-order-modal/parts-order-modal.container";
|
||||||
|
import PartsDispatchTable from "../parts-dispatch-table/parts-dispatch-table.component";
|
||||||
|
|
||||||
export default function JobsDetailPliComponent({
|
export default function JobsDetailPliComponent({
|
||||||
job,
|
job,
|
||||||
billsQuery,
|
billsQuery,
|
||||||
handleBillOnRowClick,
|
handleBillOnRowClick,
|
||||||
handlePartsOrderOnRowClick,
|
handlePartsOrderOnRowClick,
|
||||||
|
handlePartsDispatchOnRowClick,
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -43,6 +45,13 @@ export default function JobsDetailPliComponent({
|
|||||||
billsQuery={billsQuery}
|
billsQuery={billsQuery}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
|
<Col span={24}>
|
||||||
|
<PartsDispatchTable
|
||||||
|
job={job}
|
||||||
|
handleOnRowClick={handlePartsDispatchOnRowClick}
|
||||||
|
billsQuery={billsQuery}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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 (
|
return (
|
||||||
<JobsDetailPliComponent
|
<JobsDetailPliComponent
|
||||||
job={job}
|
job={job}
|
||||||
billsQuery={billsQuery}
|
billsQuery={billsQuery}
|
||||||
handleBillOnRowClick={handleBillOnRowClick}
|
handleBillOnRowClick={handleBillOnRowClick}
|
||||||
handlePartsOrderOnRowClick={handlePartsOrderOnRowClick}
|
handlePartsOrderOnRowClick={handlePartsOrderOnRowClick}
|
||||||
|
handlePartsDispatchOnRowClick={handlePartsDispatchOnRowClick}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,21 @@
|
|||||||
import {
|
import { Divider, Form, Input, Select, Space, Switch, Tooltip } from "antd";
|
||||||
Divider,
|
|
||||||
Form,
|
|
||||||
Input,
|
|
||||||
InputNumber,
|
|
||||||
Select,
|
|
||||||
Space,
|
|
||||||
Switch,
|
|
||||||
Tooltip,
|
|
||||||
} from "antd";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CABCpvrtCalculator from "../ca-bc-pvrt-calculator/ca-bc-pvrt-calculator.component";
|
|
||||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||||
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
|
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
|
||||||
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
|
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
|
||||||
import FormRow from "../layout-form-row/layout-form-row.component";
|
import FormRow from "../layout-form-row/layout-form-row.component";
|
||||||
|
import JobsDetailRatesLabor from "./jobs-detail-rates.labor.component";
|
||||||
|
import JobsDetailRatesMaterials from "./jobs-detail-rates.materials.component";
|
||||||
|
import JobsDetailRatesOther from "./jobs-detail-rates.other.component";
|
||||||
import JobsDetailRatesParts from "./jobs-detail-rates.parts.component";
|
import JobsDetailRatesParts from "./jobs-detail-rates.parts.component";
|
||||||
|
import JobsDetailRatesTaxes from "./jobs-detail-rates.taxes.component";
|
||||||
|
import JobsDetailRatesProfileOVerride from "./jobs-detail-rates.profile-override.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly,
|
||||||
@@ -84,14 +80,7 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<CurrencyInput disabled={jobRO || bodyshop.cdk_dealerid} />
|
<CurrencyInput disabled={jobRO || bodyshop.cdk_dealerid} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{bodyshop.region_config === "CA_BC" && (
|
|
||||||
<Space align="center">
|
|
||||||
<Form.Item label={t("jobs.fields.ca_bc_pvrt")} name="ca_bc_pvrt">
|
|
||||||
<CurrencyInput disabled={jobRO} min={0} />
|
|
||||||
</Form.Item>
|
|
||||||
<CABCpvrtCalculator form={form} disabled={jobRO} />
|
|
||||||
</Space>
|
|
||||||
)}
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.auto_add_ats")}
|
label={t("jobs.fields.auto_add_ats")}
|
||||||
name="auto_add_ats"
|
name="auto_add_ats"
|
||||||
@@ -120,41 +109,7 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
|
|||||||
}}
|
}}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
|
||||||
<Form.Item
|
|
||||||
label={t("jobs.fields.federal_tax_rate")}
|
|
||||||
name="federal_tax_rate"
|
|
||||||
>
|
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("jobs.fields.state_tax_rate")}
|
|
||||||
name="state_tax_rate"
|
|
||||||
>
|
|
||||||
<InputNumber
|
|
||||||
min={0}
|
|
||||||
max={1}
|
|
||||||
precision={2}
|
|
||||||
disabled={jobRO}
|
|
||||||
autoComplete="new-password"
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("jobs.fields.local_tax_rate")}
|
|
||||||
name="local_tax_rate"
|
|
||||||
>
|
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
|
||||||
</Form.Item>
|
|
||||||
{bodyshop.region_config.toLowerCase().startsWith("ca") && (
|
|
||||||
<Form.Item
|
|
||||||
label={t("jobs.fields.ca_gst_registrant")}
|
|
||||||
name="ca_gst_registrant"
|
|
||||||
valuePropName="checked"
|
|
||||||
>
|
|
||||||
<Switch disabled={jobRO} />
|
|
||||||
</Form.Item>
|
|
||||||
)}
|
|
||||||
</FormRow>
|
|
||||||
<Divider
|
<Divider
|
||||||
orientation="left"
|
orientation="left"
|
||||||
type="horizontal"
|
type="horizontal"
|
||||||
@@ -242,7 +197,15 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
|
|||||||
<CurrencyInput min={0} disabled={jobRO} />
|
<CurrencyInput min={0} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
|
<Divider orientation="left">Tax Profile</Divider>
|
||||||
|
|
||||||
|
<JobsDetailRatesProfileOVerride form={form} />
|
||||||
|
|
||||||
<JobsDetailRatesParts form={form} />
|
<JobsDetailRatesParts form={form} />
|
||||||
|
<JobsDetailRatesLabor form={form} />
|
||||||
|
<JobsDetailRatesMaterials form={form} />
|
||||||
|
<JobsDetailRatesOther form={form} />
|
||||||
|
<JobsDetailRatesTaxes form={form} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,427 @@
|
|||||||
|
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,145 @@
|
|||||||
|
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,104 @@
|
|||||||
|
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);
|
||||||
@@ -29,7 +29,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||||
name={["parts_tax_rates", "PAA", "prt_discp"]}
|
name={["parts_tax_rates", "PAA", "prt_discp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||||
@@ -42,7 +42,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||||
name={["parts_tax_rates", "PAA", "prt_mkupp"]}
|
name={["parts_tax_rates", "PAA", "prt_mkupp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||||
@@ -68,18 +68,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>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||||
|
name={["parts_tax_rates", "PAA", "prt_tx_in1"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||||
|
name={["parts_tax_rates", "PAA", "prt_tx_in2"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||||
|
name={["parts_tax_rates", "PAA", "prt_tx_in3"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||||
|
name={["parts_tax_rates", "PAA", "prt_tx_in4"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||||
|
name={["parts_tax_rates", "PAA", "prt_tx_in5"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("joblines.fields.part_types.PAC")}>
|
<LayoutFormRow header={t("joblines.fields.part_types.PAC")}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||||
name={["parts_tax_rates", "PAC", "prt_discp"]}
|
name={["parts_tax_rates", "PAC", "prt_discp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||||
@@ -92,7 +132,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||||
name={["parts_tax_rates", "PAC", "prt_mkupp"]}
|
name={["parts_tax_rates", "PAC", "prt_mkupp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||||
@@ -118,18 +158,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>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||||
|
name={["parts_tax_rates", "PAC", "prt_tx_in1"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||||
|
name={["parts_tax_rates", "PAC", "prt_tx_in2"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||||
|
name={["parts_tax_rates", "PAC", "prt_tx_in3"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||||
|
name={["parts_tax_rates", "PAC", "prt_tx_in4"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||||
|
name={["parts_tax_rates", "PAC", "prt_tx_in5"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("joblines.fields.part_types.PAL")}>
|
<LayoutFormRow header={t("joblines.fields.part_types.PAL")}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||||
name={["parts_tax_rates", "PAL", "prt_discp"]}
|
name={["parts_tax_rates", "PAL", "prt_discp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||||
@@ -142,7 +222,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||||
name={["parts_tax_rates", "PAL", "prt_mkupp"]}
|
name={["parts_tax_rates", "PAL", "prt_mkupp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||||
@@ -168,18 +248,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>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||||
|
name={["parts_tax_rates", "PAL", "prt_tx_in1"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||||
|
name={["parts_tax_rates", "PAL", "prt_tx_in2"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||||
|
name={["parts_tax_rates", "PAL", "prt_tx_in3"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||||
|
name={["parts_tax_rates", "PAL", "prt_tx_in4"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||||
|
name={["parts_tax_rates", "PAL", "prt_tx_in5"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("joblines.fields.part_types.PAG")}>
|
<LayoutFormRow header={t("joblines.fields.part_types.PAG")}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||||
name={["parts_tax_rates", "PAG", "prt_discp"]}
|
name={["parts_tax_rates", "PAG", "prt_discp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||||
@@ -192,7 +312,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||||
name={["parts_tax_rates", "PAG", "prt_mkupp"]}
|
name={["parts_tax_rates", "PAG", "prt_mkupp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||||
@@ -218,18 +338,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>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||||
|
name={["parts_tax_rates", "PAG", "prt_tx_in1"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||||
|
name={["parts_tax_rates", "PAG", "prt_tx_in2"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||||
|
name={["parts_tax_rates", "PAG", "prt_tx_in3"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||||
|
name={["parts_tax_rates", "PAG", "prt_tx_in4"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||||
|
name={["parts_tax_rates", "PAG", "prt_tx_in5"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("joblines.fields.part_types.PAM")}>
|
<LayoutFormRow header={t("joblines.fields.part_types.PAM")}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||||
name={["parts_tax_rates", "PAM", "prt_discp"]}
|
name={["parts_tax_rates", "PAM", "prt_discp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||||
@@ -242,7 +402,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||||
name={["parts_tax_rates", "PAM", "prt_mkupp"]}
|
name={["parts_tax_rates", "PAM", "prt_mkupp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||||
@@ -268,18 +428,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>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||||
|
name={["parts_tax_rates", "PAM", "prt_tx_in1"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||||
|
name={["parts_tax_rates", "PAM", "prt_tx_in2"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||||
|
name={["parts_tax_rates", "PAM", "prt_tx_in3"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||||
|
name={["parts_tax_rates", "PAM", "prt_tx_in4"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||||
|
name={["parts_tax_rates", "PAM", "prt_tx_in5"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("joblines.fields.part_types.PAN")}>
|
<LayoutFormRow header={t("joblines.fields.part_types.PAN")}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||||
name={["parts_tax_rates", "PAN", "prt_discp"]}
|
name={["parts_tax_rates", "PAN", "prt_discp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||||
@@ -292,7 +492,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||||
name={["parts_tax_rates", "PAN", "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>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||||
@@ -318,18 +518,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>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||||
|
name={["parts_tax_rates", "PAN", "prt_tx_in1"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||||
|
name={["parts_tax_rates", "PAN", "prt_tx_in2"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||||
|
name={["parts_tax_rates", "PAN", "prt_tx_in3"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||||
|
name={["parts_tax_rates", "PAN", "prt_tx_in4"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||||
|
name={["parts_tax_rates", "PAN", "prt_tx_in5"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("joblines.fields.part_types.PAO")}>
|
<LayoutFormRow header={t("joblines.fields.part_types.PAO")}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||||
name={["parts_tax_rates", "PAO", "prt_discp"]}
|
name={["parts_tax_rates", "PAO", "prt_discp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||||
@@ -342,7 +582,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||||
name={["parts_tax_rates", "PAO", "prt_mkupp"]}
|
name={["parts_tax_rates", "PAO", "prt_mkupp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||||
@@ -368,18 +608,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>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||||
|
name={["parts_tax_rates", "PAO", "prt_tx_in1"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||||
|
name={["parts_tax_rates", "PAO", "prt_tx_in2"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||||
|
name={["parts_tax_rates", "PAO", "prt_tx_in3"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||||
|
name={["parts_tax_rates", "PAO", "prt_tx_in4"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||||
|
name={["parts_tax_rates", "PAO", "prt_tx_in5"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("joblines.fields.part_types.PAP")}>
|
<LayoutFormRow header={t("joblines.fields.part_types.PAP")}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||||
name={["parts_tax_rates", "PAP", "prt_discp"]}
|
name={["parts_tax_rates", "PAP", "prt_discp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||||
@@ -392,7 +672,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||||
name={["parts_tax_rates", "PAP", "prt_mkupp"]}
|
name={["parts_tax_rates", "PAP", "prt_mkupp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||||
@@ -418,18 +698,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>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||||
|
name={["parts_tax_rates", "PAP", "prt_tx_in1"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||||
|
name={["parts_tax_rates", "PAP", "prt_tx_in2"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||||
|
name={["parts_tax_rates", "PAP", "prt_tx_in3"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||||
|
name={["parts_tax_rates", "PAP", "prt_tx_in4"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||||
|
name={["parts_tax_rates", "PAP", "prt_tx_in5"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("joblines.fields.part_types.PAR")}>
|
<LayoutFormRow header={t("joblines.fields.part_types.PAR")}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||||
name={["parts_tax_rates", "PAR", "prt_discp"]}
|
name={["parts_tax_rates", "PAR", "prt_discp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||||
@@ -442,7 +762,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||||
name={["parts_tax_rates", "PAR", "prt_mkupp"]}
|
name={["parts_tax_rates", "PAR", "prt_mkupp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||||
@@ -468,18 +788,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>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||||
|
name={["parts_tax_rates", "PAR", "prt_tx_in1"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||||
|
name={["parts_tax_rates", "PAR", "prt_tx_in2"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||||
|
name={["parts_tax_rates", "PAR", "prt_tx_in3"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||||
|
name={["parts_tax_rates", "PAR", "prt_tx_in4"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||||
|
name={["parts_tax_rates", "PAR", "prt_tx_in5"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("joblines.fields.part_types.PAS")}>
|
<LayoutFormRow header={t("joblines.fields.part_types.PAS")}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||||
name={["parts_tax_rates", "PAS", "prt_discp"]}
|
name={["parts_tax_rates", "PAS", "prt_discp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||||
@@ -492,7 +852,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||||
name={["parts_tax_rates", "PAS", "prt_mkupp"]}
|
name={["parts_tax_rates", "PAS", "prt_mkupp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||||
@@ -518,18 +878,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>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||||
|
name={["parts_tax_rates", "PAS", "prt_tx_in1"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||||
|
name={["parts_tax_rates", "PAS", "prt_tx_in2"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||||
|
name={["parts_tax_rates", "PAS", "prt_tx_in3"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||||
|
name={["parts_tax_rates", "PAS", "prt_tx_in4"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||||
|
name={["parts_tax_rates", "PAS", "prt_tx_in5"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("joblines.fields.part_types.PASL")}>
|
<LayoutFormRow header={t("joblines.fields.part_types.PASL")}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||||
name={["parts_tax_rates", "PASL", "prt_discp"]}
|
name={["parts_tax_rates", "PASL", "prt_discp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||||
@@ -542,7 +942,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||||
name={["parts_tax_rates", "PASL", "prt_mkupp"]}
|
name={["parts_tax_rates", "PASL", "prt_mkupp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||||
@@ -568,18 +968,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>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
|
||||||
|
name={["parts_tax_rates", "PASL", "prt_tx_in1"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
|
||||||
|
name={["parts_tax_rates", "PASL", "prt_tx_in2"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
|
||||||
|
name={["parts_tax_rates", "PASL", "prt_tx_in3"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
|
||||||
|
name={["parts_tax_rates", "PASL", "prt_tx_in4"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
|
||||||
|
name={["parts_tax_rates", "PASL", "prt_tx_in5"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("joblines.fields.part_types.CCDR")}>
|
<LayoutFormRow header={t("joblines.fields.part_types.CCDR")}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||||
name={["parts_tax_rates", "CCDR", "prt_discp"]}
|
name={["parts_tax_rates", "CCDR", "prt_discp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||||
@@ -592,7 +1032,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||||
name={["parts_tax_rates", "CCDR", "prt_mkupp"]}
|
name={["parts_tax_rates", "CCDR", "prt_mkupp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||||
@@ -605,7 +1045,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
||||||
name={["parts_tax_rates", "CCDR", "prt_tax_rt"]}
|
name={["parts_tax_rates", "CCDR", "prt_tax_rt"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("joblines.fields.part_types.CCF")}>
|
<LayoutFormRow header={t("joblines.fields.part_types.CCF")}>
|
||||||
@@ -613,7 +1053,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||||
name={["parts_tax_rates", "CCF", "prt_discp"]}
|
name={["parts_tax_rates", "CCF", "prt_discp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||||
@@ -626,7 +1066,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||||
name={["parts_tax_rates", "CCF", "prt_mkupp"]}
|
name={["parts_tax_rates", "CCF", "prt_mkupp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||||
@@ -639,7 +1079,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
||||||
name={["parts_tax_rates", "CCF", "prt_tax_rt"]}
|
name={["parts_tax_rates", "CCF", "prt_tax_rt"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("joblines.fields.part_types.CCM")}>
|
<LayoutFormRow header={t("joblines.fields.part_types.CCM")}>
|
||||||
@@ -647,7 +1087,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||||
name={["parts_tax_rates", "CCM", "prt_discp"]}
|
name={["parts_tax_rates", "CCM", "prt_discp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||||
@@ -660,7 +1100,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||||
name={["parts_tax_rates", "CCM", "prt_mkupp"]}
|
name={["parts_tax_rates", "CCM", "prt_mkupp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||||
@@ -673,7 +1113,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
||||||
name={["parts_tax_rates", "CCM", "prt_tax_rt"]}
|
name={["parts_tax_rates", "CCM", "prt_tax_rt"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("joblines.fields.part_types.CCC")}>
|
<LayoutFormRow header={t("joblines.fields.part_types.CCC")}>
|
||||||
@@ -681,7 +1121,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||||
name={["parts_tax_rates", "CCC", "prt_discp"]}
|
name={["parts_tax_rates", "CCC", "prt_discp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||||
@@ -694,7 +1134,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||||
name={["parts_tax_rates", "CCC", "prt_mkupp"]}
|
name={["parts_tax_rates", "CCC", "prt_mkupp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||||
@@ -707,7 +1147,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
||||||
name={["parts_tax_rates", "CCC", "prt_tax_rt"]}
|
name={["parts_tax_rates", "CCC", "prt_tax_rt"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("joblines.fields.part_types.CCD")}>
|
<LayoutFormRow header={t("joblines.fields.part_types.CCD")}>
|
||||||
@@ -715,7 +1155,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
label={t("jobs.fields.parts_tax_rates.prt_discp")}
|
||||||
name={["parts_tax_rates", "CCD", "prt_discp"]}
|
name={["parts_tax_rates", "CCD", "prt_discp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
|
||||||
@@ -728,7 +1168,7 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
|
||||||
name={["parts_tax_rates", "CCD", "prt_mkupp"]}
|
name={["parts_tax_rates", "CCD", "prt_mkupp"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
|
||||||
@@ -741,39 +1181,39 @@ export function JobsDetailRatesParts({
|
|||||||
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
|
||||||
name={["parts_tax_rates", "CCD", "prt_tax_rt"]}
|
name={["parts_tax_rates", "CCD", "prt_tax_rt"]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow>
|
<LayoutFormRow>
|
||||||
<Form.Item label={t("jobs.fields.tax_tow_rt")} name="tax_tow_rt">
|
<Form.Item label={t("jobs.fields.tax_tow_rt")} name="tax_tow_rt">
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("jobs.fields.tax_str_rt")} name="tax_str_rt">
|
<Form.Item label={t("jobs.fields.tax_str_rt")} name="tax_str_rt">
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.tax_paint_mat_rt")}
|
label={t("jobs.fields.tax_paint_mat_rt")}
|
||||||
name="tax_paint_mat_rt"
|
name="tax_paint_mat_rt"
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.tax_shop_mat_rt")}
|
label={t("jobs.fields.tax_shop_mat_rt")}
|
||||||
name="tax_shop_mat_rt"
|
name="tax_shop_mat_rt"
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("jobs.fields.tax_sub_rt")} name="tax_sub_rt">
|
<Form.Item label={t("jobs.fields.tax_sub_rt")} name="tax_sub_rt">
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("jobs.fields.tax_lbr_rt")} name="tax_lbr_rt">
|
<Form.Item label={t("jobs.fields.tax_lbr_rt")} name="tax_lbr_rt">
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.tax_levies_rt")}
|
label={t("jobs.fields.tax_levies_rt")}
|
||||||
name="tax_levies_rt"
|
name="tax_levies_rt"
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
|
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
</Collapse.Panel>
|
</Collapse.Panel>
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
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,155 @@
|
|||||||
|
import { Collapse, Divider, Form, Input, InputNumber, Space } from "antd";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
jobRO: selectJobReadOnly,
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
|
||||||
|
export function JobsDetailRatesTaxes({
|
||||||
|
jobRO,
|
||||||
|
expanded,
|
||||||
|
bodyshop,
|
||||||
|
required = true,
|
||||||
|
form,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const formItems = [];
|
||||||
|
for (let tyCounter = 1; tyCounter <= 5; tyCounter++) {
|
||||||
|
const section = [];
|
||||||
|
|
||||||
|
section.push(
|
||||||
|
TaxFormItems({
|
||||||
|
typeNum: tyCounter,
|
||||||
|
rootElements: true,
|
||||||
|
bodyshop,
|
||||||
|
jobRO,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let iterator = 1; iterator <= 5; iterator++) {
|
||||||
|
section.push(
|
||||||
|
TaxFormItems({
|
||||||
|
typeNum: tyCounter,
|
||||||
|
typeNumIterator: iterator,
|
||||||
|
rootElements: false,
|
||||||
|
jobRO,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
formItems.push(Space({ children: section, wrap: true }));
|
||||||
|
formItems.push(<Divider />);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Collapse defaultActiveKey={expanded && "rates"}>
|
||||||
|
<Collapse.Panel
|
||||||
|
forceRender
|
||||||
|
header={t("jobs.labels.cieca_pft")}
|
||||||
|
key="cieca_pft"
|
||||||
|
>
|
||||||
|
{formItems}
|
||||||
|
</Collapse.Panel>
|
||||||
|
</Collapse>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default connect(mapStateToProps, null)(JobsDetailRatesTaxes);
|
||||||
|
|
||||||
|
function TaxFormItems({
|
||||||
|
typeNum,
|
||||||
|
typeNumIterator,
|
||||||
|
rootElements,
|
||||||
|
bodyshopjobRO,
|
||||||
|
jobRO,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
if (rootElements)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.responsibilitycenter_tax_type", {
|
||||||
|
typeNum,
|
||||||
|
typeNumIterator,
|
||||||
|
})}
|
||||||
|
// rules={[
|
||||||
|
// {
|
||||||
|
// required: true,
|
||||||
|
// //message: t("general.validation.required"),
|
||||||
|
// },
|
||||||
|
// ]}
|
||||||
|
name={["cieca_pft", `tax_type${typeNum}`]}
|
||||||
|
>
|
||||||
|
<Input disabled={jobRO} />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.responsibilitycenter_tax_tier", {
|
||||||
|
typeNum,
|
||||||
|
typeNumIterator,
|
||||||
|
})}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["cieca_pft", `ty${typeNum}_tier${typeNumIterator}`]}
|
||||||
|
>
|
||||||
|
<InputNumber precision={0} min={0} disabled={jobRO} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.responsibilitycenter_tax_thres", {
|
||||||
|
typeNum,
|
||||||
|
typeNumIterator,
|
||||||
|
})}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["cieca_pft", `ty${typeNum}_thres${typeNumIterator}`]}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} precision={2} disabled={jobRO} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.responsibilitycenter_tax_rate", {
|
||||||
|
typeNum,
|
||||||
|
typeNumIterator,
|
||||||
|
})}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["cieca_pft", `ty${typeNum}_rate${typeNumIterator}`]}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} precision={2} disabled={jobRO} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.responsibilitycenter_tax_sur", {
|
||||||
|
typeNum,
|
||||||
|
typeNumIterator,
|
||||||
|
})}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["cieca_pft", `ty${typeNum}_sur${typeNumIterator}`]}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} precision={2} disabled={jobRO} />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,333 @@
|
|||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Col,
|
||||||
|
Row,
|
||||||
|
Space,
|
||||||
|
Table,
|
||||||
|
Typography,
|
||||||
|
notification,
|
||||||
|
} from "antd";
|
||||||
|
import { SyncOutlined } from "@ant-design/icons";
|
||||||
|
import axios from "axios";
|
||||||
|
import _ from "lodash";
|
||||||
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
|
import "./labor-allocations-table.styles.scss";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
technician: selectTechnician,
|
||||||
|
});
|
||||||
|
|
||||||
|
export function PayrollLaborAllocationsTable({
|
||||||
|
jobId,
|
||||||
|
joblines,
|
||||||
|
timetickets,
|
||||||
|
bodyshop,
|
||||||
|
adjustments,
|
||||||
|
technician,
|
||||||
|
refetch,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [totals, setTotals] = useState([]);
|
||||||
|
const [state, setState] = useState({
|
||||||
|
sortedInfo: {
|
||||||
|
columnKey: "cost_center",
|
||||||
|
field: "cost_center",
|
||||||
|
order: "ascend",
|
||||||
|
},
|
||||||
|
filteredInfo: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function CalculateTotals() {
|
||||||
|
const { data } = await axios.post("/payroll/calculatelabor", {
|
||||||
|
jobid: jobId,
|
||||||
|
});
|
||||||
|
setTotals(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!joblines && !!timetickets && !!bodyshop) {
|
||||||
|
CalculateTotals();
|
||||||
|
}
|
||||||
|
if (!jobId) setTotals([]);
|
||||||
|
}, [joblines, timetickets, bodyshop, adjustments, jobId]);
|
||||||
|
|
||||||
|
const convertedLines = useMemo(
|
||||||
|
() => joblines && joblines.filter((j) => j.convertedtolbr),
|
||||||
|
[joblines]
|
||||||
|
);
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t("timetickets.fields.employee"),
|
||||||
|
dataIndex: "employeeid",
|
||||||
|
key: "employeeid",
|
||||||
|
render: (text, record) => {
|
||||||
|
if (record.employeeid === undefined) {
|
||||||
|
return (
|
||||||
|
<span style={{ color: "tomato", fontWeight: "bolder" }}>
|
||||||
|
{t("timetickets.labels.unassigned")}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const emp = bodyshop.employees.find((e) => e.id === record.employeeid);
|
||||||
|
return `${emp?.first_name} ${emp?.last_name}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.mod_lbr_ty"),
|
||||||
|
dataIndex: "mod_lbr_ty",
|
||||||
|
key: "mod_lbr_ty",
|
||||||
|
render: (text, record) =>
|
||||||
|
record.employeeid === undefined ? (
|
||||||
|
<span style={{ color: "tomato", fontWeight: "bolder" }}>
|
||||||
|
{t("timetickets.labels.unassigned")}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
t(`joblines.fields.lbr_types.${record.mod_lbr_ty?.toUpperCase()}`)
|
||||||
|
),
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: t("timetickets.fields.rate"),
|
||||||
|
// dataIndex: "rate",
|
||||||
|
// key: "rate",
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
title: t("jobs.labels.hrs_total"),
|
||||||
|
dataIndex: "expectedHours",
|
||||||
|
key: "expectedHours",
|
||||||
|
sorter: (a, b) => a.expectedHours - b.expectedHours,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "expectedHours" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
render: (text, record) => record.expectedHours.toFixed(5),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.labels.hrs_claimed"),
|
||||||
|
dataIndex: "claimedHours",
|
||||||
|
key: "claimedHours",
|
||||||
|
sorter: (a, b) => a.claimedHours - b.claimedHours,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "claimedHours" && state.sortedInfo.order,
|
||||||
|
render: (text, record) =>
|
||||||
|
record.claimedHours && record.claimedHours.toFixed(5),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.labels.difference"),
|
||||||
|
dataIndex: "difference",
|
||||||
|
|
||||||
|
key: "difference",
|
||||||
|
sorter: (a, b) => a.difference - b.difference,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "difference" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => {
|
||||||
|
const difference = _.round(
|
||||||
|
record.expectedHours - record.claimedHours,
|
||||||
|
5
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<strong
|
||||||
|
style={{
|
||||||
|
color: difference >= 0 ? "green" : "red",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{difference}
|
||||||
|
</strong>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const convertedTableCols = [
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.line_desc"),
|
||||||
|
dataIndex: "line_desc",
|
||||||
|
key: "line_desc",
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.op_code_desc"),
|
||||||
|
dataIndex: "op_code_desc",
|
||||||
|
key: "op_code_desc",
|
||||||
|
ellipsis: true,
|
||||||
|
render: (text, record) =>
|
||||||
|
`${record.op_code_desc || ""}${
|
||||||
|
record.alt_partm ? ` ${record.alt_partm}` : ""
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.act_price"),
|
||||||
|
dataIndex: "act_price",
|
||||||
|
key: "act_price",
|
||||||
|
ellipsis: true,
|
||||||
|
render: (text, record) => (
|
||||||
|
<>
|
||||||
|
<CurrencyFormatter>
|
||||||
|
{record.db_ref === "900510" || record.db_ref === "900511"
|
||||||
|
? record.prt_dsmk_m
|
||||||
|
: record.act_price}
|
||||||
|
</CurrencyFormatter>
|
||||||
|
{record.prt_dsmk_p && record.prt_dsmk_p !== 0 ? (
|
||||||
|
<span
|
||||||
|
style={{ marginLeft: ".2rem" }}
|
||||||
|
>{`(${record.prt_dsmk_p}%)`}</span>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.part_qty"),
|
||||||
|
dataIndex: "part_qty",
|
||||||
|
key: "part_qty",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.mod_lbr_ty"),
|
||||||
|
dataIndex: "conv_mod_lbr_ty",
|
||||||
|
key: "conv_mod_lbr_ty",
|
||||||
|
render: (text, record) =>
|
||||||
|
record.convertedtolbr_data && record.convertedtolbr_data.mod_lbr_ty,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.mod_lb_hrs"),
|
||||||
|
dataIndex: "conv_mod_lb_hrs",
|
||||||
|
key: "conv_mod_lb_hrs",
|
||||||
|
render: (text, record) =>
|
||||||
|
record.convertedtolbr_data &&
|
||||||
|
record.convertedtolbr_data.mod_lb_hrs &&
|
||||||
|
record.convertedtolbr_data.mod_lb_hrs.toFixed(5),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
|
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||||
|
};
|
||||||
|
|
||||||
|
const summary =
|
||||||
|
totals &&
|
||||||
|
totals.reduce(
|
||||||
|
(acc, val) => {
|
||||||
|
acc.hrs_total += val.expectedHours;
|
||||||
|
acc.hrs_claimed += val.claimedHours;
|
||||||
|
// acc.adjustments += val.adjustments;
|
||||||
|
acc.difference += val.expectedHours - val.claimedHours;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{ hrs_total: 0, hrs_claimed: 0, adjustments: 0, difference: 0 }
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col span={24}>
|
||||||
|
<Card
|
||||||
|
title={t("jobs.labels.laborallocations")}
|
||||||
|
extra={
|
||||||
|
<Space>
|
||||||
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
const response = await axios.post("/payroll/payall", {
|
||||||
|
jobid: jobId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.status === 200) {
|
||||||
|
if (response.data.success !== false) {
|
||||||
|
notification.open({
|
||||||
|
type: "success",
|
||||||
|
message: t("timetickets.successes.payall"),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("timetickets.errors.payall", {
|
||||||
|
error: response.data.error,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refetch) refetch();
|
||||||
|
} else {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("timetickets.errors.payall", {
|
||||||
|
error: JSON.stringify(""),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("timetickets.actions.payall")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
const { data } = await axios.post("/payroll/calculatelabor", {
|
||||||
|
jobid: jobId,
|
||||||
|
});
|
||||||
|
setTotals(data);
|
||||||
|
refetch();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SyncOutlined />
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
rowKey={(record) => `${record.employeeid} ${record.mod_lbr_ty}`}
|
||||||
|
pagination={false}
|
||||||
|
onChange={handleTableChange}
|
||||||
|
dataSource={totals}
|
||||||
|
scroll={{
|
||||||
|
x: true,
|
||||||
|
}}
|
||||||
|
summary={() => (
|
||||||
|
<Table.Summary.Row>
|
||||||
|
<Table.Summary.Cell>
|
||||||
|
<Typography.Title level={4}>
|
||||||
|
{t("general.labels.totals")}
|
||||||
|
</Typography.Title>
|
||||||
|
</Table.Summary.Cell>
|
||||||
|
<Table.Summary.Cell></Table.Summary.Cell>
|
||||||
|
<Table.Summary.Cell>
|
||||||
|
{summary.hrs_total.toFixed(5)}
|
||||||
|
</Table.Summary.Cell>
|
||||||
|
<Table.Summary.Cell>
|
||||||
|
{summary.hrs_claimed.toFixed(5)}
|
||||||
|
</Table.Summary.Cell>
|
||||||
|
|
||||||
|
<Table.Summary.Cell>
|
||||||
|
{summary.difference.toFixed(5)}
|
||||||
|
</Table.Summary.Cell>
|
||||||
|
</Table.Summary.Row>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
{convertedLines && convertedLines.length > 0 && (
|
||||||
|
<Col span={24}>
|
||||||
|
<Card title={t("jobs.labels.convertedtolabor")}>
|
||||||
|
<Table
|
||||||
|
columns={convertedTableCols}
|
||||||
|
rowKey="id"
|
||||||
|
pagination={false}
|
||||||
|
dataSource={convertedLines}
|
||||||
|
scroll={{
|
||||||
|
x: true,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default connect(mapStateToProps, null)(PayrollLaborAllocationsTable);
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import { useMutation } from "@apollo/client";
|
||||||
|
import { Button, Card, Col, Row, Table, notification } from "antd";
|
||||||
|
import moment from "moment-business-days";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { UPDATE_PARTS_DISPATCH_LINE } from "../../graphql/parts-dispatch.queries";
|
||||||
|
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
|
|
||||||
|
export default function PartsDispatchExpander({ dispatch, job }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [updateDispatchLine] = useMutation(UPDATE_PARTS_DISPATCH_LINE);
|
||||||
|
|
||||||
|
const handleAccept = async ({ partsDispatchLineId }) => {
|
||||||
|
const accepted_at = moment();
|
||||||
|
const result = await updateDispatchLine({
|
||||||
|
variables: { id: partsDispatchLineId, line: { accepted_at } },
|
||||||
|
optimisticResponse: {
|
||||||
|
update_parts_dispatch_lines_by_pk: {
|
||||||
|
accepted_at,
|
||||||
|
id: partsDispatchLineId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (result.errors) {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("parts_dispatch.errors.accepting", {
|
||||||
|
error: JSON.stringify(result.errors),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.part_qty"),
|
||||||
|
dataIndex: "quantity",
|
||||||
|
key: "quantity",
|
||||||
|
width: "10%",
|
||||||
|
//sorter: (a, b) => alphaSort(a.number, b.number),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.line_desc"),
|
||||||
|
dataIndex: "joblineid",
|
||||||
|
key: "joblineid",
|
||||||
|
//sorter: (a, b) => alphaSort(a.number, b.number),
|
||||||
|
render: (text, record) => record.jobline.line_desc,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("parts_dispatch_lines.fields.accepted_at"),
|
||||||
|
dataIndex: "accepted_at",
|
||||||
|
key: "accepted_at",
|
||||||
|
width: "20%",
|
||||||
|
|
||||||
|
//sorter: (a, b) => alphaSort(a.number, b.number),
|
||||||
|
render: (text, record) =>
|
||||||
|
record.accepted_at ? (
|
||||||
|
<DateTimeFormatter>{record.accepted_at}</DateTimeFormatter>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
onClick={() => handleAccept({ partsDispatchLineId: record.id })}
|
||||||
|
>
|
||||||
|
{t("parts_dispatch.actions.accept")}
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col span={24}>
|
||||||
|
<Table
|
||||||
|
rowKey={"id"}
|
||||||
|
dataSource={dispatch.parts_dispatch_lines}
|
||||||
|
columns={columns}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
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);
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Card, Col, Input, Row, Space, Typography } from "antd";
|
import { Card, Col, Input, Row, Space, Typography } from "antd";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
@@ -23,8 +24,13 @@ export function PrintCenterJobsComponent({ printCenterModal, bodyshop }) {
|
|||||||
const { id: jobId, job } = printCenterModal.context;
|
const { id: jobId, job } = printCenterModal.context;
|
||||||
const tempList = TemplateList("job", {});
|
const tempList = TemplateList("job", {});
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { Enhanced_Payroll } = useTreatments(
|
||||||
|
["Enhanced_Payroll"],
|
||||||
|
{},
|
||||||
|
bodyshop.imexshopid
|
||||||
|
);
|
||||||
|
|
||||||
const JobsReportsList =
|
const Templates =
|
||||||
bodyshop.cdk_dealerid === null && bodyshop.pbs_serialnumber === null
|
bodyshop.cdk_dealerid === null && bodyshop.pbs_serialnumber === null
|
||||||
? Object.keys(tempList)
|
? Object.keys(tempList)
|
||||||
.map((key) => {
|
.map((key) => {
|
||||||
@@ -51,7 +57,26 @@ export function PrintCenterJobsComponent({ printCenterModal, bodyshop }) {
|
|||||||
bodyshop.region_config.includes(Object.keys(temp.regions)) ===
|
bodyshop.region_config.includes(Object.keys(temp.regions)) ===
|
||||||
true)
|
true)
|
||||||
);
|
);
|
||||||
|
const JobsReportsList =
|
||||||
|
Enhanced_Payroll.treatment === "on"
|
||||||
|
? Object.keys(Templates)
|
||||||
|
.map((key) => {
|
||||||
|
return Templates[key];
|
||||||
|
})
|
||||||
|
.filter(
|
||||||
|
(temp) =>
|
||||||
|
temp.enhanced_payroll === undefined ||
|
||||||
|
temp.enhanced_payroll === true
|
||||||
|
)
|
||||||
|
: Object.keys(Templates)
|
||||||
|
.map((key) => {
|
||||||
|
return Templates[key];
|
||||||
|
})
|
||||||
|
.filter(
|
||||||
|
(temp) =>
|
||||||
|
temp.enhanced_payroll === undefined ||
|
||||||
|
temp.enhanced_payroll === false
|
||||||
|
);
|
||||||
const filteredJobsReportsList =
|
const filteredJobsReportsList =
|
||||||
search !== ""
|
search !== ""
|
||||||
? JobsReportsList.filter((r) =>
|
? JobsReportsList.filter((r) =>
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ export default function PrintWrapperComponent({
|
|||||||
children,
|
children,
|
||||||
id,
|
id,
|
||||||
emailOnly = false,
|
emailOnly = false,
|
||||||
|
disabled,
|
||||||
}) {
|
}) {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const handlePrint = async (type) => {
|
const handlePrint = async (type) => {
|
||||||
|
if (disabled) return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
await GenerateDocument(templateObject, messageObject, type, id);
|
await GenerateDocument(templateObject, messageObject, type, id);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@@ -20,8 +22,18 @@ export default function PrintWrapperComponent({
|
|||||||
return (
|
return (
|
||||||
<Space>
|
<Space>
|
||||||
{children || null}
|
{children || null}
|
||||||
{!emailOnly && <PrinterFilled onClick={() => handlePrint("p")} />}
|
{!emailOnly && (
|
||||||
<MailFilled onClick={() => handlePrint("e")} />
|
<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 />}
|
{loading && <Spin />}
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ const sortByParentId = (arr) => {
|
|||||||
//console.log("sortByParentId -> byParentsIdsList", byParentsIdsList);
|
//console.log("sortByParentId -> byParentsIdsList", byParentsIdsList);
|
||||||
|
|
||||||
while (byParentsIdsList[parentId]) {
|
while (byParentsIdsList[parentId]) {
|
||||||
sortedList.push(byParentsIdsList[parentId][0]);
|
sortedList.push(...byParentsIdsList[parentId]); //Spread in the whole list in case several items have the same parents.
|
||||||
parentId = byParentsIdsList[parentId][0].id;
|
parentId = byParentsIdsList[parentId][byParentsIdsList[parentId].length -1].id; //Grab the ID from the last one.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (byParentsIdsList["null"])
|
if (byParentsIdsList["null"])
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import ProductionListColumnNote from "./production-list-columns.productionnote.c
|
|||||||
import ProductionListColumnCategory from "./production-list-columns.status.category";
|
import ProductionListColumnCategory from "./production-list-columns.status.category";
|
||||||
import ProductionListColumnStatus from "./production-list-columns.status.component";
|
import ProductionListColumnStatus from "./production-list-columns.status.component";
|
||||||
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
|
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
|
||||||
|
import { store } from "../../redux/store";
|
||||||
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
|
|
||||||
const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
|
const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
|
||||||
return [
|
return [
|
||||||
@@ -39,6 +41,29 @@ const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
|
|||||||
</Link>
|
</Link>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: i18n.t("timetickets.actions.claimtasks"),
|
||||||
|
dataIndex: "claimtasks",
|
||||||
|
key: "claimtasks",
|
||||||
|
ellipsis: true,
|
||||||
|
render: (text, record) => (
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
store.dispatch(
|
||||||
|
setModalContext({
|
||||||
|
context: {
|
||||||
|
actions: {},
|
||||||
|
context: { jobid: record.id },
|
||||||
|
},
|
||||||
|
modal: "timeTicketTask",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{i18n.t("timetickets.actions.claimtasks")}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: i18n.t("jobs.fields.ro_number"),
|
title: i18n.t("jobs.fields.ro_number"),
|
||||||
dataIndex: "ro_number",
|
dataIndex: "ro_number",
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export function ProductionListDetail({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
placement="right"
|
placement="right"
|
||||||
width={"33%"}
|
width={"50%"}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
visible={selected}
|
visible={selected}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
import {useLazyQuery} from "@apollo/client";
|
import { useLazyQuery } from "@apollo/client";
|
||||||
import {Button, Card, Col, DatePicker, Form, Input, Radio, Row, Typography,} from "antd";
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
import { Button, Card, Col, DatePicker, Form, Input, Radio, Row, Typography, } from "antd";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import React, {useState} from "react";
|
import React, { useState } from "react";
|
||||||
import {useTranslation} from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {connect} from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import {createStructuredSelector} from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import {QUERY_ACTIVE_EMPLOYEES} from "../../graphql/employees.queries";
|
import { QUERY_ACTIVE_EMPLOYEES } from "../../graphql/employees.queries";
|
||||||
import {QUERY_ALL_VENDORS} from "../../graphql/vendors.queries";
|
import { QUERY_ALL_VENDORS } from "../../graphql/vendors.queries";
|
||||||
import {selectReportCenter} from "../../redux/modals/modals.selectors";
|
import { selectReportCenter } from "../../redux/modals/modals.selectors";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import DatePickerRanges from "../../utils/DatePickerRanges";
|
import DatePickerRanges from "../../utils/DatePickerRanges";
|
||||||
import {GenerateDocument} from "../../utils/RenderTemplate";
|
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||||
import {TemplateList} from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component";
|
import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component";
|
||||||
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
||||||
import "./report-center-modal.styles.scss";
|
|
||||||
import ReportCenterModalFiltersSortersComponent from "./report-center-modal-filters-sorters-component";
|
import ReportCenterModalFiltersSortersComponent from "./report-center-modal-filters-sorters-component";
|
||||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
import "./report-center-modal.styles.scss";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
reportCenterModal: selectReportCenter,
|
reportCenterModal: selectReportCenter,
|
||||||
@@ -33,13 +34,35 @@ export default connect(
|
|||||||
export function ReportCenterModalComponent({reportCenterModal, bodyshop}) {
|
export function ReportCenterModalComponent({reportCenterModal, bodyshop}) {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
|
const { Enhanced_Payroll } = useTreatments(
|
||||||
|
["Enhanced_Payroll"],
|
||||||
|
{},
|
||||||
|
bodyshop.imexshopid
|
||||||
|
);
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const {t} = useTranslation();
|
const {t} = useTranslation();
|
||||||
const Templates = TemplateList("report_center");
|
const Templates = TemplateList("report_center");
|
||||||
const ReportsList = Object.keys(Templates).map((key) => {
|
const ReportsList =
|
||||||
return Templates[key];
|
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 {open} = reportCenterModal;
|
||||||
|
|
||||||
const [callVendorQuery, {data: vendorData, called: vendorCalled}] =
|
const [callVendorQuery, {data: vendorData, called: vendorCalled}] =
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
Switch,
|
Switch,
|
||||||
Table,
|
Table,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import { useForm } from "antd/es/form/Form";
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import querystring from "query-string";
|
import querystring from "query-string";
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
@@ -36,6 +36,7 @@ import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
|||||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
import ShopEmployeeAddVacation from "./shop-employees-add-vacation.component";
|
import ShopEmployeeAddVacation from "./shop-employees-add-vacation.component";
|
||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -46,7 +47,7 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
|
|
||||||
export function ShopEmployeesFormComponent({ bodyshop }) {
|
export function ShopEmployeesFormComponent({ bodyshop }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [form] = useForm();
|
const [form] = Form.useForm();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const search = querystring.parse(useLocation().search);
|
const search = querystring.parse(useLocation().search);
|
||||||
const [deleteVacation] = useMutation(DELETE_VACATION);
|
const [deleteVacation] = useMutation(DELETE_VACATION);
|
||||||
@@ -56,7 +57,11 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
|
|||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only",
|
nextFetchPolicy: "network-only",
|
||||||
});
|
});
|
||||||
|
const { Enhanced_Payroll } = useTreatments(
|
||||||
|
["Enhanced_Payroll"],
|
||||||
|
{},
|
||||||
|
bodyshop.imexshopid
|
||||||
|
);
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data && data.employees_by_pk) form.setFieldsValue(data.employees_by_pk);
|
if (data && data.employees_by_pk) form.setFieldsValue(data.employees_by_pk);
|
||||||
@@ -362,7 +367,7 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
|
|||||||
{t("timetickets.labels.shift")}
|
{t("timetickets.labels.shift")}
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
|
|
||||||
{bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
|
{bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || Enhanced_Payroll.treatment === "on"
|
||||||
? CiecaSelect(false, true)
|
? CiecaSelect(false, true)
|
||||||
: bodyshop.md_responsibility_centers.costs.map(
|
: bodyshop.md_responsibility_centers.costs.map(
|
||||||
(c) => (
|
(c) => (
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import ShopInfoResponsibilityCenterComponent from "./shop-info.responsibilitycen
|
|||||||
import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
|
import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
|
||||||
import ShopInfoSchedulingComponent from "./shop-info.scheduling.component";
|
import ShopInfoSchedulingComponent from "./shop-info.scheduling.component";
|
||||||
import ShopInfoSpeedPrint from "./shop-info.speedprint.component";
|
import ShopInfoSpeedPrint from "./shop-info.speedprint.component";
|
||||||
|
import ShopInfoTaskPresets from "./shop-info.task-presets.component";
|
||||||
import { useHistory, useLocation } from "react-router-dom";
|
import { useHistory, useLocation } from "react-router-dom";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
|
|
||||||
@@ -96,6 +97,12 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
|||||||
<ShopInfoPartsScan form={form} />
|
<ShopInfoPartsScan form={form} />
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
)}
|
)}
|
||||||
|
<Tabs.TabPane
|
||||||
|
key="task-presets"
|
||||||
|
tab={t("bodyshop.labels.task-presets")}
|
||||||
|
>
|
||||||
|
<ShopInfoTaskPresets form={form} />
|
||||||
|
</Tabs.TabPane>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -189,20 +189,22 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
<Switch />
|
<Switch />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item shouldUpdate noStyle>
|
{
|
||||||
{() => (
|
// <Form.Item shouldUpdate noStyle>
|
||||||
<Form.Item
|
// {() => (
|
||||||
label={t("bodyshop.labels.qbo_usa")}
|
// <Form.Item
|
||||||
shouldUpdate
|
// label={t("bodyshop.labels.qbo_usa")}
|
||||||
valuePropName="checked"
|
// shouldUpdate
|
||||||
name={["accountingconfig", "qbo_usa"]}
|
// valuePropName="checked"
|
||||||
>
|
// name={["accountingconfig", "qbo_usa"]}
|
||||||
<Switch
|
// >
|
||||||
disabled={!form.getFieldValue(["accountingconfig", "qbo"])}
|
// <Switch
|
||||||
/>
|
// disabled={!form.getFieldValue(["accountingconfig", "qbo"])}
|
||||||
</Form.Item>
|
// />
|
||||||
)}
|
// </Form.Item>
|
||||||
</Form.Item>
|
// )}
|
||||||
|
// </Form.Item>
|
||||||
|
}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.labels.qbo_departmentid")}
|
label={t("bodyshop.labels.qbo_departmentid")}
|
||||||
name={["accountingconfig", "qbo_departmentid"]}
|
name={["accountingconfig", "qbo_departmentid"]}
|
||||||
@@ -291,36 +293,34 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber min={0} precision={2} />
|
<InputNumber min={0} precision={2} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
{
|
||||||
label={t("bodyshop.fields.federal_tax_id")}
|
// <Form.Item
|
||||||
name="federal_tax_id"
|
// label={t("bodyshop.fields.federal_tax_id")}
|
||||||
rules={[
|
// name="federal_tax_id"
|
||||||
{
|
// >
|
||||||
required: true,
|
// <Input />
|
||||||
//message: t("general.validation.required"),
|
// </Form.Item>
|
||||||
},
|
}
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.state_tax_id")}
|
label={t("bodyshop.fields.state_tax_id")}
|
||||||
name="state_tax_id"
|
name="state_tax_id"
|
||||||
>
|
>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
{
|
||||||
label={t("bodyshop.fields.invoice_federal_tax_rate")}
|
// <Form.Item
|
||||||
name={["bill_tax_rates", "federal_tax_rate"]}
|
// label={t("bodyshop.fields.invoice_federal_tax_rate")}
|
||||||
rules={[
|
// name={["bill_tax_rates", "federal_tax_rate"]}
|
||||||
{
|
// rules={[
|
||||||
required: true,
|
// {
|
||||||
//message: t("general.validation.required"),
|
// required: true,
|
||||||
},
|
// //message: t("general.validation.required"),
|
||||||
]}
|
// },
|
||||||
>
|
// ]}
|
||||||
<InputNumber />
|
// >
|
||||||
</Form.Item>
|
// <InputNumber />
|
||||||
|
// </Form.Item>
|
||||||
|
}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.invoice_state_tax_rate")}
|
label={t("bodyshop.fields.invoice_state_tax_rate")}
|
||||||
name={["bill_tax_rates", "state_tax_rate"]}
|
name={["bill_tax_rates", "state_tax_rate"]}
|
||||||
|
|||||||
@@ -408,7 +408,31 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
name={["md_rbac", "owners:detail"]}
|
name={["md_rbac", "bills:list"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.employees.page")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "employees:page"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.employee_teams.page")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "employee_teams:page"]}
|
||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -540,7 +564,67 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
name={["md_rbac", "shop:config"]}
|
name={["md_rbac", "timetickets:edit"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "timetickets:shiftedit"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.timetickets.editcommitted")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "timetickets:editcommitted"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.ttapprovals.view")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "ttapprovals:view"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.ttapprovals.approve")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "ttapprovals:approve"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.shop.vendors")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "shop:vendors"]}
|
||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
import ShopInfoResponsibilitycentersTaxesComponent from "./shop-info.responsibilitycenters.taxes.component";
|
||||||
|
|
||||||
const SelectorDiv = styled.div`
|
const SelectorDiv = styled.div`
|
||||||
.ant-form-item .ant-select {
|
.ant-form-item .ant-select {
|
||||||
@@ -4116,122 +4117,124 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
|||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
</SelectorDiv>
|
</SelectorDiv>
|
||||||
|
|
||||||
<LayoutFormRow
|
{
|
||||||
header={t("bodyshop.labels.responsibilitycenters.tax_accounts")}
|
// <LayoutFormRow
|
||||||
id="tax_accounts"
|
// header={t("bodyshop.labels.responsibilitycenters.tax_accounts")}
|
||||||
>
|
// id="tax_accounts"
|
||||||
<Form.Item
|
// >
|
||||||
label={t("bodyshop.fields.responsibilitycenters.federal_tax")}
|
// <Form.Item
|
||||||
rules={[
|
// label={t("bodyshop.fields.responsibilitycenters.federal_tax")}
|
||||||
{
|
// rules={[
|
||||||
required: true,
|
// {
|
||||||
//message: t("general.validation.required"),
|
// required: true,
|
||||||
},
|
// //message: t("general.validation.required"),
|
||||||
]}
|
// },
|
||||||
name={["md_responsibility_centers", "taxes", "federal", "name"]}
|
// ]}
|
||||||
>
|
// name={["md_responsibility_centers", "taxes", "federal", "name"]}
|
||||||
<Input />
|
// >
|
||||||
</Form.Item>
|
// <Input />
|
||||||
{/* <Form.Item
|
// </Form.Item>
|
||||||
label={t("bodyshop.fields.responsibilitycenter_accountnumber")}
|
// {/* <Form.Item
|
||||||
rules={[
|
// label={t("bodyshop.fields.responsibilitycenter_accountnumber")}
|
||||||
{
|
// rules={[
|
||||||
required: true,
|
// {
|
||||||
//message: t("general.validation.required"),
|
// required: true,
|
||||||
},
|
// //message: t("general.validation.required"),
|
||||||
]}
|
// },
|
||||||
name={[
|
// ]}
|
||||||
"md_responsibility_centers",
|
// name={[
|
||||||
"taxes",
|
// "md_responsibility_centers",
|
||||||
"federal",
|
// "taxes",
|
||||||
"accountnumber",
|
// "federal",
|
||||||
]}
|
// "accountnumber",
|
||||||
>
|
// ]}
|
||||||
<Input />
|
// >
|
||||||
</Form.Item> */}
|
// <Input />
|
||||||
{/* <Form.Item
|
// </Form.Item> */}
|
||||||
label={t("bodyshop.fields.responsibilitycenter_accountname")}
|
// {/* <Form.Item
|
||||||
rules={[
|
// label={t("bodyshop.fields.responsibilitycenter_accountname")}
|
||||||
{
|
// rules={[
|
||||||
required: true,
|
// {
|
||||||
//message: t("general.validation.required"),
|
// required: true,
|
||||||
},
|
// //message: t("general.validation.required"),
|
||||||
]}
|
// },
|
||||||
name={[
|
// ]}
|
||||||
"md_responsibility_centers",
|
// name={[
|
||||||
"taxes",
|
// "md_responsibility_centers",
|
||||||
"federal",
|
// "taxes",
|
||||||
"accountname",
|
// "federal",
|
||||||
]}
|
// "accountname",
|
||||||
>
|
// ]}
|
||||||
<Input />
|
// >
|
||||||
</Form.Item> */}
|
// <Input />
|
||||||
<Form.Item
|
// </Form.Item> */}
|
||||||
label={t("bodyshop.fields.responsibilitycenter_accountdesc")}
|
// <Form.Item
|
||||||
rules={[
|
// label={t("bodyshop.fields.responsibilitycenter_accountdesc")}
|
||||||
{
|
// rules={[
|
||||||
required: true,
|
// {
|
||||||
//message: t("general.validation.required"),
|
// required: true,
|
||||||
},
|
// //message: t("general.validation.required"),
|
||||||
]}
|
// },
|
||||||
name={[
|
// ]}
|
||||||
"md_responsibility_centers",
|
// name={[
|
||||||
"taxes",
|
// "md_responsibility_centers",
|
||||||
"federal",
|
// "taxes",
|
||||||
"accountdesc",
|
// "federal",
|
||||||
]}
|
// "accountdesc",
|
||||||
>
|
// ]}
|
||||||
<Input />
|
// >
|
||||||
</Form.Item>
|
// <Input />
|
||||||
<Form.Item
|
// </Form.Item>
|
||||||
label={t("bodyshop.fields.responsibilitycenter_accountitem")}
|
// <Form.Item
|
||||||
rules={[
|
// label={t("bodyshop.fields.responsibilitycenter_accountitem")}
|
||||||
{
|
// rules={[
|
||||||
required: true,
|
// {
|
||||||
//message: t("general.validation.required"),
|
// required: true,
|
||||||
},
|
// //message: t("general.validation.required"),
|
||||||
]}
|
// },
|
||||||
name={[
|
// ]}
|
||||||
"md_responsibility_centers",
|
// name={[
|
||||||
"taxes",
|
// "md_responsibility_centers",
|
||||||
"federal",
|
// "taxes",
|
||||||
"accountitem",
|
// "federal",
|
||||||
]}
|
// "accountitem",
|
||||||
>
|
// ]}
|
||||||
<Input />
|
// >
|
||||||
</Form.Item>
|
// <Input />
|
||||||
{(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
|
// </Form.Item>
|
||||||
<Form.Item
|
// {(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
|
||||||
label={t("bodyshop.fields.dms.dms_acctnumber")}
|
// <Form.Item
|
||||||
rules={[
|
// label={t("bodyshop.fields.dms.dms_acctnumber")}
|
||||||
{
|
// rules={[
|
||||||
required: true,
|
// {
|
||||||
//message: t("general.validation.required"),
|
// required: true,
|
||||||
},
|
// //message: t("general.validation.required"),
|
||||||
]}
|
// },
|
||||||
name={[
|
// ]}
|
||||||
"md_responsibility_centers",
|
// name={[
|
||||||
"taxes",
|
// "md_responsibility_centers",
|
||||||
"federal",
|
// "taxes",
|
||||||
"dms_acctnumber",
|
// "federal",
|
||||||
]}
|
// "dms_acctnumber",
|
||||||
>
|
// ]}
|
||||||
<Input />
|
// >
|
||||||
</Form.Item>
|
// <Input />
|
||||||
)}
|
// </Form.Item>
|
||||||
<Form.Item
|
// )}
|
||||||
label={t("bodyshop.fields.responsibilitycenter_rate")}
|
// <Form.Item
|
||||||
rules={[
|
// label={t("bodyshop.fields.responsibilitycenter_rate")}
|
||||||
{
|
// rules={[
|
||||||
required: true,
|
// {
|
||||||
//message: t("general.validation.required"),
|
// required: true,
|
||||||
},
|
// //message: t("general.validation.required"),
|
||||||
]}
|
// },
|
||||||
name={["md_responsibility_centers", "taxes", "federal", "rate"]}
|
// ]}
|
||||||
>
|
// name={["md_responsibility_centers", "taxes", "federal", "rate"]}
|
||||||
<InputNumber precision={2} />
|
// >
|
||||||
</Form.Item>
|
// <InputNumber precision={2} />
|
||||||
</LayoutFormRow>
|
// </Form.Item>
|
||||||
|
// </LayoutFormRow>
|
||||||
|
}
|
||||||
{DmsAp.treatment === "on" && (
|
{DmsAp.treatment === "on" && (
|
||||||
<LayoutFormRow id="federal_tax_itc">
|
<LayoutFormRow id="federal_tax_itc">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@@ -4347,202 +4350,242 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
)}
|
)}
|
||||||
<LayoutFormRow id="state_tax">
|
{
|
||||||
<Form.Item
|
// <LayoutFormRow id="state_tax">
|
||||||
label={t("bodyshop.fields.responsibilitycenters.state_tax")}
|
// <Form.Item
|
||||||
rules={[
|
// label={t("bodyshop.fields.responsibilitycenters.state_tax")}
|
||||||
{
|
// rules={[
|
||||||
required: true,
|
// {
|
||||||
//message: t("general.validation.required"),
|
// required: true,
|
||||||
},
|
// //message: t("general.validation.required"),
|
||||||
]}
|
// },
|
||||||
name={["md_responsibility_centers", "taxes", "state", "name"]}
|
// ]}
|
||||||
>
|
// name={["md_responsibility_centers", "taxes", "state", "name"]}
|
||||||
<Input />
|
// >
|
||||||
</Form.Item>
|
// <Input />
|
||||||
{/* <Form.Item
|
// </Form.Item>
|
||||||
label={t("bodyshop.fields.responsibilitycenter_accountnumber")}
|
// {/* <Form.Item
|
||||||
rules={[
|
// label={t("bodyshop.fields.responsibilitycenter_accountnumber")}
|
||||||
{
|
// rules={[
|
||||||
required: true,
|
// {
|
||||||
//message: t("general.validation.required"),
|
// required: true,
|
||||||
},
|
// //message: t("general.validation.required"),
|
||||||
]}
|
// },
|
||||||
name={[
|
// ]}
|
||||||
"md_responsibility_centers",
|
// name={[
|
||||||
"taxes",
|
// "md_responsibility_centers",
|
||||||
"state",
|
// "taxes",
|
||||||
"accountnumber",
|
// "state",
|
||||||
]}
|
// "accountnumber",
|
||||||
>
|
// ]}
|
||||||
<Input />
|
// >
|
||||||
</Form.Item>
|
// <Input />
|
||||||
<Form.Item
|
// </Form.Item>
|
||||||
label={t("bodyshop.fields.responsibilitycenter_accountname")}
|
// <Form.Item
|
||||||
rules={[
|
// label={t("bodyshop.fields.responsibilitycenter_accountname")}
|
||||||
{
|
// rules={[
|
||||||
required: true,
|
// {
|
||||||
//message: t("general.validation.required"),
|
// required: true,
|
||||||
},
|
// //message: t("general.validation.required"),
|
||||||
]}
|
// },
|
||||||
name={["md_responsibility_centers", "taxes", "state", "accountname"]}
|
// ]}
|
||||||
>
|
// name={["md_responsibility_centers", "taxes", "state", "accountname"]}
|
||||||
<Input />
|
// >
|
||||||
</Form.Item> */}
|
// <Input />
|
||||||
<Form.Item
|
// </Form.Item> */}
|
||||||
label={t("bodyshop.fields.responsibilitycenter_accountdesc")}
|
// <Form.Item
|
||||||
rules={[
|
// label={t("bodyshop.fields.responsibilitycenter_accountdesc")}
|
||||||
{
|
// rules={[
|
||||||
required: true,
|
// {
|
||||||
//message: t("general.validation.required"),
|
// required: true,
|
||||||
},
|
// //message: t("general.validation.required"),
|
||||||
]}
|
// },
|
||||||
name={["md_responsibility_centers", "taxes", "state", "accountdesc"]}
|
// ]}
|
||||||
>
|
// name={["md_responsibility_centers", "taxes", "state", "accountdesc"]}
|
||||||
<Input />
|
// >
|
||||||
</Form.Item>
|
// <Input />
|
||||||
<Form.Item
|
// </Form.Item>
|
||||||
label={t("bodyshop.fields.responsibilitycenter_accountitem")}
|
// <Form.Item
|
||||||
rules={[
|
// label={t("bodyshop.fields.responsibilitycenter_accountitem")}
|
||||||
{
|
// rules={[
|
||||||
required: true,
|
// {
|
||||||
//message: t("general.validation.required"),
|
// required: true,
|
||||||
},
|
// //message: t("general.validation.required"),
|
||||||
]}
|
// },
|
||||||
name={["md_responsibility_centers", "taxes", "state", "accountitem"]}
|
// ]}
|
||||||
>
|
// name={["md_responsibility_centers", "taxes", "state", "accountitem"]}
|
||||||
<Input />
|
// >
|
||||||
</Form.Item>
|
// <Input />
|
||||||
{(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
|
// </Form.Item>
|
||||||
<Form.Item
|
// {(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
|
||||||
label={t("bodyshop.fields.dms.dms_acctnumber")}
|
// <Form.Item
|
||||||
rules={[
|
// label={t("bodyshop.fields.dms.dms_acctnumber")}
|
||||||
{
|
// rules={[
|
||||||
required: true,
|
// {
|
||||||
//message: t("general.validation.required"),
|
// required: true,
|
||||||
},
|
// //message: t("general.validation.required"),
|
||||||
]}
|
// },
|
||||||
name={[
|
// ]}
|
||||||
"md_responsibility_centers",
|
// name={[
|
||||||
"taxes",
|
// "md_responsibility_centers",
|
||||||
"state",
|
// "taxes",
|
||||||
"dms_acctnumber",
|
// "state",
|
||||||
]}
|
// "dms_acctnumber",
|
||||||
>
|
// ]}
|
||||||
<Input />
|
// >
|
||||||
</Form.Item>
|
// <Input />
|
||||||
)}
|
// </Form.Item>
|
||||||
<Form.Item
|
// )}
|
||||||
label={t("bodyshop.fields.responsibilitycenter_rate")}
|
// <Form.Item
|
||||||
rules={[
|
// label={t("bodyshop.fields.responsibilitycenter_rate")}
|
||||||
{
|
// rules={[
|
||||||
required: true,
|
// {
|
||||||
//message: t("general.validation.required"),
|
// required: true,
|
||||||
},
|
// //message: t("general.validation.required"),
|
||||||
]}
|
// },
|
||||||
name={["md_responsibility_centers", "taxes", "state", "rate"]}
|
// ]}
|
||||||
>
|
// name={["md_responsibility_centers", "taxes", "state", "rate"]}
|
||||||
<InputNumber precision={2} />
|
// >
|
||||||
</Form.Item>
|
// <InputNumber precision={2} />
|
||||||
</LayoutFormRow>
|
// </Form.Item>
|
||||||
<LayoutFormRow id="local_tax">
|
// </LayoutFormRow>
|
||||||
<Form.Item
|
}
|
||||||
label={t("bodyshop.fields.responsibilitycenters.local_tax")}
|
<ShopInfoResponsibilitycentersTaxesComponent form={form} />
|
||||||
rules={[
|
<Form.Item
|
||||||
{
|
label={t("bodyshop.fields.responsibilitycenters.itemexemptcode")}
|
||||||
required: true,
|
rules={[
|
||||||
//message: t("general.validation.required"),
|
{
|
||||||
},
|
required: true,
|
||||||
]}
|
//message: t("general.validation.required"),
|
||||||
name={["md_responsibility_centers", "taxes", "local", "name"]}
|
},
|
||||||
>
|
]}
|
||||||
<Input />
|
name={["md_responsibility_centers", "taxes", "itemexemptcode"]}
|
||||||
</Form.Item>
|
>
|
||||||
{/* <Form.Item
|
<Input />
|
||||||
label={t("bodyshop.fields.responsibilitycenter_accountnumber")}
|
</Form.Item>
|
||||||
rules={[
|
<Form.Item
|
||||||
{
|
label={t("bodyshop.fields.responsibilitycenters.invoiceexemptcode")}
|
||||||
required: true,
|
rules={[
|
||||||
//message: t("general.validation.required"),
|
{
|
||||||
},
|
required: true,
|
||||||
]}
|
//message: t("general.validation.required"),
|
||||||
name={[
|
},
|
||||||
"md_responsibility_centers",
|
]}
|
||||||
"taxes",
|
name={["md_responsibility_centers", "taxes", "invoiceexemptcode"]}
|
||||||
"local",
|
>
|
||||||
"accountnumber",
|
<Input />
|
||||||
]}
|
</Form.Item>
|
||||||
>
|
|
||||||
<Input />
|
{
|
||||||
</Form.Item>
|
// <LayoutFormRow id="local_tax">
|
||||||
<Form.Item
|
// <Form.Item
|
||||||
label={t("bodyshop.fields.responsibilitycenter_accountname")}
|
// label={t("bodyshop.fields.responsibilitycenters.local_tax")}
|
||||||
rules={[
|
// rules={[
|
||||||
{
|
// {
|
||||||
required: true,
|
// required: true,
|
||||||
//message: t("general.validation.required"),
|
// //message: t("general.validation.required"),
|
||||||
},
|
// },
|
||||||
]}
|
// ]}
|
||||||
name={["md_responsibility_centers", "taxes", "local", "accountname"]}
|
// name={["md_responsibility_centers", "taxes", "local", "name"]}
|
||||||
>
|
// >
|
||||||
<Input />
|
// <Input />
|
||||||
</Form.Item> */}
|
// </Form.Item>
|
||||||
<Form.Item
|
// {/* <Form.Item
|
||||||
label={t("bodyshop.fields.responsibilitycenter_accountdesc")}
|
// label={t("bodyshop.fields.responsibilitycenter_accountnumber")}
|
||||||
rules={[
|
// rules={[
|
||||||
{
|
// {
|
||||||
required: true,
|
// required: true,
|
||||||
//message: t("general.validation.required"),
|
// //message: t("general.validation.required"),
|
||||||
},
|
// },
|
||||||
]}
|
// ]}
|
||||||
name={["md_responsibility_centers", "taxes", "local", "accountdesc"]}
|
// name={[
|
||||||
>
|
// "md_responsibility_centers",
|
||||||
<Input />
|
// "taxes",
|
||||||
</Form.Item>
|
// "local",
|
||||||
<Form.Item
|
// "accountnumber",
|
||||||
label={t("bodyshop.fields.responsibilitycenter_accountitem")}
|
// ]}
|
||||||
rules={[
|
// >
|
||||||
{
|
// <Input />
|
||||||
required: true,
|
// </Form.Item>
|
||||||
//message: t("general.validation.required"),
|
// <Form.Item
|
||||||
},
|
// label={t("bodyshop.fields.responsibilitycenter_accountname")}
|
||||||
]}
|
// rules={[
|
||||||
name={["md_responsibility_centers", "taxes", "local", "accountitem"]}
|
// {
|
||||||
>
|
// required: true,
|
||||||
<Input />
|
// //message: t("general.validation.required"),
|
||||||
</Form.Item>
|
// },
|
||||||
{(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
|
// ]}
|
||||||
<Form.Item
|
// name={["md_responsibility_centers", "taxes", "local", "accountname"]}
|
||||||
label={t("bodyshop.fields.dms.dms_acctnumber")}
|
// >
|
||||||
rules={[
|
// <Input />
|
||||||
{
|
// </Form.Item> */}
|
||||||
required: true,
|
// <Form.Item
|
||||||
//message: t("general.validation.required"),
|
// label={t("bodyshop.fields.responsibilitycenter_accountdesc")}
|
||||||
},
|
// rules={[
|
||||||
]}
|
// {
|
||||||
name={[
|
// required: true,
|
||||||
"md_responsibility_centers",
|
// //message: t("general.validation.required"),
|
||||||
"taxes",
|
// },
|
||||||
"local",
|
// ]}
|
||||||
"dms_acctnumber",
|
// name={[
|
||||||
]}
|
// "md_responsibility_centers",
|
||||||
>
|
// "taxes",
|
||||||
<Input />
|
// "local",
|
||||||
</Form.Item>
|
// "accountdesc",
|
||||||
)}
|
// ]}
|
||||||
<Form.Item
|
// >
|
||||||
label={t("bodyshop.fields.responsibilitycenter_rate")}
|
// <Input />
|
||||||
rules={[
|
// </Form.Item>
|
||||||
{
|
// <Form.Item
|
||||||
required: true,
|
// label={t("bodyshop.fields.responsibilitycenter_accountitem")}
|
||||||
//message: t("general.validation.required"),
|
// rules={[
|
||||||
},
|
// {
|
||||||
]}
|
// required: true,
|
||||||
name={["md_responsibility_centers", "taxes", "local", "rate"]}
|
// //message: t("general.validation.required"),
|
||||||
>
|
// },
|
||||||
<InputNumber precision={2} />
|
// ]}
|
||||||
</Form.Item>
|
// name={[
|
||||||
</LayoutFormRow>
|
// "md_responsibility_centers",
|
||||||
|
// "taxes",
|
||||||
|
// "local",
|
||||||
|
// "accountitem",
|
||||||
|
// ]}
|
||||||
|
// >
|
||||||
|
// <Input />
|
||||||
|
// </Form.Item>
|
||||||
|
// {(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
|
||||||
|
// <Form.Item
|
||||||
|
// label={t("bodyshop.fields.dms.dms_acctnumber")}
|
||||||
|
// rules={[
|
||||||
|
// {
|
||||||
|
// required: true,
|
||||||
|
// //message: t("general.validation.required"),
|
||||||
|
// },
|
||||||
|
// ]}
|
||||||
|
// name={[
|
||||||
|
// "md_responsibility_centers",
|
||||||
|
// "taxes",
|
||||||
|
// "local",
|
||||||
|
// "dms_acctnumber",
|
||||||
|
// ]}
|
||||||
|
// >
|
||||||
|
// <Input />
|
||||||
|
// </Form.Item>
|
||||||
|
// )}
|
||||||
|
// <Form.Item
|
||||||
|
// label={t("bodyshop.fields.responsibilitycenter_rate")}
|
||||||
|
// rules={[
|
||||||
|
// {
|
||||||
|
// required: true,
|
||||||
|
// //message: t("general.validation.required"),
|
||||||
|
// },
|
||||||
|
// ]}
|
||||||
|
// name={["md_responsibility_centers", "taxes", "local", "rate"]}
|
||||||
|
// >
|
||||||
|
// <InputNumber precision={2} />
|
||||||
|
// </Form.Item>
|
||||||
|
// </LayoutFormRow>
|
||||||
|
}
|
||||||
<LayoutFormRow header={<div>AR</div>} id="AR">
|
<LayoutFormRow header={<div>AR</div>} id="AR">
|
||||||
{/* <Form.Item
|
{/* <Form.Item
|
||||||
label={t("bodyshop.fields.responsibilitycenters.ar")}
|
label={t("bodyshop.fields.responsibilitycenters.ar")}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,269 @@
|
|||||||
|
import { DeleteFilled } from "@ant-design/icons";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Checkbox,
|
||||||
|
Col,
|
||||||
|
Form,
|
||||||
|
Input,
|
||||||
|
InputNumber,
|
||||||
|
Row,
|
||||||
|
Select,
|
||||||
|
Space,
|
||||||
|
Switch,
|
||||||
|
} from "antd";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||||
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
|
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(ShopInfoTaskPresets);
|
||||||
|
|
||||||
|
export function ShopInfoTaskPresets({ bodyshop, form }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<LayoutFormRow noDivider>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.md_tasks_presets.enable_tasks")}
|
||||||
|
valuePropName="checked"
|
||||||
|
name={["md_tasks_presets", "enable_tasks"]}
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.md_tasks_presets.use_approvals")}
|
||||||
|
valuePropName="checked"
|
||||||
|
name={["md_tasks_presets", "use_approvals"]}
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
</LayoutFormRow>
|
||||||
|
|
||||||
|
<LayoutFormRow header={t("bodyshop.labels.md_tasks_presets")}>
|
||||||
|
<Form.List name={["md_tasks_presets", "presets"]}>
|
||||||
|
{(fields, { add, remove, move }) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<Form.Item key={field.key}>
|
||||||
|
<LayoutFormRow noDivider>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.md_tasks_presets.name")}
|
||||||
|
key={`${index}name`}
|
||||||
|
name={[field.name, "name"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
span={12}
|
||||||
|
label={t("bodyshop.fields.md_tasks_presets.hourstype")}
|
||||||
|
key={`${index}hourstype`}
|
||||||
|
name={[field.name, "hourstype"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Checkbox.Group>
|
||||||
|
<Row>
|
||||||
|
<Col span={4}>
|
||||||
|
<Checkbox
|
||||||
|
value="LAA"
|
||||||
|
style={{ lineHeight: "32px" }}
|
||||||
|
>
|
||||||
|
{t("joblines.fields.lbr_types.LAA")}
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<Checkbox
|
||||||
|
value="LAB"
|
||||||
|
style={{ lineHeight: "32px" }}
|
||||||
|
>
|
||||||
|
{t("joblines.fields.lbr_types.LAB")}
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<Checkbox
|
||||||
|
value="LAD"
|
||||||
|
style={{ lineHeight: "32px" }}
|
||||||
|
>
|
||||||
|
{t("joblines.fields.lbr_types.LAD")}
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<Checkbox
|
||||||
|
value="LAE"
|
||||||
|
style={{ lineHeight: "32px" }}
|
||||||
|
>
|
||||||
|
{t("joblines.fields.lbr_types.LAE")}
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<Checkbox
|
||||||
|
value="LAF"
|
||||||
|
style={{ lineHeight: "32px" }}
|
||||||
|
>
|
||||||
|
{t("joblines.fields.lbr_types.LAF")}
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<Checkbox
|
||||||
|
value="LAG"
|
||||||
|
style={{ lineHeight: "32px" }}
|
||||||
|
>
|
||||||
|
{t("joblines.fields.lbr_types.LAG")}
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<Checkbox
|
||||||
|
value="LAM"
|
||||||
|
style={{ lineHeight: "32px" }}
|
||||||
|
>
|
||||||
|
{t("joblines.fields.lbr_types.LAM")}
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<Checkbox
|
||||||
|
value="LAR"
|
||||||
|
style={{ lineHeight: "32px" }}
|
||||||
|
>
|
||||||
|
{t("joblines.fields.lbr_types.LAR")}
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<Checkbox
|
||||||
|
value="LAS"
|
||||||
|
style={{ lineHeight: "32px" }}
|
||||||
|
>
|
||||||
|
{t("joblines.fields.lbr_types.LAS")}
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<Checkbox
|
||||||
|
value="LAU"
|
||||||
|
style={{ lineHeight: "32px" }}
|
||||||
|
>
|
||||||
|
{t("joblines.fields.lbr_types.LAU")}
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<Checkbox
|
||||||
|
value="LA1"
|
||||||
|
style={{ lineHeight: "32px" }}
|
||||||
|
>
|
||||||
|
{t("joblines.fields.lbr_types.LA1")}
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<Checkbox
|
||||||
|
value="LA2"
|
||||||
|
style={{ lineHeight: "32px" }}
|
||||||
|
>
|
||||||
|
{t("joblines.fields.lbr_types.LA2")}
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<Checkbox
|
||||||
|
value="LA3"
|
||||||
|
style={{ lineHeight: "32px" }}
|
||||||
|
>
|
||||||
|
{t("joblines.fields.lbr_types.LA3")}
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<Checkbox
|
||||||
|
value="LA4"
|
||||||
|
style={{ lineHeight: "32px" }}
|
||||||
|
>
|
||||||
|
{t("joblines.fields.lbr_types.LA4")}
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Checkbox.Group>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.md_tasks_presets.percent")}
|
||||||
|
key={`${index}percent`}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={[field.name, "percent"]}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} max={100} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.md_tasks_presets.memo")}
|
||||||
|
key={`${index}memo`}
|
||||||
|
name={[field.name, "memo"]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.md_tasks_presets.nextstatus")}
|
||||||
|
key={`${index}nextstatus`}
|
||||||
|
name={[field.name, "nextstatus"]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
options={bodyshop.md_ro_statuses.production_statuses.map(
|
||||||
|
(o) => ({ value: o, label: o })
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Space wrap>
|
||||||
|
<DeleteFilled
|
||||||
|
onClick={() => {
|
||||||
|
remove(field.name);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<FormListMoveArrows
|
||||||
|
move={move}
|
||||||
|
index={index}
|
||||||
|
total={fields.length}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</LayoutFormRow>
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
<Form.Item>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
onClick={() => {
|
||||||
|
add();
|
||||||
|
}}
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
>
|
||||||
|
{t("bodyshop.actions.add_task_preset")}
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
</LayoutFormRow>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default function ShopEmployeeTeamMember({teamMember}) {
|
||||||
|
return (
|
||||||
|
<div>ShopEmployeeTeamMember</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,436 @@
|
|||||||
|
import { DeleteFilled } from "@ant-design/icons";
|
||||||
|
import { useMutation, useQuery } from "@apollo/client";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Form,
|
||||||
|
Input,
|
||||||
|
InputNumber,
|
||||||
|
Space,
|
||||||
|
Switch,
|
||||||
|
notification,
|
||||||
|
} from "antd";
|
||||||
|
|
||||||
|
import querystring from "query-string";
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { useHistory, useLocation } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||||
|
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||||
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
|
|
||||||
|
import {
|
||||||
|
INSERT_EMPLOYEE_TEAM,
|
||||||
|
QUERY_EMPLOYEE_TEAM_BY_ID,
|
||||||
|
UPDATE_EMPLOYEE_TEAM,
|
||||||
|
} from "../../graphql/employee_teams.queries";
|
||||||
|
import EmployeeSearchSelectComponent from "../employee-search-select/employee-search-select.component";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
|
||||||
|
export function ShopEmployeeTeamsFormComponent({ bodyshop }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const history = useHistory();
|
||||||
|
const search = querystring.parse(useLocation().search);
|
||||||
|
|
||||||
|
const { error, data } = useQuery(QUERY_EMPLOYEE_TEAM_BY_ID, {
|
||||||
|
variables: { id: search.employeeTeamId },
|
||||||
|
skip: !search.employeeTeamId || search.employeeTeamId === "new",
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
nextFetchPolicy: "network-only",
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (data && data.employee_teams_by_pk)
|
||||||
|
form.setFieldsValue(data.employee_teams_by_pk);
|
||||||
|
else {
|
||||||
|
form.resetFields();
|
||||||
|
}
|
||||||
|
}, [form, data, search.employeeTeamId]);
|
||||||
|
|
||||||
|
const [updateEmployeeTeam] = useMutation(UPDATE_EMPLOYEE_TEAM);
|
||||||
|
const [insertEmployeeTeam] = useMutation(INSERT_EMPLOYEE_TEAM);
|
||||||
|
|
||||||
|
const handleFinish = async ({ employee_team_members, ...values }) => {
|
||||||
|
if (search.employeeTeamId && search.employeeTeamId !== "new") {
|
||||||
|
//Update a record.
|
||||||
|
logImEXEvent("shop_employee_update");
|
||||||
|
|
||||||
|
const result = await updateEmployeeTeam({
|
||||||
|
variables: {
|
||||||
|
employeeTeamId: search.employeeTeamId,
|
||||||
|
employeeTeam: values,
|
||||||
|
teamMemberUpdates: employee_team_members
|
||||||
|
.filter((e) => e.id)
|
||||||
|
.map((e) => {
|
||||||
|
delete e.__typename;
|
||||||
|
return { where: { id: { _eq: e.id } }, _set: e };
|
||||||
|
}),
|
||||||
|
teamMemberInserts: employee_team_members
|
||||||
|
.filter((e) => e.id === null || e.id === undefined)
|
||||||
|
.map((e) => ({ ...e, teamid: search.employeeTeamId })),
|
||||||
|
teamMemberDeletes:
|
||||||
|
data.employee_teams_by_pk.employee_team_members.filter(
|
||||||
|
(e) => !employee_team_members.find((etm) => etm.id === e.id)
|
||||||
|
),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!result.errors) {
|
||||||
|
notification["success"]({
|
||||||
|
message: t("employees.successes.save"),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
notification["error"]({
|
||||||
|
message: t("employees.errors.save", {
|
||||||
|
message: JSON.stringify(error),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//New record, insert it.
|
||||||
|
logImEXEvent("shop_employee_insert");
|
||||||
|
|
||||||
|
insertEmployeeTeam({
|
||||||
|
variables: {
|
||||||
|
employeeTeam: {
|
||||||
|
...values,
|
||||||
|
employee_team_members: { data: employee_team_members },
|
||||||
|
bodyshopid: bodyshop.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
refetchQueries: ["QUERY_TEAMS"],
|
||||||
|
}).then((r) => {
|
||||||
|
search.employeeTeamId = r.data.insert_employee_teams_one.id;
|
||||||
|
history.push({ search: querystring.stringify(search) });
|
||||||
|
notification["success"]({
|
||||||
|
message: t("employees.successes.save"),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!search.employeeTeamId) return null;
|
||||||
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
extra={
|
||||||
|
<Button type="primary" onClick={() => form.submit()}>
|
||||||
|
{t("general.actions.save")}
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
onFinish={handleFinish}
|
||||||
|
autoComplete={"off"}
|
||||||
|
layout="vertical"
|
||||||
|
form={form}
|
||||||
|
>
|
||||||
|
<LayoutFormRow>
|
||||||
|
<Form.Item
|
||||||
|
name="name"
|
||||||
|
label={t("employee_teams.fields.name")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("employee_teams.fields.active")}
|
||||||
|
name="active"
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("employee_teams.fields.max_load")}
|
||||||
|
name="max_load"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} precision={1} />
|
||||||
|
</Form.Item>
|
||||||
|
</LayoutFormRow>
|
||||||
|
<Form.List name={["employee_team_members"]}>
|
||||||
|
{(fields, { add, remove, move }) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<Form.Item key={field.key} style={{ padding: 0, margin: 2 }}>
|
||||||
|
<Form.Item
|
||||||
|
label={t("employees.fields.id")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "id"]}
|
||||||
|
hidden
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<LayoutFormRow grow>
|
||||||
|
<Form.Item
|
||||||
|
label={t("employee_teams.fields.employeeid")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "employeeid"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<EmployeeSearchSelectComponent
|
||||||
|
options={bodyshop.employees}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("employee_teams.fields.percentage")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "percentage"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} max={100} precision={2} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.lbr_types.LAA")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "labor_rates", "LAA"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<CurrencyInput />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.lbr_types.LAB")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "labor_rates", "LAB"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<CurrencyInput />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.lbr_types.LAD")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "labor_rates", "LAD"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<CurrencyInput />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.lbr_types.LAE")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "labor_rates", "LAE"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<CurrencyInput />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.lbr_types.LAF")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "labor_rates", "LAF"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<CurrencyInput />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.lbr_types.LAG")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "labor_rates", "LAG"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<CurrencyInput />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.lbr_types.LAM")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "labor_rates", "LAM"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<CurrencyInput />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.lbr_types.LAR")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "labor_rates", "LAR"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<CurrencyInput />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.lbr_types.LAS")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "labor_rates", "LAS"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<CurrencyInput />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.lbr_types.LAU")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "labor_rates", "LAU"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<CurrencyInput />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.lbr_types.LA1")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "labor_rates", "LA1"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<CurrencyInput />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.lbr_types.LA2")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "labor_rates", "LA2"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<CurrencyInput />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.lbr_types.LA3")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "labor_rates", "LA3"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<CurrencyInput />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.lbr_types.LA4")}
|
||||||
|
key={`${index}`}
|
||||||
|
name={[field.name, "labor_rates", "LA4"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<CurrencyInput />
|
||||||
|
</Form.Item>
|
||||||
|
<Space align="center">
|
||||||
|
<DeleteFilled
|
||||||
|
onClick={() => {
|
||||||
|
remove(field.name);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<FormListMoveArrows
|
||||||
|
move={move}
|
||||||
|
index={index}
|
||||||
|
total={fields.length}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</LayoutFormRow>
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
<Form.Item>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
onClick={() => {
|
||||||
|
add();
|
||||||
|
}}
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
>
|
||||||
|
{t("employee_teams.actions.newmember")}
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
</Form>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(ShopEmployeeTeamsFormComponent);
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import { Button, Table } from "antd";
|
||||||
|
import queryString from "query-string";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useHistory, useLocation } from "react-router-dom";
|
||||||
|
|
||||||
|
export default function ShopEmployeeTeamsListComponent({
|
||||||
|
loading,
|
||||||
|
employee_teams,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const history = useHistory();
|
||||||
|
const search = queryString.parse(useLocation().search);
|
||||||
|
|
||||||
|
const handleOnRowClick = (record) => {
|
||||||
|
if (record) {
|
||||||
|
search.employeeTeamId = record.id;
|
||||||
|
history.push({ search: queryString.stringify(search) });
|
||||||
|
} else {
|
||||||
|
delete search.employeeTeamId;
|
||||||
|
history.push({ search: queryString.stringify(search) });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t("employee_teams.fields.name"),
|
||||||
|
dataIndex: "name",
|
||||||
|
key: "name",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Table
|
||||||
|
title={() => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
search.employeeTeamId = "new";
|
||||||
|
history.push({ search: queryString.stringify(search) });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("employee_teams.actions.new")}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
loading={loading}
|
||||||
|
pagination={{ position: "top" }}
|
||||||
|
columns={columns}
|
||||||
|
rowKey="id"
|
||||||
|
dataSource={employee_teams}
|
||||||
|
rowSelection={{
|
||||||
|
onSelect: (props) => {
|
||||||
|
search.employeeTeamId = props.id;
|
||||||
|
history.push({ search: queryString.stringify(search) });
|
||||||
|
},
|
||||||
|
type: "radio",
|
||||||
|
selectedRowKeys: [search.employeeTeamId],
|
||||||
|
}}
|
||||||
|
onRow={(record, rowIndex) => {
|
||||||
|
return {
|
||||||
|
onClick: (event) => {
|
||||||
|
handleOnRowClick(record);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
43
client/src/components/shop-teams/shop-teams.container.jsx
Normal file
43
client/src/components/shop-teams/shop-teams.container.jsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { useQuery } from "@apollo/client";
|
||||||
|
import React from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { QUERY_TEAMS } from "../../graphql/employee_teams.queries";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
|
import ShopEmployeeTeamsListComponent from "./shop-employee-teams.list";
|
||||||
|
import ShopEmployeeTeamsFormComponent from "./shop-employee-teams.form.component";
|
||||||
|
import { Col, Row } from "antd";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
|
||||||
|
function ShopTeamsContainer({ bodyshop }) {
|
||||||
|
const { loading, error, data } = useQuery(QUERY_TEAMS, {
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
nextFetchPolicy: "network-only",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<RbacWrapper action="employee_teams:page">
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col span={6}>
|
||||||
|
<ShopEmployeeTeamsListComponent
|
||||||
|
employee_teams={data ? data.employee_teams : []}
|
||||||
|
loading={loading}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={18}>
|
||||||
|
<ShopEmployeeTeamsFormComponent />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</RbacWrapper>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default connect(mapStateToProps, null)(ShopTeamsContainer);
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import { LockOutlined, UserOutlined } from "@ant-design/icons";
|
import { LockOutlined, UserOutlined } from "@ant-design/icons";
|
||||||
import { Button, Form, Input, Typography } from "antd";
|
import { Button, Form, Input } from "antd";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Link, Redirect, useLocation } from "react-router-dom";
|
import { Link, Redirect, useLocation } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import ImEXOnlineLogo from "../../assets/logo192.png";
|
import RomeLogo from "../../assets/RomeOnlineBlue.png";
|
||||||
import {
|
import {
|
||||||
emailSignInStart,
|
emailSignInStart,
|
||||||
sendPasswordReset,
|
sendPasswordReset,
|
||||||
@@ -53,8 +53,7 @@ export function SignInComponent({
|
|||||||
return (
|
return (
|
||||||
<div className="login-container">
|
<div className="login-container">
|
||||||
<div className="login-logo-container">
|
<div className="login-logo-container">
|
||||||
<img src={ImEXOnlineLogo} height="100" width="100" alt="ImEX Online" />
|
<img src={RomeLogo} width={200} alt="Rome Online" />
|
||||||
<Typography.Title>{t("titles.app")}</Typography.Title>
|
|
||||||
</div>
|
</div>
|
||||||
<Form onFinish={handleFinish} form={form} size="large">
|
<Form onFinish={handleFinish} form={form} size="large">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
|||||||
import JobSearchSelect from "../job-search-select/job-search-select.component";
|
import JobSearchSelect from "../job-search-select/job-search-select.component";
|
||||||
import JobsDetailLaborContainer from "../jobs-detail-labor/jobs-detail-labor.container";
|
import JobsDetailLaborContainer from "../jobs-detail-labor/jobs-detail-labor.container";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -16,6 +17,11 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
|
|
||||||
export function TechClockInComponent({ form, bodyshop, technician }) {
|
export function TechClockInComponent({ form, bodyshop, technician }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { Enhanced_Payroll } = useTreatments(
|
||||||
|
["Enhanced_Payroll"],
|
||||||
|
{},
|
||||||
|
bodyshop.imexshopid
|
||||||
|
);
|
||||||
const emps = bodyshop.employees.filter((e) => e.id === technician.id)[0];
|
const emps = bodyshop.employees.filter((e) => e.id === technician.id)[0];
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -53,7 +59,7 @@ export function TechClockInComponent({ form, bodyshop, technician }) {
|
|||||||
<Select.Option key={item.cost_center} value={item.cost_center}>
|
<Select.Option key={item.cost_center} value={item.cost_center}>
|
||||||
{item.cost_center === "timetickets.labels.shift"
|
{item.cost_center === "timetickets.labels.shift"
|
||||||
? t(item.cost_center)
|
? t(item.cost_center)
|
||||||
: bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
|
: bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || Enhanced_Payroll.treatment === "on"
|
||||||
? t(
|
? t(
|
||||||
`joblines.fields.lbr_types.${item.cost_center.toUpperCase()}`
|
`joblines.fields.lbr_types.${item.cost_center.toUpperCase()}`
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
import TechJobPrintTickets from "../tech-job-print-tickets/tech-job-print-tickets.component";
|
import TechJobPrintTickets from "../tech-job-print-tickets/tech-job-print-tickets.component";
|
||||||
import TechClockInComponent from "./tech-job-clock-in-form.component";
|
import TechClockInComponent from "./tech-job-clock-in-form.component";
|
||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
technician: selectTechnician,
|
technician: selectTechnician,
|
||||||
@@ -32,9 +33,10 @@ export function TechClockInContainer({
|
|||||||
bodyshop,
|
bodyshop,
|
||||||
currentUser,
|
currentUser,
|
||||||
}) {
|
}) {
|
||||||
console.log(
|
const { Enhanced_Payroll } = useTreatments(
|
||||||
"🚀 ~ file: tech-job-clock-in-form.container.jsx:30 ~ technician:",
|
["Enhanced_Payroll"],
|
||||||
technician
|
{},
|
||||||
|
bodyshop.imexshopid
|
||||||
);
|
);
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -68,7 +70,7 @@ export function TechClockInContainer({
|
|||||||
jobid: values.jobid,
|
jobid: values.jobid,
|
||||||
cost_center: values.cost_center,
|
cost_center: values.cost_center,
|
||||||
ciecacode:
|
ciecacode:
|
||||||
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
|
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || Enhanced_Payroll.treatment === 'on'
|
||||||
? values.cost_center
|
? values.cost_center
|
||||||
: Object.keys(
|
: Object.keys(
|
||||||
bodyshop.md_responsibility_centers.defaults.costs
|
bodyshop.md_responsibility_centers.defaults.costs
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
|||||||
import { CalculateAllocationsTotals } from "../labor-allocations-table/labor-allocations-table.utility";
|
import { CalculateAllocationsTotals } from "../labor-allocations-table/labor-allocations-table.utility";
|
||||||
import TechJobClockoutDelete from "../tech-job-clock-out-delete/tech-job-clock-out-delete.component";
|
import TechJobClockoutDelete from "../tech-job-clock-out-delete/tech-job-clock-out-delete.component";
|
||||||
import { LaborAllocationContainer } from "../time-ticket-modal/time-ticket-modal.component";
|
import { LaborAllocationContainer } from "../time-ticket-modal/time-ticket-modal.component";
|
||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -43,6 +44,12 @@ export function TechClockOffButton({
|
|||||||
const [updateTimeticket] = useMutation(UPDATE_TIME_TICKET);
|
const [updateTimeticket] = useMutation(UPDATE_TIME_TICKET);
|
||||||
const [updateJobStatus] = useMutation(UPDATE_JOB_STATUS);
|
const [updateJobStatus] = useMutation(UPDATE_JOB_STATUS);
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
const { Enhanced_Payroll } = useTreatments(
|
||||||
|
["Enhanced_Payroll"],
|
||||||
|
{},
|
||||||
|
bodyshop.imexshopid
|
||||||
|
);
|
||||||
|
|
||||||
const { queryLoading, data: lineTicketData } = useQuery(
|
const { queryLoading, data: lineTicketData } = useQuery(
|
||||||
GET_LINE_TICKET_BY_PK,
|
GET_LINE_TICKET_BY_PK,
|
||||||
{
|
{
|
||||||
@@ -76,7 +83,9 @@ export function TechClockOffButton({
|
|||||||
?.rate,
|
?.rate,
|
||||||
flat_rate: emps && emps.flat_rate,
|
flat_rate: emps && emps.flat_rate,
|
||||||
ciecacode:
|
ciecacode:
|
||||||
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
|
bodyshop.cdk_dealerid ||
|
||||||
|
bodyshop.pbs_serialnumber ||
|
||||||
|
Enhanced_Payroll.treatment === "on"
|
||||||
? values.cost_center
|
? values.cost_center
|
||||||
: Object.keys(
|
: Object.keys(
|
||||||
bodyshop.md_responsibility_centers.defaults.costs
|
bodyshop.md_responsibility_centers.defaults.costs
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import Icon, { SearchOutlined, ScheduleOutlined } from "@ant-design/icons";
|
import Icon, {
|
||||||
|
SearchOutlined,
|
||||||
|
ScheduleOutlined,
|
||||||
|
UserAddOutlined,
|
||||||
|
CarOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
import { Layout, Menu } from "antd";
|
import { Layout, Menu } from "antd";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -11,22 +16,38 @@ import { createStructuredSelector } from "reselect";
|
|||||||
import { techLogout } from "../../redux/tech/tech.actions";
|
import { techLogout } from "../../redux/tech/tech.actions";
|
||||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||||
import { BsKanban } from "react-icons/bs";
|
import { BsKanban } from "react-icons/bs";
|
||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
|
|
||||||
const { Sider } = Layout;
|
const { Sider } = Layout;
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
technician: selectTechnician,
|
technician: selectTechnician,
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
techLogout: () => dispatch(techLogout()),
|
techLogout: () => dispatch(techLogout()),
|
||||||
|
setTimeTicketTaskContext: (context) =>
|
||||||
|
dispatch(setModalContext({ context: context, modal: "timeTicketTask" })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function TechSider({ technician, techLogout }) {
|
export function TechSider({
|
||||||
|
technician,
|
||||||
|
techLogout,
|
||||||
|
bodyshop,
|
||||||
|
setTimeTicketTaskContext,
|
||||||
|
}) {
|
||||||
const [collapsed, setCollapsed] = useState(true);
|
const [collapsed, setCollapsed] = useState(true);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const onCollapse = (collapsed) => {
|
const onCollapse = (collapsed) => {
|
||||||
setCollapsed(collapsed);
|
setCollapsed(collapsed);
|
||||||
};
|
};
|
||||||
|
const { Enhanced_Payroll } = useTreatments(
|
||||||
|
["Enhanced_Payroll"],
|
||||||
|
{},
|
||||||
|
bodyshop.imexshopid
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sider
|
<Sider
|
||||||
@@ -51,13 +72,38 @@ export function TechSider({ technician, techLogout }) {
|
|||||||
<Menu.Item key="2" disabled={!!!technician} icon={<SearchOutlined />}>
|
<Menu.Item key="2" disabled={!!!technician} icon={<SearchOutlined />}>
|
||||||
<Link to={`/tech/joblookup`}>{t("menus.tech.joblookup")}</Link>
|
<Link to={`/tech/joblookup`}>{t("menus.tech.joblookup")}</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
{Enhanced_Payroll.treatment === "on" ? (
|
||||||
key="3"
|
<>
|
||||||
disabled={!!!technician}
|
<Menu.Item
|
||||||
icon={<Icon component={FaBusinessTime} />}
|
key="TechAssignedProdJobs"
|
||||||
>
|
disabled={!!!technician}
|
||||||
<Link to={`/tech/jobclock`}>{t("menus.tech.jobclockin")}</Link>
|
icon={<UserAddOutlined />}
|
||||||
</Menu.Item>
|
>
|
||||||
|
<Link to={`/tech/assigned`}> {t("menus.tech.assignedjobs")}</Link>
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item
|
||||||
|
key="3"
|
||||||
|
disabled={!!!technician}
|
||||||
|
icon={<Icon component={FaBusinessTime} />}
|
||||||
|
onClick={() => {
|
||||||
|
setTimeTicketTaskContext({
|
||||||
|
actions: {},
|
||||||
|
context: { jobid: null },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("menus.tech.claimtask")}
|
||||||
|
</Menu.Item>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Menu.Item
|
||||||
|
key="3"
|
||||||
|
disabled={!!!technician}
|
||||||
|
icon={<Icon component={FaBusinessTime} />}
|
||||||
|
>
|
||||||
|
<Link to={`/tech/jobclock`}>{t("menus.tech.jobclockin")}</Link>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
key="4"
|
key="4"
|
||||||
disabled={!!!technician}
|
disabled={!!!technician}
|
||||||
@@ -68,6 +114,15 @@ export function TechSider({ technician, techLogout }) {
|
|||||||
<Menu.Item key="5" disabled={!!!technician} icon={<ScheduleOutlined />}>
|
<Menu.Item key="5" disabled={!!!technician} icon={<ScheduleOutlined />}>
|
||||||
<Link to={`/tech/list`}>{t("menus.tech.productionlist")}</Link>
|
<Link to={`/tech/list`}>{t("menus.tech.productionlist")}</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
<Menu.Item
|
||||||
|
key="dispatchedparts"
|
||||||
|
disabled={!!!technician}
|
||||||
|
icon={<CarOutlined />}
|
||||||
|
>
|
||||||
|
<Link to={`/tech/dispatchedparts`}>
|
||||||
|
{t("menus.tech.dispatchedparts")}
|
||||||
|
</Link>
|
||||||
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
key="6"
|
key="6"
|
||||||
disabled={!!!technician}
|
disabled={!!!technician}
|
||||||
@@ -75,6 +130,7 @@ export function TechSider({ technician, techLogout }) {
|
|||||||
>
|
>
|
||||||
<Link to={`/tech/board`}> {t("menus.tech.productionboard")}</Link>
|
<Link to={`/tech/board`}> {t("menus.tech.productionboard")}</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
key="7"
|
key="7"
|
||||||
disabled={!!!technician}
|
disabled={!!!technician}
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
import { DownOutlined } from "@ant-design/icons";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Checkbox,
|
||||||
|
Col,
|
||||||
|
Form,
|
||||||
|
InputNumber,
|
||||||
|
Popover,
|
||||||
|
Radio,
|
||||||
|
Row,
|
||||||
|
Space,
|
||||||
|
Spin,
|
||||||
|
} from "antd";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { GET_JOB_INFO_DRAW_CALCULATIONS } from "../../graphql/jobs-lines.queries";
|
||||||
|
import { useQuery } from "@apollo/client";
|
||||||
|
|
||||||
|
export default function TimeTicketCalculatorComponent({
|
||||||
|
setProductiveHours,
|
||||||
|
|
||||||
|
jobid,
|
||||||
|
}) {
|
||||||
|
const { loading, data: lineTicketData } = useQuery(GET_JOB_INFO_DRAW_CALCULATIONS, {
|
||||||
|
variables: { id: jobid },
|
||||||
|
skip: !jobid,
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
nextFetchPolicy: "network-only",
|
||||||
|
});
|
||||||
|
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const handleOpenChange = (flag) => setVisible(flag);
|
||||||
|
const handleFinish = ({ type, hourstype, percent }) => {
|
||||||
|
//setProductiveHours(values);
|
||||||
|
//setVisible(false);
|
||||||
|
const eligibleHours = Array.isArray(hourstype)
|
||||||
|
? lineTicketData.joblines.reduce(
|
||||||
|
(acc, val) =>
|
||||||
|
acc + (hourstype.includes(val.mod_lbr_ty) ? val.mod_lb_hrs : 0),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
: lineTicketData.joblines.reduce(
|
||||||
|
(acc, val) =>
|
||||||
|
acc + (hourstype === val.mod_lbr_ty ? val.mod_lb_hrs : 0),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
if (type === "draw") {
|
||||||
|
setProductiveHours(eligibleHours * (percent / 100));
|
||||||
|
} else if (type === "cut") {
|
||||||
|
setProductiveHours(eligibleHours * (percent / 100));
|
||||||
|
console.log(
|
||||||
|
"Cut selected, rate set to: ",
|
||||||
|
lineTicketData.jobs_by_pk[`rate_${hourstype.toLowerCase()}`]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const popContent = (
|
||||||
|
<Spin spinning={loading}>
|
||||||
|
<Form onFinish={handleFinish}>
|
||||||
|
<Form.Item name="type">
|
||||||
|
<Radio.Group>
|
||||||
|
<Radio.Button value="draw">Draw</Radio.Button>
|
||||||
|
<Radio.Button value="cut">Cut of Sale</Radio.Button>
|
||||||
|
</Radio.Group>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item noStyle shouldUpdate>
|
||||||
|
{({ getFieldValue }) => (
|
||||||
|
<Form.Item name="hourstype">
|
||||||
|
{getFieldValue("type") === "draw" ? (
|
||||||
|
<Checkbox.Group>
|
||||||
|
<Row>
|
||||||
|
<Col span={8}>
|
||||||
|
<Checkbox value="LAB" style={{ lineHeight: "32px" }}>
|
||||||
|
Body
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Checkbox value="LAR" style={{ lineHeight: "32px" }}>
|
||||||
|
Refinish
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Checkbox value="LAM" style={{ lineHeight: "32px" }}>
|
||||||
|
Mechanical
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Checkbox value="LAF" style={{ lineHeight: "32px" }}>
|
||||||
|
Frame
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Checkbox value="LAG" style={{ lineHeight: "32px" }}>
|
||||||
|
Glass
|
||||||
|
</Checkbox>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Checkbox.Group>
|
||||||
|
) : (
|
||||||
|
<Radio.Group>
|
||||||
|
<Radio value="LAB">Body</Radio>
|
||||||
|
|
||||||
|
<Radio value="LAR">Refinish</Radio>
|
||||||
|
|
||||||
|
<Radio value="LAM">Mechanical</Radio>
|
||||||
|
|
||||||
|
<Radio value="LAF">Frame</Radio>
|
||||||
|
|
||||||
|
<Radio value="LAG">Glass</Radio>
|
||||||
|
</Radio.Group>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name="percent">
|
||||||
|
<InputNumber min={0} max={100} precision={1} addonAfter="%" />
|
||||||
|
</Form.Item>
|
||||||
|
<Button htmlType="submit">Calculate</Button>
|
||||||
|
</Form>
|
||||||
|
</Spin>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
content={popContent}
|
||||||
|
trigger={["click"]}
|
||||||
|
open={visible}
|
||||||
|
onOpenChange={handleOpenChange}
|
||||||
|
placement="right"
|
||||||
|
destroyTooltipOnHide
|
||||||
|
>
|
||||||
|
<Button onClick={(e) => e.preventDefault()}>
|
||||||
|
<Space>
|
||||||
|
Draw Calculator
|
||||||
|
<DownOutlined />
|
||||||
|
</Space>
|
||||||
|
</Button>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,277 @@
|
|||||||
|
import { useQuery } from "@apollo/client";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Form,
|
||||||
|
InputNumber,
|
||||||
|
Modal,
|
||||||
|
Radio,
|
||||||
|
Select,
|
||||||
|
Space,
|
||||||
|
Table,
|
||||||
|
Typography,
|
||||||
|
} from "antd";
|
||||||
|
import Dinero from "dinero.js";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { GET_JOB_INFO_DRAW_CALCULATIONS } from "../../graphql/jobs-lines.queries";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||||
|
import JobSearchSelectComponent from "../job-search-select/job-search-select.component";
|
||||||
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({});
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(TimeTicketListTeamPay);
|
||||||
|
|
||||||
|
export function TimeTicketListTeamPay({ bodyshop, context, actions }) {
|
||||||
|
//const { refetch } = actions;
|
||||||
|
const { jobId } = context;
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const {
|
||||||
|
//loading,
|
||||||
|
data: lineTicketData,
|
||||||
|
} = useQuery(GET_JOB_INFO_DRAW_CALCULATIONS, {
|
||||||
|
variables: { id: jobId },
|
||||||
|
skip: !jobId,
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
nextFetchPolicy: "network-only",
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleOk = () => {
|
||||||
|
setVisible(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
width={"80%"}
|
||||||
|
open={visible}
|
||||||
|
destroyOnClose
|
||||||
|
onOk={handleOk}
|
||||||
|
onCancel={() => setVisible(false)}
|
||||||
|
>
|
||||||
|
<Form layout="vertical" form={form} initialValues={{ jobid: jobId }}>
|
||||||
|
<LayoutFormRow grow noDivider>
|
||||||
|
<Form.Item shouldUpdate>
|
||||||
|
{() => (
|
||||||
|
<Form.Item
|
||||||
|
name="jobid"
|
||||||
|
label={t("timetickets.fields.ro_number")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: !(
|
||||||
|
form.getFieldValue("cost_center") ===
|
||||||
|
"timetickets.labels.shift"
|
||||||
|
),
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<JobSearchSelectComponent
|
||||||
|
convertedOnly={!bodyshop.tt_allow_post_to_invoiced}
|
||||||
|
notExported={!bodyshop.tt_allow_post_to_invoiced}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("timetickets.fields.date")}
|
||||||
|
name="date"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<FormDatePicker />
|
||||||
|
</Form.Item>
|
||||||
|
</LayoutFormRow>
|
||||||
|
|
||||||
|
<LayoutFormRow grow noDivider>
|
||||||
|
<Form.Item name="team" label="Team">
|
||||||
|
<Select
|
||||||
|
options={Teams.map((team) => ({
|
||||||
|
value: team.name,
|
||||||
|
label: team.name,
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name="hourstype">
|
||||||
|
<Radio.Group>
|
||||||
|
<Radio value="LAB">Body</Radio>
|
||||||
|
<Radio value="LAR">Refinish</Radio>
|
||||||
|
<Radio value="LAM">Mechanical</Radio>
|
||||||
|
<Radio value="LAF">Frame</Radio>
|
||||||
|
<Radio value="LAG">Glass</Radio>
|
||||||
|
</Radio.Group>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name="percent">
|
||||||
|
<InputNumber min={0} max={100} precision={1} addonAfter="%" />
|
||||||
|
</Form.Item>
|
||||||
|
</LayoutFormRow>
|
||||||
|
|
||||||
|
<Form.Item shouldUpdate noStyle>
|
||||||
|
{({ getFieldsValue }) => {
|
||||||
|
const formData = getFieldsValue();
|
||||||
|
|
||||||
|
let data = [];
|
||||||
|
let eligibleHours = 0;
|
||||||
|
const theTeam = Teams.find((team) => team.name === formData.team);
|
||||||
|
if (theTeam) {
|
||||||
|
eligibleHours =
|
||||||
|
lineTicketData.joblines.reduce(
|
||||||
|
(acc, val) =>
|
||||||
|
acc +
|
||||||
|
(formData.hourstype === val.mod_lbr_ty
|
||||||
|
? val.mod_lb_hrs
|
||||||
|
: 0),
|
||||||
|
0
|
||||||
|
) * (formData.percent / 100 || 0);
|
||||||
|
|
||||||
|
data = theTeam.employees.map((e) => {
|
||||||
|
return {
|
||||||
|
employeeid: e.employeeid,
|
||||||
|
percentage: e.percentage,
|
||||||
|
rate: e.rates[formData.hourstype],
|
||||||
|
cost_center:
|
||||||
|
bodyshop.md_responsibility_centers.defaults.costs[
|
||||||
|
formData.hourstype
|
||||||
|
],
|
||||||
|
productivehrs:
|
||||||
|
Math.round(eligibleHours * 100 * (e.percentage / 100)) /
|
||||||
|
100,
|
||||||
|
pay: Dinero({
|
||||||
|
amount: Math.round(
|
||||||
|
(e.rates[formData.hourstype] || 0) * 100
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.multiply(
|
||||||
|
Math.round(eligibleHours * 100 * (e.percentage / 100)) /
|
||||||
|
100
|
||||||
|
)
|
||||||
|
.toFormat("$0.00"),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
dataSource={data}
|
||||||
|
rowKey={"employeeid"}
|
||||||
|
title={() => (
|
||||||
|
<Space>
|
||||||
|
<Typography.Title level={4}>
|
||||||
|
Tickets to be Created
|
||||||
|
</Typography.Title>
|
||||||
|
<span>{`(${eligibleHours} hours to split)`}</span>
|
||||||
|
</Space>
|
||||||
|
)}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: t("timetickets.fields.employee"),
|
||||||
|
dataIndex: "employee",
|
||||||
|
key: "employee",
|
||||||
|
render: (text, record) => {
|
||||||
|
const emp = bodyshop.employees.find(
|
||||||
|
(e) => e.id === record.employeeid
|
||||||
|
);
|
||||||
|
return `${emp?.first_name} ${emp?.last_name}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("timetickets.fields.cost_center"),
|
||||||
|
dataIndex: "cost_center",
|
||||||
|
key: "cost_center",
|
||||||
|
|
||||||
|
render: (text, record) =>
|
||||||
|
record.cost_center === "timetickets.labels.shift"
|
||||||
|
? t(record.cost_center)
|
||||||
|
: record.cost_center,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("timetickets.fields.productivehrs"),
|
||||||
|
dataIndex: "productivehrs",
|
||||||
|
key: "productivehrs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Percentage",
|
||||||
|
dataIndex: "percentage",
|
||||||
|
key: "percentage",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Rate",
|
||||||
|
dataIndex: "rate",
|
||||||
|
key: "rate",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Pay",
|
||||||
|
dataIndex: "pay",
|
||||||
|
key: "pay",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
<Button onClick={() => setVisible(true)}>Assign Team Pay </Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Teams = [
|
||||||
|
{
|
||||||
|
name: "Team A",
|
||||||
|
employees: [
|
||||||
|
{
|
||||||
|
employeeid: "9f1bdc23-8dc2-4b6a-8ca1-5f83a6fdcc22",
|
||||||
|
percentage: 50,
|
||||||
|
rates: {
|
||||||
|
LAB: 10,
|
||||||
|
LAR: 15,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
employeeid: "201db66c-96c7-41ec-bed4-76842ba93087",
|
||||||
|
percentage: 50,
|
||||||
|
rates: {
|
||||||
|
LAB: 20,
|
||||||
|
LAR: 25,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Team B",
|
||||||
|
employees: [
|
||||||
|
{
|
||||||
|
employeeid: "9f1bdc23-8dc2-4b6a-8ca1-5f83a6fdcc22",
|
||||||
|
percentage: 75,
|
||||||
|
rates: {
|
||||||
|
LAB: 100,
|
||||||
|
LAR: 150,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
employeeid: "201db66c-96c7-41ec-bed4-76842ba93087",
|
||||||
|
percentage: 25,
|
||||||
|
rates: {
|
||||||
|
LAB: 200,
|
||||||
|
LAR: 250,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
import { EditFilled } from "@ant-design/icons";
|
import { EditFilled, SyncOutlined } from "@ant-design/icons";
|
||||||
import { Card, Space, Table } from "antd";
|
import { Button, Card, Checkbox, Space, Table } from "antd";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import React, { useMemo, useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
import {
|
import {
|
||||||
selectAuthLevel,
|
selectAuthLevel,
|
||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
@@ -18,18 +19,22 @@ import RbacWrapper, {
|
|||||||
HasRbacAccess,
|
HasRbacAccess,
|
||||||
} from "../rbac-wrapper/rbac-wrapper.component";
|
} from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
import TimeTicketEnterButton from "../time-ticket-enter-button/time-ticket-enter-button.component";
|
import TimeTicketEnterButton from "../time-ticket-enter-button/time-ticket-enter-button.component";
|
||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
authLevel: selectAuthLevel,
|
authLevel: selectAuthLevel,
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
setTimeTicketTaskContext: (context) =>
|
||||||
|
dispatch(setModalContext({ context: context, modal: "timeTicketTask" })),
|
||||||
});
|
});
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(TimeTicketList);
|
export default connect(mapStateToProps, mapDispatchToProps)(TimeTicketList);
|
||||||
|
|
||||||
export function TimeTicketList({
|
export function TimeTicketList({
|
||||||
bodyshop,
|
bodyshop,
|
||||||
|
setTimeTicketTaskContext,
|
||||||
authLevel,
|
authLevel,
|
||||||
currentUser,
|
currentUser,
|
||||||
disabled,
|
disabled,
|
||||||
@@ -46,7 +51,11 @@ export function TimeTicketList({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { Enhanced_Payroll } = useTreatments(
|
||||||
|
["Enhanced_Payroll"],
|
||||||
|
{},
|
||||||
|
bodyshop.imexshopid
|
||||||
|
);
|
||||||
const totals = useMemo(() => {
|
const totals = useMemo(() => {
|
||||||
if (timetickets)
|
if (timetickets)
|
||||||
return timetickets.reduce(
|
return timetickets.reduce(
|
||||||
@@ -61,6 +70,14 @@ export function TimeTicketList({
|
|||||||
}, [timetickets]);
|
}, [timetickets]);
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t("timetickets.fields.committed"),
|
||||||
|
dataIndex: "committed_at",
|
||||||
|
key: "committed_at",
|
||||||
|
render: (text, record) => (
|
||||||
|
<Checkbox disabled checked={record.committed_at} />
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("timetickets.fields.date"),
|
title: t("timetickets.fields.date"),
|
||||||
dataIndex: "date",
|
dataIndex: "date",
|
||||||
@@ -118,21 +135,26 @@ export function TimeTicketList({
|
|||||||
}) || [],
|
}) || [],
|
||||||
onFilter: (value, record) => value.includes(record.cost_center),
|
onFilter: (value, record) => value.includes(record.cost_center),
|
||||||
},
|
},
|
||||||
{
|
...(jobId
|
||||||
title: t("jobs.fields.ro_number"),
|
? []
|
||||||
dataIndex: "ro_number",
|
: [
|
||||||
key: "ro_number",
|
{
|
||||||
sorter: (a, b) =>
|
title: t("jobs.fields.ro_number"),
|
||||||
alphaSort(a.job && a.job.ro_number, b.job && b.job.ro_number),
|
dataIndex: "ro_number",
|
||||||
sortOrder:
|
key: "ro_number",
|
||||||
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
sorter: (a, b) =>
|
||||||
render: (text, record) =>
|
alphaSort(a.job && a.job.ro_number, b.job && b.job.ro_number),
|
||||||
record.job && (
|
sortOrder:
|
||||||
<Link to={"/manage/jobs/" + record.job.id}>
|
state.sortedInfo.columnKey === "ro_number" &&
|
||||||
{record.job.ro_number || "N/A"}
|
state.sortedInfo.order,
|
||||||
</Link>
|
render: (text, record) =>
|
||||||
),
|
record.job && (
|
||||||
},
|
<Link to={"/manage/jobs/" + record.job.id}>
|
||||||
|
{record.job.ro_number || "N/A"}
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]),
|
||||||
{
|
{
|
||||||
title: t("timetickets.fields.productivehrs"),
|
title: t("timetickets.fields.productivehrs"),
|
||||||
dataIndex: "productivehrs",
|
dataIndex: "productivehrs",
|
||||||
@@ -142,14 +164,20 @@ export function TimeTicketList({
|
|||||||
state.sortedInfo.columnKey === "productivehrs" &&
|
state.sortedInfo.columnKey === "productivehrs" &&
|
||||||
state.sortedInfo.order,
|
state.sortedInfo.order,
|
||||||
},
|
},
|
||||||
{
|
...(Enhanced_Payroll.treatment === "on"
|
||||||
title: t("timetickets.fields.actualhrs"),
|
? []
|
||||||
dataIndex: "actualhrs",
|
: [
|
||||||
key: "actualhrs",
|
{
|
||||||
sorter: (a, b) => a.actualhrs - b.actualhrs,
|
title: t("timetickets.fields.actualhrs"),
|
||||||
sortOrder:
|
dataIndex: "actualhrs",
|
||||||
state.sortedInfo.columnKey === "actualhrs" && state.sortedInfo.order,
|
key: "actualhrs",
|
||||||
},
|
sorter: (a, b) => a.actualhrs - b.actualhrs,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "actualhrs" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
|
||||||
{
|
{
|
||||||
title: t("timetickets.fields.memo"),
|
title: t("timetickets.fields.memo"),
|
||||||
dataIndex: "memo",
|
dataIndex: "memo",
|
||||||
@@ -160,42 +188,59 @@ export function TimeTicketList({
|
|||||||
render: (text, record) =>
|
render: (text, record) =>
|
||||||
record.clockon || record.clockoff ? t(record.memo) : record.memo,
|
record.clockon || record.clockoff ? t(record.memo) : record.memo,
|
||||||
},
|
},
|
||||||
{
|
...(Enhanced_Payroll.treatment === "on"
|
||||||
title: t("timetickets.fields.clockon"),
|
? [
|
||||||
dataIndex: "clockon",
|
{
|
||||||
key: "clockon",
|
title: t("timetickets.fields.task_name"),
|
||||||
|
dataIndex: "task_name",
|
||||||
|
key: "task_name",
|
||||||
|
sorter: (a, b) => alphaSort(a.task_name, b.task_name),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "task_name" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
...(Enhanced_Payroll.treatment === "on"
|
||||||
|
? []
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
title: t("timetickets.fields.clockon"),
|
||||||
|
dataIndex: "clockon",
|
||||||
|
key: "clockon",
|
||||||
|
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<DateTimeFormatter>{record.clockon}</DateTimeFormatter>
|
<DateTimeFormatter>{record.clockon}</DateTimeFormatter>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("timetickets.fields.clockoff"),
|
title: t("timetickets.fields.clockoff"),
|
||||||
dataIndex: "clockoff",
|
dataIndex: "clockoff",
|
||||||
key: "clockoff",
|
key: "clockoff",
|
||||||
|
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<DateTimeFormatter>{record.clockoff}</DateTimeFormatter>
|
<DateTimeFormatter>{record.clockoff}</DateTimeFormatter>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("timetickets.fields.clockhours"),
|
title: t("timetickets.fields.clockhours"),
|
||||||
dataIndex: "clockhours",
|
dataIndex: "clockhours",
|
||||||
key: "clockhours",
|
key: "clockhours",
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
if (record.clockoff && record.clockon)
|
if (record.clockoff && record.clockon)
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{moment(record.clockoff)
|
{moment(record.clockoff)
|
||||||
.diff(moment(record.clockon), "hours", true)
|
.diff(moment(record.clockon), "hours", true)
|
||||||
.toFixed(2)}
|
.toFixed(2)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
else {
|
else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
]),
|
||||||
{
|
{
|
||||||
title: t("timetickets.fields.created_by"),
|
title: t("timetickets.fields.created_by"),
|
||||||
dataIndex: "created_by",
|
dataIndex: "created_by",
|
||||||
@@ -205,6 +250,15 @@ export function TimeTicketList({
|
|||||||
state.sortedInfo.columnKey === "created_by" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "created_by" && state.sortedInfo.order,
|
||||||
render: (text, record) => record.created_by,
|
render: (text, record) => record.created_by,
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// title: "Pay",
|
||||||
|
// dataIndex: "pay",
|
||||||
|
// key: "pay",
|
||||||
|
// render: (text, record) =>
|
||||||
|
// Dinero({ amount: Math.round(record.rate * 100) })
|
||||||
|
// .multiply(record.flat_rate ? record.productivehrs : record.actualhrs)
|
||||||
|
// .toFormat("$0.00"),
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
title: t("general.labels.actions"),
|
title: t("general.labels.actions"),
|
||||||
dataIndex: "actions",
|
dataIndex: "actions",
|
||||||
@@ -234,6 +288,11 @@ export function TimeTicketList({
|
|||||||
timeticket: record,
|
timeticket: record,
|
||||||
}}
|
}}
|
||||||
disabled={
|
disabled={
|
||||||
|
HasRbacAccess({
|
||||||
|
bodyshop,
|
||||||
|
authLevel: authLevel,
|
||||||
|
action: "timetickets:editcommitted",
|
||||||
|
}) &&
|
||||||
HasRbacAccess({
|
HasRbacAccess({
|
||||||
bodyshop,
|
bodyshop,
|
||||||
authLevel: authLevel,
|
authLevel: authLevel,
|
||||||
@@ -261,6 +320,19 @@ export function TimeTicketList({
|
|||||||
title={t("timetickets.labels.timetickets")}
|
title={t("timetickets.labels.timetickets")}
|
||||||
extra={
|
extra={
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
|
{jobId && bodyshop.md_tasks_presets.enable_tasks && (
|
||||||
|
<Button
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={() => {
|
||||||
|
setTimeTicketTaskContext({
|
||||||
|
actions: { refetch: refetch },
|
||||||
|
context: { jobid: jobId },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("timetickets.actions.claimtasks")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
{jobId &&
|
{jobId &&
|
||||||
(techConsole ? null : (
|
(techConsole ? null : (
|
||||||
<TimeTicketEnterButton
|
<TimeTicketEnterButton
|
||||||
@@ -277,6 +349,13 @@ export function TimeTicketList({
|
|||||||
</TimeTicketEnterButton>
|
</TimeTicketEnterButton>
|
||||||
))}
|
))}
|
||||||
{extra}
|
{extra}
|
||||||
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
refetch();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SyncOutlined />
|
||||||
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
|||||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||||
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
|
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
import TimeTicketList from "../time-ticket-list/time-ticket-list.component";
|
import TimeTicketList from "../time-ticket-list/time-ticket-list.component";
|
||||||
|
import TimeTicketCalculatorComponent from "../time-ticket-calculator/time-ticket-calculator.component";
|
||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -36,9 +38,15 @@ export function TimeTicketModalComponent({
|
|||||||
authLevel,
|
authLevel,
|
||||||
employeeAutoCompleteOptions,
|
employeeAutoCompleteOptions,
|
||||||
isEdit,
|
isEdit,
|
||||||
|
disabled,
|
||||||
employeeSelectDisabled,
|
employeeSelectDisabled,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { Enhanced_Payroll } = useTreatments(
|
||||||
|
["Enhanced_Payroll"],
|
||||||
|
{},
|
||||||
|
bodyshop.imexshopid
|
||||||
|
);
|
||||||
const [loadLineTicketData, { called, loading, data: lineTicketData }] =
|
const [loadLineTicketData, { called, loading, data: lineTicketData }] =
|
||||||
useLazyQuery(GET_LINE_TICKET_BY_PK, {
|
useLazyQuery(GET_LINE_TICKET_BY_PK, {
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
@@ -49,14 +57,14 @@ export function TimeTicketModalComponent({
|
|||||||
<Select
|
<Select
|
||||||
value={value === "timetickets.labels.shift" ? t(value) : value}
|
value={value === "timetickets.labels.shift" ? t(value) : value}
|
||||||
{...props}
|
{...props}
|
||||||
disabled={value === "timetickets.labels.shift"}
|
disabled={value === "timetickets.labels.shift" || disabled}
|
||||||
>
|
>
|
||||||
{emps &&
|
{emps &&
|
||||||
emps.rates.map((item) => (
|
emps.rates.map((item) => (
|
||||||
<Select.Option key={item.cost_center} value={item.cost_center}>
|
<Select.Option key={item.cost_center} value={item.cost_center}>
|
||||||
{item.cost_center === "timetickets.labels.shift"
|
{item.cost_center === "timetickets.labels.shift"
|
||||||
? t(item.cost_center)
|
? t(item.cost_center)
|
||||||
: bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
|
: bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || Enhanced_Payroll.treatment === 'on'
|
||||||
? t(
|
? t(
|
||||||
`joblines.fields.lbr_types.${item.cost_center.toUpperCase()}`
|
`joblines.fields.lbr_types.${item.cost_center.toUpperCase()}`
|
||||||
)
|
)
|
||||||
@@ -72,7 +80,7 @@ export function TimeTicketModalComponent({
|
|||||||
<Input
|
<Input
|
||||||
value={value?.startsWith("timetickets.") ? t(value) : value}
|
value={value?.startsWith("timetickets.") ? t(value) : value}
|
||||||
{...props}
|
{...props}
|
||||||
disabled={value?.startsWith("timetickets.")}
|
disabled={value?.startsWith("timetickets.") || disabled}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -126,7 +134,7 @@ export function TimeTicketModalComponent({
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<EmployeeSearchSelect
|
<EmployeeSearchSelect
|
||||||
disabled={employeeSelectDisabled}
|
disabled={employeeSelectDisabled || disabled}
|
||||||
options={employeeAutoCompleteOptions}
|
options={employeeAutoCompleteOptions}
|
||||||
onSelect={(value) => {
|
onSelect={(value) => {
|
||||||
const emps =
|
const emps =
|
||||||
@@ -178,65 +186,73 @@ export function TimeTicketModalComponent({
|
|||||||
<LayoutFormRow>
|
<LayoutFormRow>
|
||||||
<Form.Item shouldUpdate>
|
<Form.Item shouldUpdate>
|
||||||
{() => (
|
{() => (
|
||||||
<Form.Item
|
<>
|
||||||
label={t("timetickets.fields.productivehrs")}
|
<Form.Item
|
||||||
name="productivehrs"
|
label={t("timetickets.fields.productivehrs")}
|
||||||
rules={[
|
name="productivehrs"
|
||||||
({ getFieldValue }) => ({
|
rules={[
|
||||||
validator(rule, value) {
|
({ getFieldValue }) => ({
|
||||||
if (!bodyshop.tt_enforce_hours_for_tech_console) {
|
validator(rule, value) {
|
||||||
return Promise.resolve();
|
if (!bodyshop.tt_enforce_hours_for_tech_console) {
|
||||||
}
|
return Promise.resolve();
|
||||||
if (
|
}
|
||||||
!value ||
|
if (
|
||||||
getFieldValue("cost_center") === null ||
|
!value ||
|
||||||
!lineTicketData
|
getFieldValue("cost_center") === null ||
|
||||||
)
|
!lineTicketData
|
||||||
return Promise.resolve();
|
)
|
||||||
|
return Promise.resolve();
|
||||||
|
|
||||||
//Check the cost center,
|
//Check the cost center,
|
||||||
const totals = CalculateAllocationsTotals(
|
const totals = CalculateAllocationsTotals(
|
||||||
bodyshop,
|
bodyshop,
|
||||||
lineTicketData.joblines,
|
lineTicketData.joblines,
|
||||||
lineTicketData.timetickets,
|
lineTicketData.timetickets,
|
||||||
lineTicketData.jobs_by_pk.lbr_adjustments
|
lineTicketData.jobs_by_pk.lbr_adjustments
|
||||||
);
|
|
||||||
|
|
||||||
const fieldTypeToCheck =
|
|
||||||
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
|
|
||||||
? "mod_lbr_ty"
|
|
||||||
: "cost_center";
|
|
||||||
|
|
||||||
const costCenterDiff =
|
|
||||||
Math.round(
|
|
||||||
totals.find(
|
|
||||||
(total) =>
|
|
||||||
total[fieldTypeToCheck] ===
|
|
||||||
getFieldValue("cost_center")
|
|
||||||
)?.difference * 10
|
|
||||||
) / 10;
|
|
||||||
|
|
||||||
if (value > costCenterDiff)
|
|
||||||
return Promise.reject(
|
|
||||||
t(
|
|
||||||
"timetickets.validation.hoursenteredmorethanavailable"
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
else {
|
|
||||||
return Promise.resolve();
|
const fieldTypeToCheck =
|
||||||
}
|
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
|
||||||
|
? "mod_lbr_ty"
|
||||||
|
: "cost_center";
|
||||||
|
|
||||||
|
const costCenterDiff =
|
||||||
|
Math.round(
|
||||||
|
totals.find(
|
||||||
|
(total) =>
|
||||||
|
total[fieldTypeToCheck] ===
|
||||||
|
getFieldValue("cost_center")
|
||||||
|
)?.difference * 10
|
||||||
|
) / 10;
|
||||||
|
|
||||||
|
if (value > costCenterDiff)
|
||||||
|
return Promise.reject(
|
||||||
|
t(
|
||||||
|
"timetickets.validation.hoursenteredmorethanavailable"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
else {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
required:
|
||||||
|
form.getFieldValue("cost_center") !==
|
||||||
|
"timetickets.labels.shift",
|
||||||
|
//message: t("general.validation.required"),
|
||||||
},
|
},
|
||||||
}),
|
]}
|
||||||
{
|
>
|
||||||
required:
|
<InputNumber precision={1} />
|
||||||
form.getFieldValue("cost_center") !==
|
</Form.Item>
|
||||||
"timetickets.labels.shift",
|
<TimeTicketCalculatorComponent
|
||||||
//message: t("general.validation.required"),
|
jobid={form.getFieldValue("jobid")}
|
||||||
},
|
setProductiveHours={(productivehrs) =>
|
||||||
]}
|
form.setFieldsValue({ productivehrs })
|
||||||
>
|
}
|
||||||
<InputNumber precision={1} />
|
/>
|
||||||
</Form.Item>
|
</>
|
||||||
)}
|
)}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@@ -270,6 +286,7 @@ export function TimeTicketModalComponent({
|
|||||||
<FormDateTimePicker
|
<FormDateTimePicker
|
||||||
minuteStep={5}
|
minuteStep={5}
|
||||||
disabled={
|
disabled={
|
||||||
|
disabled ||
|
||||||
!HasRbacAccess({
|
!HasRbacAccess({
|
||||||
bodyshop,
|
bodyshop,
|
||||||
authLevel,
|
authLevel,
|
||||||
@@ -308,6 +325,7 @@ export function TimeTicketModalComponent({
|
|||||||
<FormDateTimePicker
|
<FormDateTimePicker
|
||||||
minuteStep={5}
|
minuteStep={5}
|
||||||
disabled={
|
disabled={
|
||||||
|
disabled ||
|
||||||
!HasRbacAccess({
|
!HasRbacAccess({
|
||||||
bodyshop,
|
bodyshop,
|
||||||
authLevel,
|
authLevel,
|
||||||
@@ -363,7 +381,12 @@ export function TimeTicketModalComponent({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LaborAllocationContainer({ jobid, loading, lineTicketData }) {
|
export function LaborAllocationContainer({
|
||||||
|
jobid,
|
||||||
|
loading,
|
||||||
|
lineTicketData,
|
||||||
|
hideTimeTickets = false,
|
||||||
|
}) {
|
||||||
if (loading) return <LoadingSkeleton />;
|
if (loading) return <LoadingSkeleton />;
|
||||||
if (!lineTicketData) return null;
|
if (!lineTicketData) return null;
|
||||||
return (
|
return (
|
||||||
@@ -374,12 +397,13 @@ export function LaborAllocationContainer({ jobid, loading, lineTicketData }) {
|
|||||||
timetickets={lineTicketData.timetickets}
|
timetickets={lineTicketData.timetickets}
|
||||||
adjustments={lineTicketData.jobs_by_pk.lbr_adjustments}
|
adjustments={lineTicketData.jobs_by_pk.lbr_adjustments}
|
||||||
/>
|
/>
|
||||||
|
{!hideTimeTickets && (
|
||||||
<TimeTicketList
|
<TimeTicketList
|
||||||
loading={loading}
|
loading={loading}
|
||||||
timetickets={lineTicketData.timetickets}
|
timetickets={lineTicketData.timetickets}
|
||||||
techConsole
|
techConsole
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
|||||||
import { selectTimeTicket } from "../../redux/modals/modals.selectors";
|
import { selectTimeTicket } from "../../redux/modals/modals.selectors";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import TimeTicketModalComponent from "./time-ticket-modal.component";
|
import TimeTicketModalComponent from "./time-ticket-modal.component";
|
||||||
|
import TimeTicketsCommitToggleComponent from "../time-tickets-commit-toggle/time-tickets-commit-toggle.component";
|
||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
timeTicketModal: selectTimeTicket,
|
timeTicketModal: selectTimeTicket,
|
||||||
@@ -34,7 +36,11 @@ export function TimeTicketModalContainer({
|
|||||||
const [enterAgain, setEnterAgain] = useState(false);
|
const [enterAgain, setEnterAgain] = useState(false);
|
||||||
const [insertTicket] = useMutation(INSERT_NEW_TIME_TICKET);
|
const [insertTicket] = useMutation(INSERT_NEW_TIME_TICKET);
|
||||||
const [updateTicket] = useMutation(UPDATE_TIME_TICKET);
|
const [updateTicket] = useMutation(UPDATE_TIME_TICKET);
|
||||||
|
const { Enhanced_Payroll } = useTreatments(
|
||||||
|
["Enhanced_Payroll"],
|
||||||
|
{},
|
||||||
|
bodyshop.imexshopid
|
||||||
|
);
|
||||||
const { data: EmployeeAutoCompleteData } = useQuery(QUERY_ACTIVE_EMPLOYEES, {
|
const { data: EmployeeAutoCompleteData } = useQuery(QUERY_ACTIVE_EMPLOYEES, {
|
||||||
skip: !timeTicketModal.visible,
|
skip: !timeTicketModal.visible,
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
@@ -147,7 +153,7 @@ export function TimeTicketModalContainer({
|
|||||||
if (!!changedFields.cost_center && !!EmployeeAutoCompleteData) {
|
if (!!changedFields.cost_center && !!EmployeeAutoCompleteData) {
|
||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
ciecacode:
|
ciecacode:
|
||||||
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
|
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || Enhanced_Payroll.treatment === 'on'
|
||||||
? changedFields.cost_center
|
? changedFields.cost_center
|
||||||
: Object.keys(
|
: Object.keys(
|
||||||
bodyshop.md_responsibility_centers.defaults.costs
|
bodyshop.md_responsibility_centers.defaults.costs
|
||||||
@@ -175,7 +181,11 @@ export function TimeTicketModalContainer({
|
|||||||
footer={
|
footer={
|
||||||
<span>
|
<span>
|
||||||
<Button onClick={handleCancel}>{t("general.actions.cancel")}</Button>
|
<Button onClick={handleCancel}>{t("general.actions.cancel")}</Button>
|
||||||
<Button loading={loading} onClick={() => form.submit()}>
|
<Button
|
||||||
|
loading={loading}
|
||||||
|
disabled={timeTicketModal.context?.timeticket?.committed_at}
|
||||||
|
onClick={() => form.submit()}
|
||||||
|
>
|
||||||
{t("general.actions.save")}
|
{t("general.actions.save")}
|
||||||
</Button>
|
</Button>
|
||||||
{timeTicketModal.context && timeTicketModal.context.id ? null : (
|
{timeTicketModal.context && timeTicketModal.context.id ? null : (
|
||||||
@@ -199,6 +209,7 @@ export function TimeTicketModalContainer({
|
|||||||
autoComplete={"off"}
|
autoComplete={"off"}
|
||||||
form={form}
|
form={form}
|
||||||
onFinishFailed={() => setEnterAgain(false)}
|
onFinishFailed={() => setEnterAgain(false)}
|
||||||
|
disabled={timeTicketModal.context?.timeticket?.committed_at}
|
||||||
initialValues={
|
initialValues={
|
||||||
timeTicketModal.context.timeticket
|
timeTicketModal.context.timeticket
|
||||||
? {
|
? {
|
||||||
@@ -219,6 +230,9 @@ export function TimeTicketModalContainer({
|
|||||||
<PageHeader
|
<PageHeader
|
||||||
extra={
|
extra={
|
||||||
<Space>
|
<Space>
|
||||||
|
<TimeTicketsCommitToggleComponent
|
||||||
|
timeticket={timeTicketModal.context?.timeticket}
|
||||||
|
/>
|
||||||
<Button onClick={handleCancel}>
|
<Button onClick={handleCancel}>
|
||||||
{t("general.actions.cancel")}
|
{t("general.actions.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -242,14 +256,16 @@ export function TimeTicketModalContainer({
|
|||||||
<TimeTicketModalComponent
|
<TimeTicketModalComponent
|
||||||
isEdit={timeTicketModal.context.id}
|
isEdit={timeTicketModal.context.id}
|
||||||
form={form}
|
form={form}
|
||||||
|
disabled={timeTicketModal.context?.timeticket?.committed_at}
|
||||||
employeeAutoCompleteOptions={
|
employeeAutoCompleteOptions={
|
||||||
EmployeeAutoCompleteData && EmployeeAutoCompleteData.employees
|
EmployeeAutoCompleteData && EmployeeAutoCompleteData.employees
|
||||||
}
|
}
|
||||||
employeeSelectDisabled={
|
employeeSelectDisabled={
|
||||||
timeTicketModal.context?.timeticket?.employeeid &&
|
timeTicketModal.context?.timeticket?.committed_at ||
|
||||||
|
(timeTicketModal.context?.timeticket?.employeeid &&
|
||||||
!timeTicketModal.context.id
|
!timeTicketModal.context.id
|
||||||
? true
|
? true
|
||||||
: false
|
: false)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -0,0 +1,197 @@
|
|||||||
|
import {
|
||||||
|
Alert,
|
||||||
|
Col,
|
||||||
|
Form,
|
||||||
|
Radio,
|
||||||
|
Row,
|
||||||
|
Skeleton,
|
||||||
|
Space,
|
||||||
|
Spin,
|
||||||
|
Typography,
|
||||||
|
} from "antd";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import ReadOnlyFormItemComponent from "../form-items-formatted/read-only-form-item.component";
|
||||||
|
import JobSearchSelectComponent from "../job-search-select/job-search-select.component";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
//currentUser: selectCurrentUser
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(TimeTicketTaskModalComponent);
|
||||||
|
|
||||||
|
export function TimeTicketTaskModalComponent({
|
||||||
|
bodyshop,
|
||||||
|
form,
|
||||||
|
loading,
|
||||||
|
completedTasks,
|
||||||
|
unassignedHours,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col xl={12} lg={24}>
|
||||||
|
<Form.Item
|
||||||
|
name="jobid"
|
||||||
|
label={t("timetickets.fields.ro_number")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<JobSearchSelectComponent convertedOnly={true} notExported={true} />
|
||||||
|
</Form.Item>
|
||||||
|
<Space wrap>
|
||||||
|
<Form.Item name="task" label={t("timetickets.labels.task")}>
|
||||||
|
{loading ? (
|
||||||
|
<Spin />
|
||||||
|
) : (
|
||||||
|
<Radio.Group
|
||||||
|
optionType="button"
|
||||||
|
options={bodyshop.md_tasks_presets.presets.map((preset) => ({
|
||||||
|
value: preset.name,
|
||||||
|
label: preset.name,
|
||||||
|
disabled: completedTasks
|
||||||
|
.map((task) => task.name)
|
||||||
|
.includes(preset.name),
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item dependencies={["task"]}>
|
||||||
|
{() => {
|
||||||
|
const { task } = form.getFieldsValue();
|
||||||
|
const theTaskPreset = bodyshop.md_tasks_presets?.presets?.find(
|
||||||
|
(tp) => tp.name === task
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!task) return null;
|
||||||
|
return (
|
||||||
|
<table className="task-tickets-table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>{t("bodyshop.fields.md_tasks_presets.percent")}</td>
|
||||||
|
<td>{`${theTaskPreset.percent || 0}%`}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{t("bodyshop.fields.md_tasks_presets.hourstype")}
|
||||||
|
</td>
|
||||||
|
<td>{theTaskPreset.hourstype.join(", ")}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{t("bodyshop.fields.md_tasks_presets.nextstatus")}
|
||||||
|
</td>
|
||||||
|
<td>{theTaskPreset.nextstatus}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
</Space>
|
||||||
|
</Col>
|
||||||
|
<Col xl={12} lg={24}>
|
||||||
|
{loading ? (
|
||||||
|
<Skeleton />
|
||||||
|
) : (
|
||||||
|
<Form.List name="timetickets">
|
||||||
|
{(fields, { add, remove, move }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Typography.Title level={4}>
|
||||||
|
{t("timetickets.labels.claimtaskpreview")}
|
||||||
|
</Typography.Title>
|
||||||
|
<table className="task-tickets-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{t("timetickets.fields.employee")}</th>
|
||||||
|
<th>{t("timetickets.fields.cost_center")}</th>
|
||||||
|
<th>{t("timetickets.fields.ciecacode")}</th>
|
||||||
|
<th>{t("timetickets.fields.productivehrs")}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<tr key={field.key}>
|
||||||
|
<td>
|
||||||
|
<Form.Item
|
||||||
|
key={`${index}employeeid`}
|
||||||
|
name={[field.name, "employeeid"]}
|
||||||
|
>
|
||||||
|
<ReadOnlyFormItemComponent type="employee" />
|
||||||
|
</Form.Item>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Form.Item
|
||||||
|
key={`${index}cost_center`}
|
||||||
|
name={[field.name, "cost_center"]}
|
||||||
|
>
|
||||||
|
<ReadOnlyFormItemComponent />
|
||||||
|
</Form.Item>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Form.Item
|
||||||
|
key={`${index}ciecacode`}
|
||||||
|
name={[field.name, "ciecacode"]}
|
||||||
|
>
|
||||||
|
<ReadOnlyFormItemComponent />
|
||||||
|
</Form.Item>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Form.Item
|
||||||
|
key={`${index}productivehrs`}
|
||||||
|
name={[field.name, "productivehrs"]}
|
||||||
|
>
|
||||||
|
<ReadOnlyFormItemComponent />
|
||||||
|
</Form.Item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<Alert
|
||||||
|
type="success"
|
||||||
|
message={t("timetickets.labels.payrollclaimedtasks")}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
)}
|
||||||
|
{unassignedHours > 0 && (
|
||||||
|
<Alert
|
||||||
|
type="error"
|
||||||
|
message={t("timetickets.validation.unassignedlines", {
|
||||||
|
unassignedHours: unassignedHours,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
{bodyshop?.md_tasks_presets?.use_approvals && (
|
||||||
|
<Col xl={12} lg={24}>
|
||||||
|
<Alert
|
||||||
|
message={t("tt_approvals.labels.approval_queue_in_use")}
|
||||||
|
type="warning"
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { Form, Modal, notification } from "antd";
|
||||||
|
import axios from "axios";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||||
|
import { selectTimeTicketTasks } from "../../redux/modals/modals.selectors";
|
||||||
|
import {
|
||||||
|
selectBodyshop,
|
||||||
|
selectCurrentUser,
|
||||||
|
} from "../../redux/user/user.selectors";
|
||||||
|
import TimeTicketTaskModalComponent from "./time-ticket-task-modal.component";
|
||||||
|
import { useApolloClient } from "@apollo/client";
|
||||||
|
import { QUERY_COMPLETED_TASKS } from "../../graphql/jobs.queries";
|
||||||
|
import "./time-ticket-task-modal.styles.scss";
|
||||||
|
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
timeTicketTasksModal: selectTimeTicketTasks,
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
currentUser: selectCurrentUser,
|
||||||
|
technician: selectTechnician,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
toggleModalVisible: () => dispatch(toggleModalVisible("timeTicketTask")),
|
||||||
|
});
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(TimeTickeTaskModalContainer);
|
||||||
|
|
||||||
|
export function TimeTickeTaskModalContainer({
|
||||||
|
bodyshop,
|
||||||
|
currentUser,
|
||||||
|
technician,
|
||||||
|
timeTicketTasksModal,
|
||||||
|
toggleModalVisible,
|
||||||
|
}) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const { context, visible, actions } = timeTicketTasksModal;
|
||||||
|
const [completedTasks, setCompletedTasks] = useState([]);
|
||||||
|
const [unassignedHours, setUnassignedHours] = useState(0);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const client = useApolloClient();
|
||||||
|
|
||||||
|
async function handleFinish(values) {
|
||||||
|
calculateTickets({ values, handleFinish: true });
|
||||||
|
}
|
||||||
|
const getCompletedTasks = useCallback(
|
||||||
|
async (jobid) => {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
const { data } = await client.query({
|
||||||
|
query: QUERY_COMPLETED_TASKS,
|
||||||
|
variables: { jobid },
|
||||||
|
});
|
||||||
|
|
||||||
|
setCompletedTasks(data.jobs_by_pk.completed_tasks || []);
|
||||||
|
setLoading(false);
|
||||||
|
},
|
||||||
|
[client]
|
||||||
|
);
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible) {
|
||||||
|
form.setFieldsValue({ ...context, task: null, timetickets: null });
|
||||||
|
if (context.jobid) {
|
||||||
|
getCompletedTasks(context.jobid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [context.jobid, visible, getCompletedTasks, form, context]);
|
||||||
|
|
||||||
|
async function handleValueChange(changedValues, allValues) {
|
||||||
|
if (changedValues.jobid) {
|
||||||
|
getCompletedTasks(changedValues.jobid);
|
||||||
|
}
|
||||||
|
if (allValues.jobid && allValues.task) {
|
||||||
|
calculateTickets({ values: allValues, handleFinish: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const calculateTickets = async ({ values, handleFinish }) => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const { data, ...response } = await axios.post("/payroll/claimtask", {
|
||||||
|
jobid: values.jobid,
|
||||||
|
task: values.task,
|
||||||
|
calculateOnly: !handleFinish,
|
||||||
|
employee: technician
|
||||||
|
? {
|
||||||
|
name: `${technician.first_name} ${technician.last_name}`.trim(),
|
||||||
|
employeeid: technician.id,
|
||||||
|
}
|
||||||
|
: { name: currentUser.displayName, email: currentUser.email },
|
||||||
|
});
|
||||||
|
if (response.status === 200 && handleFinish) {
|
||||||
|
//Close the modal
|
||||||
|
if (actions?.refetch) actions.refetch();
|
||||||
|
toggleModalVisible();
|
||||||
|
} else if (handleFinish === false) {
|
||||||
|
form.setFieldsValue({ timetickets: data.ticketsToInsert });
|
||||||
|
setUnassignedHours(data.unassignedHours);
|
||||||
|
} else {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("timetickets.errors.creating", {
|
||||||
|
message: JSON.stringify(data),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("timetickets.errors.creating", { message: error.message }),
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
destroyOnClose
|
||||||
|
open={visible}
|
||||||
|
onCancel={() => {
|
||||||
|
toggleModalVisible();
|
||||||
|
form.resetFields();
|
||||||
|
}}
|
||||||
|
width="80%"
|
||||||
|
onOk={() => form.submit()}
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
autoComplete={"off"}
|
||||||
|
form={form}
|
||||||
|
layout="vertical"
|
||||||
|
onFinish={handleFinish}
|
||||||
|
initialValues={context}
|
||||||
|
onValuesChange={handleValueChange}
|
||||||
|
>
|
||||||
|
<TimeTicketTaskModalComponent
|
||||||
|
form={form}
|
||||||
|
loading={loading}
|
||||||
|
completedTasks={completedTasks}
|
||||||
|
unassignedHours={unassignedHours}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
.task-tickets-table {
|
||||||
|
table-layout: fixed;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
padding: 8px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
|
||||||
|
.ant-form-item {
|
||||||
|
margin-bottom: 0px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:hover {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import { useMutation } from "@apollo/client";
|
||||||
|
import { Button, notification } from "antd";
|
||||||
|
import moment from "moment";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { UPDATE_TIME_TICKET } from "../../graphql/timetickets.queries";
|
||||||
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
|
import {
|
||||||
|
selectBodyshop,
|
||||||
|
selectCurrentUser,
|
||||||
|
} from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
currentUser: selectCurrentUser,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
setTimeTicketContext: (context) =>
|
||||||
|
dispatch(setModalContext({ context: context, modal: "timeTicket" })),
|
||||||
|
});
|
||||||
|
export function TimeTicketsCommit({
|
||||||
|
bodyshop,
|
||||||
|
currentUser,
|
||||||
|
timeticket,
|
||||||
|
disabled,
|
||||||
|
refetch,
|
||||||
|
setTimeTicketContext,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [updateTimeTicket] = useMutation(UPDATE_TIME_TICKET);
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const handleCommit = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const ticketUpdate = timeticket.committed_at
|
||||||
|
? { commited_by: null, committed_at: null }
|
||||||
|
: {
|
||||||
|
commited_by: currentUser.email,
|
||||||
|
committed_at: moment(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await updateTimeTicket({
|
||||||
|
variables: {
|
||||||
|
timeticketId: timeticket.id,
|
||||||
|
timeticket: ticketUpdate,
|
||||||
|
},
|
||||||
|
update(cache) {
|
||||||
|
cache.modify({
|
||||||
|
fields: {
|
||||||
|
timeTickets(existingtickets, { readField }) {
|
||||||
|
return existingtickets.map((ticket) => {
|
||||||
|
if (timeticket.id === readField("id", ticket)) {
|
||||||
|
return {
|
||||||
|
...ticket,
|
||||||
|
...ticketUpdate,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return ticket;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (result.errors) {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("timetickets.errors.creating", {
|
||||||
|
message: JSON.stringify(result.errors),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setTimeTicketContext({
|
||||||
|
context: {
|
||||||
|
id: timeticket.id,
|
||||||
|
timeticket: result.data.update_timetickets.returning[0],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
notification.open({
|
||||||
|
type: "success",
|
||||||
|
message: t("timetickets.successes.committed"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!timeticket?.id) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button onClick={handleCommit} loading={loading} disabled={!timeticket?.id}>
|
||||||
|
{timeticket?.committed_at
|
||||||
|
? t("timetickets.actions.uncommit")
|
||||||
|
: t("timetickets.actions.commitone")}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(TimeTicketsCommit);
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import { useMutation } from "@apollo/client";
|
||||||
|
import { Button, notification } from "antd";
|
||||||
|
import moment from "moment";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { UPDATE_TIME_TICKETS } from "../../graphql/timetickets.queries";
|
||||||
|
import {
|
||||||
|
selectBodyshop,
|
||||||
|
selectCurrentUser,
|
||||||
|
} from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
currentUser: selectCurrentUser,
|
||||||
|
});
|
||||||
|
|
||||||
|
export function TimeTicketsCommit({
|
||||||
|
bodyshop,
|
||||||
|
currentUser,
|
||||||
|
timetickets,
|
||||||
|
disabled,
|
||||||
|
loadingCallback,
|
||||||
|
completedCallback,
|
||||||
|
refetch,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [updateTimeTickets] = useMutation(UPDATE_TIME_TICKETS);
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const handleCommit = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const result = await updateTimeTickets({
|
||||||
|
variables: {
|
||||||
|
timeticketIds: timetickets.map((ticket) => ticket.id),
|
||||||
|
timeticket: {
|
||||||
|
commited_by: currentUser.email,
|
||||||
|
committed_at: moment(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update(cache) {
|
||||||
|
cache.modify({
|
||||||
|
fields: {
|
||||||
|
timeTickets(existingtickets, { readField }) {
|
||||||
|
const modifiedIds = timetickets.map((ticket) => ticket.id);
|
||||||
|
return existingtickets.map((ticket) => {
|
||||||
|
if (modifiedIds.includes(readField("id", ticket))) {
|
||||||
|
return {
|
||||||
|
...ticket,
|
||||||
|
commited_by: currentUser.email,
|
||||||
|
committed_at: moment(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return ticket;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (result.errors) {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("timetickets.errors.creating", {
|
||||||
|
message: JSON.stringify(result.errors),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
notification.open({
|
||||||
|
type: "success",
|
||||||
|
message: t("timetickets.successes.committed"),
|
||||||
|
});
|
||||||
|
if (!!completedCallback) completedCallback([]);
|
||||||
|
if (!!loadingCallback) loadingCallback(false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
onClick={handleCommit}
|
||||||
|
loading={loading}
|
||||||
|
disabled={disabled || timetickets?.length === 0}
|
||||||
|
>
|
||||||
|
{t("timetickets.actions.commit", { count: timetickets?.length })}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, null)(TimeTicketsCommit);
|
||||||
@@ -10,7 +10,7 @@ import { onlyUnique } from "../../utils/arrayHelper";
|
|||||||
import { alphaSort } from "../../utils/sorters";
|
import { alphaSort } from "../../utils/sorters";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
||||||
|
import Dinero from "dinero.js";
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
@@ -129,6 +129,16 @@ const JobRelatedTicketsTable = ({
|
|||||||
return acc;
|
return acc;
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
|
const pay = item.tickets
|
||||||
|
.filter((ticket) => ticket.cost_center === costCenter)
|
||||||
|
.reduce((acc, val) => {
|
||||||
|
return acc.add(
|
||||||
|
Dinero({ amount: Math.round(val.rate * 100) }).multiply(
|
||||||
|
val.flat_rate ? val.productivehrs : val.actualhrs
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, Dinero());
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: `${item.jobKey}${costCenter}`,
|
id: `${item.jobKey}${costCenter}`,
|
||||||
costCenter,
|
costCenter,
|
||||||
@@ -136,6 +146,7 @@ const JobRelatedTicketsTable = ({
|
|||||||
actHrs: actHrs.toFixed(1),
|
actHrs: actHrs.toFixed(1),
|
||||||
prodHrs: prodHrs.toFixed(1),
|
prodHrs: prodHrs.toFixed(1),
|
||||||
clockHrs,
|
clockHrs,
|
||||||
|
pay,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@@ -195,6 +206,15 @@ const JobRelatedTicketsTable = ({
|
|||||||
state.sortedInfo.columnKey === "clockHrs" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "clockHrs" && state.sortedInfo.order,
|
||||||
render: (text, record) => record.clockHrs.toFixed(2),
|
render: (text, record) => record.clockHrs.toFixed(2),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Pay",
|
||||||
|
dataIndex: "Pay",
|
||||||
|
key: "Pay",
|
||||||
|
sorter: (a, b) => a.clockHrs - b.clockHrs,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "clockHrs" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => record.pay.toFormat("$0.00"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("general.labels.actions"),
|
title: t("general.labels.actions"),
|
||||||
dataIndex: "actions",
|
dataIndex: "actions",
|
||||||
|
|||||||
@@ -0,0 +1,241 @@
|
|||||||
|
import { SyncOutlined } from "@ant-design/icons";
|
||||||
|
import { Button, Card, Space, Table, Tag } from "antd";
|
||||||
|
import Dinero from "dinero.js";
|
||||||
|
import queryString from "query-string";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { Link, useHistory, useLocation } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
|
import {
|
||||||
|
selectAuthLevel,
|
||||||
|
selectBodyshop,
|
||||||
|
} from "../../redux/user/user.selectors";
|
||||||
|
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
|
import { onlyUnique } from "../../utils/arrayHelper";
|
||||||
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
|
import TtApproveButtonComponent from "../tt-approve-button/tt-approve-button.component";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
authLevel: selectAuthLevel,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
setTimeTicketTaskContext: (context) =>
|
||||||
|
dispatch(setModalContext({ context: context, modal: "timeTicketTask" })),
|
||||||
|
});
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(TtApprovalsListComponent);
|
||||||
|
|
||||||
|
export function TtApprovalsListComponent({
|
||||||
|
bodyshop,
|
||||||
|
setTimeTicketTaskContext,
|
||||||
|
authLevel,
|
||||||
|
disabled,
|
||||||
|
loading,
|
||||||
|
tt_approval_queue,
|
||||||
|
total,
|
||||||
|
refetch,
|
||||||
|
techConsole,
|
||||||
|
jobId,
|
||||||
|
extra,
|
||||||
|
}) {
|
||||||
|
const [state, setState] = useState({
|
||||||
|
sortedInfo: {},
|
||||||
|
filteredInfo: { text: "" },
|
||||||
|
});
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const history = useHistory();
|
||||||
|
const search = queryString.parse(useLocation().search);
|
||||||
|
const { page } = search;
|
||||||
|
const [selectedTickets, setSelectedTickets] = useState([]);
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t("timetickets.fields.date"),
|
||||||
|
dataIndex: "date",
|
||||||
|
key: "date",
|
||||||
|
sorter: (a, b) => dateSort(a.date, b.date),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "date" && state.sortedInfo.order,
|
||||||
|
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("timetickets.fields.employee"),
|
||||||
|
dataIndex: "employeeid",
|
||||||
|
key: "employeeid",
|
||||||
|
sorter: (a, b) => alphaSort(a.employee.last_name, b.employee.last_name),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "employee" && state.sortedInfo.order,
|
||||||
|
render: (text, record) =>
|
||||||
|
`${record.employee.first_name} ${record.employee.last_name}`,
|
||||||
|
filters:
|
||||||
|
tt_approval_queue
|
||||||
|
.map((l) => l.employeeid)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: (() => {
|
||||||
|
const emp = bodyshop.employees.find((e) => e.id === s);
|
||||||
|
|
||||||
|
return `${emp?.first_name} ${emp?.last_name}`;
|
||||||
|
})(), //
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
}) || [],
|
||||||
|
onFilter: (value, record) => value.includes(record.employeeid),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("timetickets.fields.cost_center"),
|
||||||
|
dataIndex: "cost_center",
|
||||||
|
key: "cost_center",
|
||||||
|
sorter: (a, b) => alphaSort(a.cost_center, b.cost_center),
|
||||||
|
render: (text, record) =>
|
||||||
|
record.cost_center === "timetickets.labels.shift"
|
||||||
|
? t(record.cost_center)
|
||||||
|
: record.cost_center,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "cost_center" && state.sortedInfo.order,
|
||||||
|
filters:
|
||||||
|
tt_approval_queue
|
||||||
|
.map((l) => l.cost_center)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s === "timetickets.labels.shift" ? t(s) : s, //|| "No Status*",
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
}) || [],
|
||||||
|
onFilter: (value, record) => value.includes(record.cost_center),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.ro_number"),
|
||||||
|
dataIndex: "ro_number",
|
||||||
|
key: "ro_number",
|
||||||
|
sorter: (a, b) =>
|
||||||
|
alphaSort(a.job && a.job.ro_number, b.job && b.job.ro_number),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||||
|
render: (text, record) =>
|
||||||
|
record.job && (
|
||||||
|
<Link to={"/manage/jobs/" + record.job.id}>
|
||||||
|
<Space wrap>
|
||||||
|
{record.job.ro_number || "N/A"}
|
||||||
|
<Tag>{record.job.status}</Tag>
|
||||||
|
</Space>
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("timetickets.fields.productivehrs"),
|
||||||
|
dataIndex: "productivehrs",
|
||||||
|
key: "productivehrs",
|
||||||
|
sorter: (a, b) => a.productivehrs - b.productivehrs,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "productivehrs" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("timetickets.fields.actualhrs"),
|
||||||
|
dataIndex: "actualhrs",
|
||||||
|
key: "actualhrs",
|
||||||
|
sorter: (a, b) => a.actualhrs - b.actualhrs,
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "actualhrs" && state.sortedInfo.order,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("timetickets.fields.memo"),
|
||||||
|
dataIndex: "memo",
|
||||||
|
key: "memo",
|
||||||
|
sorter: (a, b) => alphaSort(a.memo, b.memo),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "memo" && state.sortedInfo.order,
|
||||||
|
render: (text, record) =>
|
||||||
|
record.clockon || record.clockoff ? t(record.memo) : record.memo,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("timetickets.fields.clockon"),
|
||||||
|
dataIndex: "clockon",
|
||||||
|
key: "clockon",
|
||||||
|
|
||||||
|
render: (text, record) => (
|
||||||
|
<DateTimeFormatter>{record.clockon}</DateTimeFormatter>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Pay",
|
||||||
|
dataIndex: "pay",
|
||||||
|
key: "pay",
|
||||||
|
render: (text, record) =>
|
||||||
|
Dinero({ amount: Math.round(record.rate * 100) })
|
||||||
|
.multiply(record.flat_rate ? record.productivehrs : record.actualhrs)
|
||||||
|
.toFormat("$0.00"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
|
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||||
|
search.page = pagination.current;
|
||||||
|
if (sorter && sorter.column && sorter.column.sortObject) {
|
||||||
|
search.searchObj = JSON.stringify(sorter.column.sortObject(sorter.order));
|
||||||
|
} else {
|
||||||
|
delete search.searchObj;
|
||||||
|
search.sortcolumn = sorter.order ? sorter.columnKey : null;
|
||||||
|
search.sortorder = sorter.order;
|
||||||
|
}
|
||||||
|
|
||||||
|
search.sort = JSON.stringify({ [sorter.columnKey]: sorter.order });
|
||||||
|
history.push({ search: queryString.stringify(search) });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
title={t("timetickets.labels.timetickets")}
|
||||||
|
extra={
|
||||||
|
<Space wrap>
|
||||||
|
{extra}
|
||||||
|
<TtApproveButtonComponent
|
||||||
|
selectedTickets={selectedTickets}
|
||||||
|
disabled={selectedTickets.length === 0}
|
||||||
|
completedCallback={setSelectedTickets}
|
||||||
|
refetch={refetch}
|
||||||
|
/>
|
||||||
|
<Button onClick={() => refetch()}>
|
||||||
|
<SyncOutlined />
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Table
|
||||||
|
loading={loading}
|
||||||
|
columns={columns}
|
||||||
|
rowKey="id"
|
||||||
|
scroll={{
|
||||||
|
x: true,
|
||||||
|
}}
|
||||||
|
pagination={{
|
||||||
|
position: "top",
|
||||||
|
pageSize: 25,
|
||||||
|
current: parseInt(page || 1),
|
||||||
|
total: total,
|
||||||
|
}}
|
||||||
|
dataSource={tt_approval_queue}
|
||||||
|
onChange={handleTableChange}
|
||||||
|
rowSelection={{
|
||||||
|
onSelectAll: (selected, selectedRows) =>
|
||||||
|
setSelectedTickets(selectedRows.map((i) => i.id)),
|
||||||
|
onSelect: (record, selected, selectedRows, nativeEvent) => {
|
||||||
|
setSelectedTickets(selectedRows.map((i) => i.id));
|
||||||
|
},
|
||||||
|
selectedRowKeys: selectedTickets,
|
||||||
|
type: "checkbox",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import { useQuery } from "@apollo/client";
|
||||||
|
import queryString from "query-string";
|
||||||
|
import React from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { QUERY_ALL_TT_APPROVALS_PAGINATED } from "../../graphql/tt-approvals.queries";
|
||||||
|
import {
|
||||||
|
setBreadcrumbs,
|
||||||
|
setSelectedHeader,
|
||||||
|
} from "../../redux/application/application.actions";
|
||||||
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
import TtApprovalsListComponent from "./tt-approvals-list.component";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||||
|
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export function TimeTicketsContainer({
|
||||||
|
bodyshop,
|
||||||
|
setBreadcrumbs,
|
||||||
|
setSelectedHeader,
|
||||||
|
}) {
|
||||||
|
const searchParams = queryString.parse(useLocation().search);
|
||||||
|
const { page, sortcolumn, sortorder, search, searchObj } = searchParams;
|
||||||
|
|
||||||
|
const { loading, error, data, refetch } = useQuery(
|
||||||
|
QUERY_ALL_TT_APPROVALS_PAGINATED,
|
||||||
|
{
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
nextFetchPolicy: "network-only",
|
||||||
|
variables: {
|
||||||
|
search: search || "",
|
||||||
|
offset: page ? (page - 1) * 25 : 0,
|
||||||
|
limit: 25,
|
||||||
|
order: [
|
||||||
|
searchObj
|
||||||
|
? JSON.parse(searchObj)
|
||||||
|
: {
|
||||||
|
[sortcolumn || "date"]: sortorder
|
||||||
|
? sortorder === "descend"
|
||||||
|
? "desc"
|
||||||
|
: "asc"
|
||||||
|
: "desc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TtApprovalsListComponent
|
||||||
|
loading={loading}
|
||||||
|
tt_approval_queue={data ? data.tt_approval_queue : []}
|
||||||
|
total={data ? data.tt_approval_queue_aggregate.aggregate.count : 0}
|
||||||
|
refetch={refetch}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(TimeTicketsContainer);
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import { useApolloClient } from "@apollo/client";
|
||||||
|
import { Button, notification } from "antd";
|
||||||
|
import _ from "lodash";
|
||||||
|
import moment from "moment";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { INSERT_TIME_TICKET_AND_APPROVE } from "../../graphql/timetickets.queries";
|
||||||
|
import { QUERY_TT_APPROVALS_BY_IDS } from "../../graphql/tt-approvals.queries";
|
||||||
|
import {
|
||||||
|
selectAuthLevel,
|
||||||
|
selectBodyshop,
|
||||||
|
selectCurrentUser,
|
||||||
|
} from "../../redux/user/user.selectors";
|
||||||
|
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
currentUser: selectCurrentUser,
|
||||||
|
authLevel: selectAuthLevel,
|
||||||
|
});
|
||||||
|
|
||||||
|
export function TtApproveButton({
|
||||||
|
bodyshop,
|
||||||
|
currentUser,
|
||||||
|
selectedTickets,
|
||||||
|
disabled,
|
||||||
|
authLevel,
|
||||||
|
loadingCallback,
|
||||||
|
completedCallback,
|
||||||
|
refetch,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const client = useApolloClient();
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const handleQbxml = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const { data } = await client.query({
|
||||||
|
query: QUERY_TT_APPROVALS_BY_IDS,
|
||||||
|
variables: { ids: selectedTickets },
|
||||||
|
});
|
||||||
|
|
||||||
|
const insertResponse = await client.mutate({
|
||||||
|
mutation: INSERT_TIME_TICKET_AND_APPROVE,
|
||||||
|
variables: {
|
||||||
|
timeTicketInput: data.tt_approval_queue.map((tta) => ({
|
||||||
|
..._.omit(tta, ["id", "__typename"]),
|
||||||
|
ttapprovalqueueid: tta.id,
|
||||||
|
})),
|
||||||
|
approvalIds: selectedTickets,
|
||||||
|
approvalUpdate: {
|
||||||
|
approved_at: moment(),
|
||||||
|
approved_by: currentUser.email,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (insertResponse.errors) {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("timetickets.errors.creating", {
|
||||||
|
message: JSON.stringify(insertResponse.errors),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
notification.open({
|
||||||
|
type: "success",
|
||||||
|
message: t("timetickets.successes.created"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("timetickets.errors.creating", {
|
||||||
|
message: error.message,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (!!completedCallback) completedCallback([]);
|
||||||
|
// if (!!loadingCallback) loadingCallback(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
onClick={handleQbxml}
|
||||||
|
loading={loading}
|
||||||
|
disabled={
|
||||||
|
disabled ||
|
||||||
|
!HasRbacAccess({ bodyshop, authLevel, action: "ttapprovals:approve" })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t("tt_approvals.actions.approveselected")}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, null)(TtApproveButton);
|
||||||
@@ -55,7 +55,7 @@ export function UserRequestResetPw({
|
|||||||
return (
|
return (
|
||||||
<div className="login-container">
|
<div className="login-container">
|
||||||
<div className="login-logo-container">
|
<div className="login-logo-container">
|
||||||
<img src={ImEXOnlineLogo} height="100" width="100" alt="ImEX Online" />
|
<img src={ImEXOnlineLogo} height="100" width="100" alt="Rome Online" />
|
||||||
<Typography.Title>{t("titles.app")}</Typography.Title>
|
<Typography.Title>{t("titles.app")}</Typography.Title>
|
||||||
</div>
|
</div>
|
||||||
<Typography.Title level={3}>{t("titles.resetpassword")}</Typography.Title>
|
<Typography.Title level={3}>{t("titles.resetpassword")}</Typography.Title>
|
||||||
|
|||||||
@@ -25,11 +25,7 @@ export const QUERY_ALL_BILLS_PAGINATED = gql`
|
|||||||
$limit: Int
|
$limit: Int
|
||||||
$order: [bills_order_by!]!
|
$order: [bills_order_by!]!
|
||||||
) {
|
) {
|
||||||
bills(
|
bills(offset: $offset, limit: $limit, order_by: $order) {
|
||||||
offset: $offset
|
|
||||||
limit: $limit
|
|
||||||
order_by: $order
|
|
||||||
) {
|
|
||||||
id
|
id
|
||||||
vendorid
|
vendorid
|
||||||
vendor {
|
vendor {
|
||||||
@@ -98,6 +94,23 @@ export const QUERY_BILLS_BY_JOBID = gql`
|
|||||||
comments
|
comments
|
||||||
user_email
|
user_email
|
||||||
}
|
}
|
||||||
|
parts_dispatch(where: { jobid: { _eq: $jobid } }) {
|
||||||
|
id
|
||||||
|
dispatched_at
|
||||||
|
dispatched_by
|
||||||
|
employeeid
|
||||||
|
number
|
||||||
|
parts_dispatch_lines {
|
||||||
|
joblineid
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
accepted_at
|
||||||
|
jobline {
|
||||||
|
id
|
||||||
|
line_desc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
bills(where: { jobid: { _eq: $jobid } }, order_by: { date: desc }) {
|
bills(where: { jobid: { _eq: $jobid } }, order_by: { date: desc }) {
|
||||||
id
|
id
|
||||||
vendorid
|
vendorid
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user