137 Commits

Author SHA1 Message Date
github-actions[bot]
a17c2a52ce 🔄 Update image to 161 [CI SKIP] 2026-05-05 13:55:08 +00:00
e5df804b08 Merge pull request 'feat: wire contract PDF context and align MechanicAuto with AutoEvaluation' (#141) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 7m59s
Reviewed-on: #141
2026-05-05 13:52:46 +00:00
xoliqberdiyev
80a1f5ff17 feat: wire contract PDF context and align MechanicAuto with AutoEvaluation
- contract PDF: map report/customer/owner/contract from AutoEvaluationModel
  fields, accept inspection via POST serializer, fetch CBU.uz currency rates
- MechanicAutoEvaluation: add distance_covered, object_owner_residence and
  car_position/body_type/fuel_type/state_car/assessment_task_type FKs; drop
  car_type and single tex_passport_file in favour of multi-file FK model

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 18:51:24 +05:00
github-actions[bot]
42f088f8f4 🔄 Update image to 160 [CI SKIP] 2026-05-05 12:50:33 +00:00
cb98fbdc7e Merge pull request 'fix: allow all roles except 'user' on tech-passport endpoint' (#140) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m57s
Reviewed-on: #140
2026-05-05 12:48:03 +00:00
github-actions[bot]
30792dd0b9 🔄 Update image to 159 [CI SKIP] 2026-05-05 12:47:45 +00:00
xoliqberdiyev
25e92623fd fix: allow all roles except 'user' on tech-passport endpoint
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 17:47:39 +05:00
27d1d70010 Merge pull request 'feat: add assessment_task_type to ReferenceType choices' (#139) from behruz into main
Some checks failed
Deploy to Production / build-and-deploy (push) Has been cancelled
Reviewed-on: #139
2026-05-05 12:45:24 +00:00
xoliqberdiyev
cb795fe3b8 feat: add assessment_task_type to ReferenceType choices
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 17:44:56 +05:00
github-actions[bot]
64da9f20d3 🔄 Update image to 158 [CI SKIP] 2026-05-05 12:10:06 +00:00
075188e85d Merge pull request 'add new fields for auto-evalution model' (#138) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m54s
Reviewed-on: #138
2026-05-05 12:07:47 +00:00
xoliqberdiyev
9a730fa0a3 add new fields for auto-evalution model 2026-05-05 17:07:28 +05:00
github-actions[bot]
91bdeb4b67 🔄 Update image to 157 [CI SKIP] 2026-05-05 11:59:57 +00:00
91073731dc Merge pull request 'change' (#137) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 3m1s
Reviewed-on: #137
2026-05-05 11:57:38 +00:00
xoliqberdiyev
e665939b1b change 2026-05-05 16:57:08 +05:00
github-actions[bot]
ee877c0fc6 🔄 Update image to 156 [CI SKIP] 2026-05-05 11:56:32 +00:00
ccb4bd8176 Merge pull request 'feat: add mechnic-auto-model' (#136) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m55s
Reviewed-on: #136
2026-05-05 11:54:11 +00:00
xoliqberdiyev
928561be51 feat: add mechnic-auto-model 2026-05-05 16:53:49 +05:00
github-actions[bot]
84f0df3f97 🔄 Update image to 155 [CI SKIP] 2026-05-05 11:38:10 +00:00
704d3b9e6b Merge pull request 'feat: add new model for comment files' (#135) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m49s
Reviewed-on: #135
2026-05-05 11:35:53 +00:00
xoliqberdiyev
9dcce296a6 a lot of changds 2026-05-05 16:35:41 +05:00
xoliqberdiyev
4ac21100a3 feat: add new model for comment files 2026-05-05 16:32:22 +05:00
github-actions[bot]
b0d5a2f334 🔄 Update image to 154 [CI SKIP] 2026-05-05 11:26:19 +00:00
ba3d6c4e47 Merge pull request 'behruz' (#134) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 5m12s
Reviewed-on: #134
2026-05-05 11:23:52 +00:00
f3834fad70 Merge pull request 'update' (#133) from shaxob into main
Some checks failed
Deploy to Production / build-and-deploy (push) Has been cancelled
Reviewed-on: #133
2026-05-05 11:23:35 +00:00
xoliqberdiyev
269817f25e write generate pdf api 2026-05-05 16:23:08 +05:00
xoliqberdiyev
8158d7146e a lot of changes 2026-05-05 14:37:37 +05:00
Shaxobff
617bed99ae update 2026-05-05 00:18:41 +05:00
github-actions[bot]
9b782fe7bd 🔄 Update image to 152 [CI SKIP] 2026-05-04 12:51:51 +00:00
135f580db2 Merge pull request 'shaxob' (#132) from shaxob into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m23s
Reviewed-on: #132
2026-05-04 12:49:58 +00:00
Shaxobff
0c622759cc update 2026-05-04 17:41:50 +05:00
github-actions[bot]
1d750b1c1c 🔄 Update image to 151 [CI SKIP] 2026-05-04 12:33:34 +00:00
2b26c52a5c Merge pull request 'behruz' (#131) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m20s
Reviewed-on: #131
2026-05-04 12:31:44 +00:00
xoliqberdiyev
51b3535a80 write 2026-05-04 17:29:20 +05:00
Shaxobff
9028e2f102 Merge branch 'main' of https://gitea.felixits.uz/sifatbaho/backend-v1 into shaxob 2026-05-04 16:17:12 +05:00
Shaxobff
0c9c726756 add generation_pdf pdf , fix 500 error , install reportlab 2026-05-04 16:15:12 +05:00
xoliqberdiyev
c88ea1aa77 Merge branch 'main' of https://gitea.felixits.uz/sifatbaho/backend-v1 into behruz 2026-05-04 14:47:05 +05:00
github-actions[bot]
581021cbb7 🔄 Update image to 150 [CI SKIP] 2026-05-04 05:38:53 +00:00
62f65385e1 fix: DB_HOST=postgres (stack.yaml service nomi bilan mos)
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m13s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 10:37:11 +05:00
github-actions[bot]
76d2fe5090 🔄 Update image to 149 [CI SKIP] 2026-05-04 05:14:11 +00:00
92d23901a1 ci cd uchun test commit
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 4m28s
2026-05-04 10:12:19 +05:00
github-actions[bot]
42987e4154 🔄 Update image to 148 [CI SKIP] 2026-05-04 04:50:59 +00:00
84b14da3f4 ci cd uchun test commit
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m10s
2026-05-04 09:49:08 +05:00
Shaxobff
1ff23af8bf update 2026-05-01 17:15:01 +05:00
Shaxobff
feecb580c1 update 2026-05-01 16:54:38 +05:00
xoliqberdiyev
cb53924f9b change 2026-04-30 16:33:00 +05:00
github-actions[bot]
f53125cfdc 🔄 Update image to 147 [CI SKIP] 2026-04-30 11:05:42 +00:00
65ab51e652 Merge pull request 'update' (#128) from shaxob into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m11s
Reviewed-on: #128
2026-04-30 11:03:51 +00:00
2997810fae Merge pull request 'behruz' (#129) from behruz into main
Some checks failed
Deploy to Production / build-and-deploy (push) Has been cancelled
Reviewed-on: #129
2026-04-30 11:03:44 +00:00
xoliqberdiyev
d014f5a2fb fix 2026-04-30 16:02:04 +05:00
xoliqberdiyev
7d49929772 Merge branch 'main' of https://gitea.felixits.uz/sifatbaho/backend-v1 into behruz 2026-04-30 15:22:18 +05:00
Shaxobff
c29546a04b update 2026-04-30 11:11:12 +05:00
xoliqberdiyev
b39c080de3 add tasks app 2026-04-29 18:40:51 +05:00
github-actions[bot]
7ad385af94 🔄 Update image to 145 [CI SKIP] 2026-04-29 12:30:39 +00:00
3781ce29e5 Merge pull request 'shaxob' (#127) from shaxob into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m19s
Reviewed-on: #127
2026-04-29 12:28:42 +00:00
Shaxobff
db7e34c1c2 update 2026-04-29 16:12:12 +05:00
Shaxobff
1cb9551e81 update 2026-04-29 14:21:33 +05:00
Shaxobff
51b30c2cc4 update 2026-04-29 11:57:23 +05:00
Shaxobff
dc4c98bfc9 update 2026-04-29 11:18:50 +05:00
github-actions[bot]
abed9e59b4 🔄 Update image to 144 [CI SKIP] 2026-04-28 13:41:10 +00:00
f238c92a09 Merge pull request 'fix' (#125) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m11s
Reviewed-on: #125
2026-04-28 13:32:46 +00:00
xoliqberdiyev
113f2da120 fix 2026-04-28 18:32:24 +05:00
github-actions[bot]
99b265f68f 🔄 Update image to 143 [CI SKIP] 2026-04-28 13:19:46 +00:00
c5d60e799c Merge pull request 'fix' (#124) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m17s
Reviewed-on: #124
2026-04-28 13:17:52 +00:00
xoliqberdiyev
7829c9c625 fix 2026-04-28 18:17:21 +05:00
github-actions[bot]
7f462674a8 🔄 Update image to 142 [CI SKIP] 2026-04-28 13:05:16 +00:00
f7be3be5d2 Merge pull request 'behruz' (#123) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m12s
Reviewed-on: #123
2026-04-28 13:03:29 +00:00
xoliqberdiyev
557f9f821d Merge branch 'main' of https://gitea.felixits.uz/sifatbaho/backend-v1 into behruz 2026-04-28 18:03:07 +05:00
xoliqberdiyev
5f70d69896 fux 2026-04-28 18:02:39 +05:00
github-actions[bot]
4ea7070a8f 🔄 Update image to 141 [CI SKIP] 2026-04-28 12:50:39 +00:00
8b02f3a3a3 Merge pull request 'add new admin user delete api' (#122) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m12s
Reviewed-on: #122
2026-04-28 12:48:46 +00:00
xoliqberdiyev
f0d93b10ac Merge branch 'main' of https://gitea.felixits.uz/sifatbaho/backend-v1 into behruz 2026-04-28 17:48:03 +05:00
xoliqberdiyev
172ddf4da4 add new admin user delete api 2026-04-28 17:47:55 +05:00
435dd56334 Merge pull request 'fix error' (#121) from behruz into main
Some checks failed
Deploy to Production / build-and-deploy (push) Has been cancelled
Reviewed-on: #121
2026-04-28 12:40:16 +00:00
xoliqberdiyev
779c9db924 fix error 2026-04-28 17:39:55 +05:00
eaaba123b0 Merge pull request 'add new fields to request-evalution api' (#120) from behruz into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 53s
Reviewed-on: #120
2026-04-28 12:30:43 +00:00
xoliqberdiyev
63c4ad81eb add new fields to request-evalution api 2026-04-28 17:30:11 +05:00
github-actions[bot]
d065891ad5 🔄 Update image to 138 [CI SKIP] 2026-04-28 11:11:54 +00:00
94c4d03925 Merge pull request 'remove unused fields from auto-evalution model' (#119) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m10s
Reviewed-on: #119
2026-04-28 11:10:06 +00:00
xoliqberdiyev
4a958f064b remove unused fields from auto-evalution model 2026-04-28 16:09:25 +05:00
github-actions[bot]
d1f0a5a9ae 🔄 Update image to 137 [CI SKIP] 2026-04-28 11:07:21 +00:00
0084d11c62 Merge pull request 'change response of permissions apis' (#118) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 4m25s
Reviewed-on: #118
2026-04-28 11:05:35 +00:00
xoliqberdiyev
d1340cdd52 change response of permissions apis 2026-04-28 16:04:59 +05:00
github-actions[bot]
d7ea1acba6 🔄 Update image to 136 [CI SKIP] 2026-04-27 12:57:34 +00:00
560cbe8000 Merge pull request 'fix certificate create api' (#117) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m11s
Reviewed-on: #117
2026-04-27 12:55:46 +00:00
xoliqberdiyev
37d6a93529 fix certificate create api 2026-04-27 17:55:21 +05:00
github-actions[bot]
e1b445d515 🔄 Update image to 135 [CI SKIP] 2026-04-27 12:49:45 +00:00
ef87112c79 Merge pull request 'behruz' (#116) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m8s
Reviewed-on: #116
2026-04-27 12:47:57 +00:00
xoliqberdiyev
8c01c1dc2d Merge branch 'main' of https://gitea.felixits.uz/sifatbaho/backend-v1 into behruz 2026-04-27 17:47:33 +05:00
xoliqberdiyev
921b54ab7c change url 2026-04-27 17:47:20 +05:00
github-actions[bot]
a74c348187 🔄 Update image to 134 [CI SKIP] 2026-04-27 12:44:14 +00:00
52fab30588 Merge pull request 'fix 500 error' (#115) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m8s
Reviewed-on: #115
2026-04-27 12:42:29 +00:00
xoliqberdiyev
0de50ec328 fix 500 error 2026-04-27 17:41:13 +05:00
github-actions[bot]
e346546d24 🔄 Update image to 133 [CI SKIP] 2026-04-27 12:09:19 +00:00
e97c6c7ab2 Merge pull request 'update deploy.yaml file' (#114) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 4m28s
Reviewed-on: #114
2026-04-27 12:07:23 +00:00
github-actions[bot]
f7706e77ee 🔄 Update image to 132 [CI SKIP] 2026-04-27 12:07:15 +00:00
xoliqberdiyev
e351ed5303 update deploy.yaml file 2026-04-27 17:06:43 +05:00
affd3e1221 Merge pull request 'add filter to archive apis' (#113) from behruz into main
Some checks failed
Deploy to Production / build-and-deploy (push) Has been cancelled
Reviewed-on: #113
2026-04-27 12:05:24 +00:00
xoliqberdiyev
59ed3d23ac add filter to archive apis 2026-04-27 17:05:08 +05:00
github-actions[bot]
3ac6263035 🔄 Update image to 131 [CI SKIP] 2026-04-27 12:01:46 +00:00
2c6d7dd2f7 Merge pull request 'fix 500 error' (#112) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m24s
Reviewed-on: #112
2026-04-27 11:59:43 +00:00
xoliqberdiyev
a6e0fca165 fix 500 error 2026-04-27 16:59:21 +05:00
github-actions[bot]
b64073e1ad 🔄 Update image to 130 [CI SKIP] 2026-04-27 11:54:31 +00:00
e3ffdddc46 Merge pull request 'resolve the error' (#111) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m8s
Reviewed-on: #111
2026-04-27 11:52:47 +00:00
xoliqberdiyev
6f48632e2d resolve the error 2026-04-27 16:52:30 +05:00
faea9bdb89 Merge pull request 'changing code structure' (#110) from behruz into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 56s
Reviewed-on: #110
2026-04-27 11:49:40 +00:00
xoliqberdiyev
e3e7f18d7f changing code structure 2026-04-27 16:49:07 +05:00
github-actions[bot]
1dd1a132e4 🔄 Update image to 128 [CI SKIP] 2026-04-27 09:54:08 +00:00
04e193bae6 Merge pull request 'add new api' (#109) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m7s
Reviewed-on: #109
2026-04-27 09:52:24 +00:00
xoliqberdiyev
7134b2c185 add new api 2026-04-27 14:50:59 +05:00
github-actions[bot]
306aecc956 🔄 Update image to 127 [CI SKIP] 2026-04-27 09:47:57 +00:00
3ede209e52 Merge pull request 'update' (#108) from shaxob into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 4m21s
Reviewed-on: #108
2026-04-27 09:46:13 +00:00
Shaxobff
05857a227a update 2026-04-27 14:45:32 +05:00
10b25b5228 Merge pull request 'update' (#107) from shaxob into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 3m43s
Reviewed-on: #107
2026-04-27 09:39:46 +00:00
Shaxobff
fcbfa94dd4 update 2026-04-27 14:38:55 +05:00
7e778d3a3e Merge pull request 'shaxob' (#106) from shaxob into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 10m36s
Reviewed-on: #106
2026-04-27 09:02:37 +00:00
Shaxobff
81a4287db1 update 2026-04-27 11:57:27 +05:00
Shaxobff
e560fdaf2d fix bug 2026-04-27 11:28:18 +05:00
Shaxobff
0d96167a7b fix bug 2026-04-27 10:58:02 +05:00
Shaxobff
ae65d9d793 resolve migrations conflict 2026-04-25 12:52:15 +05:00
Shaxobff
5249f7e6f7 my migrations 2026-04-25 12:36:33 +05:00
Shaxobff
e1b771e166 app permission api 20 (api) 2026-04-25 12:32:56 +05:00
github-actions[bot]
eded642bd7 🔄 Update image to 124 [CI SKIP] 2026-04-24 13:57:50 +00:00
f830235813 Merge pull request 'fix' (#105) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m15s
Reviewed-on: #105
2026-04-24 13:55:58 +00:00
xoliqberdiyev
a62cf3a1ee fix 2026-04-24 18:55:42 +05:00
github-actions[bot]
2f471173c3 🔄 Update image to 123 [CI SKIP] 2026-04-24 13:52:45 +00:00
3838fbaa47 Merge pull request 'change ci/cd folder' (#104) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m16s
Reviewed-on: #104
2026-04-24 13:50:51 +00:00
xoliqberdiyev
b02078e618 cha 2026-04-24 18:49:34 +05:00
xoliqberdiyev
1f59347d87 fix 2026-04-24 18:42:11 +05:00
github-actions[bot]
fe40057d95 🔄 Update image to 122 [CI SKIP] 2026-04-24 13:40:38 +00:00
550da049b9 Merge pull request 'fix' (#103) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m17s
Reviewed-on: #103
2026-04-24 13:38:42 +00:00
xoliqberdiyev
bdd5aa9ce2 fix 2026-04-24 18:36:39 +05:00
github-actions[bot]
bd27205252 🔄 Update image to 121 [CI SKIP] 2026-04-24 13:34:27 +00:00
34aba90ebd Merge pull request 'behruz' (#102) from behruz into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 2m6s
Reviewed-on: #102
2026-04-24 13:32:26 +00:00
xoliqberdiyev
260bc9101e Merge branch 'main' of https://gitea.felixits.uz/sifatbaho/backend-v1 into behruz 2026-04-24 18:30:52 +05:00
xoliqberdiyev
f46dac515a fix 2026-04-24 18:30:41 +05:00
github-actions[bot]
c6fc150162 🔄 Update image to 120 [CI SKIP] 2026-04-24 12:57:55 +00:00
123 changed files with 7393 additions and 741 deletions

View File

@@ -23,7 +23,7 @@ DB_ENGINE=django.db.backends.postgresql_psycopg2
DB_NAME=django DB_NAME=django
DB_USER=postgres DB_USER=postgres
DB_PASSWORD=2309 DB_PASSWORD=2309
DB_HOST=db DB_HOST=postgres
DB_PORT=5432 DB_PORT=5432
# Cache # Cache
@@ -75,7 +75,3 @@ STORAGE_PROTOCOL=http:
# Didox configs # Didox configs
DIDOX_PARTNER_TOKEN=... DIDOX_PARTNER_TOKEN=...
# Celery configs

View File

@@ -47,6 +47,24 @@ jobs:
- name: Copy env - name: Copy env
run: | run: |
cp .env.example .env cp .env.example .env
update_env() {
local env_file=".env"
for kv in "$@"; do
local key="${kv%%=*}"
local value="${kv#*=}"
if grep -q "^$key=" "$env_file"; then
sed -i "s|^$key=.*|$key=$value|" "$env_file"
else
echo "$key=$value" >> "$env_file"
fi
done
}
update_env \
"DB_HOST=postgres" \
"DB_NAME=sifatbahodb" \
"DB_PORT=5432" \
"DIDOX_PARTNER_TOKEN=${{ secrets.DIDOX_TOKEN }}"
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
@@ -125,6 +143,7 @@ jobs:
rm -rf "$DIR" rm -rf "$DIR"
cd "$PROJECTS" cd "$PROJECTS"
git clone "$REPO_URL" "${{ env.PROJECT_NAME }}" git clone "$REPO_URL" "${{ env.PROJECT_NAME }}"
echo "Clone qilindi"
fi fi
cd "$DIR" cd "$DIR"
@@ -132,18 +151,11 @@ jobs:
git fetch origin main git fetch origin main
git reset --hard origin/main git reset --hard origin/main
cat > .env << 'ENVEOF' if [ ! -f .env ]; then
DB_HOST=postgres cp .env.example .env
DB_NAME=sifatbahodb echo ".env yaratildi, production qiymatlarini kiriting!"
DB_PORT=5432 fi
DB_USER=${{ secrets.DB_USER }}
DB_PASSWORD=${{ secrets.DB_PASSWORD }}
REDIS_URL=redis://redis:6379
SECRET_KEY=${{ secrets.SECRET_KEY }}
DIDOX_TOKEN=${{ secrets.DIDOX_TOKEN }}
DEBUG=False
PORT=8085
ENVEOF
export PORT=8085
docker pull ${{ secrets.DOCKER_USERNAME }}/${{ env.PROJECT_NAME }}:${{ github.run_number }}
docker stack deploy -c stack.yaml ${{ env.PROJECT_NAME }} --with-registry-auth docker stack deploy -c stack.yaml ${{ env.PROJECT_NAME }} --with-registry-auth

View File

@@ -14,6 +14,8 @@ APPS = [
"rest_framework_simplejwt", "rest_framework_simplejwt",
"django_core", "django_core",
"core.apps.accounts.apps.AccountsConfig", "core.apps.accounts.apps.AccountsConfig",
'core.apps.tasks.apps.TasksConfig',
'core.apps.documents.apps.DocumentsConfig',
] ]
if env.bool("SILK_ENABLED", False): if env.bool("SILK_ENABLED", False):

View File

@@ -186,5 +186,57 @@ PAGES = [
"link": reverse_lazy("admin:shared_villagemodel_changelist"), "link": reverse_lazy("admin:shared_villagemodel_changelist"),
}, },
] ]
},
{
"title": _("Ruxsatlar"),
"separator": True,
"items": [
{
"title": _("Ruxsatlar"),
"icon": "attach_file",
"link": reverse_lazy("admin:accounts_permission_changelist"),
},
{
"title": _("Sahifa uchun ruxsatlar"),
"icon": "attach_file",
"link": reverse_lazy("admin:accounts_permissiontotab_changelist"),
},
{
"title": _("Actionlar uchun ruxsatlar"),
"icon": "attach_file",
"link": reverse_lazy("admin:accounts_permissiontoaction_changelist"),
},
{
"title": _("Role"),
"icon": "attach_file",
"link": reverse_lazy("admin:accounts_role_changelist"),
},
]
},
{
"title": _("Task Management"),
"separator": True,
"items": [
{
"title": _("Task"),
"icon": "task",
"link": reverse_lazy("admin:tasks_task_changelist"),
},
{
"title": _("Column"),
"icon": "tag",
"link": reverse_lazy("admin:tasks_column_changelist"),
},
{
"title": _("Comment"),
"icon": "message",
"link": reverse_lazy("admin:tasks_comment_changelist"),
},
{
"title": _("Label"),
"icon": "tag",
"link": reverse_lazy("admin:tasks_label_changelist"),
},
]
} }
] ]

View File

@@ -13,7 +13,7 @@ from config.env import env
def home(request): def home(request):
return HttpResponse("OK: #88dedd85c79ccf732b2adac03616bd14e67a1579") return HttpResponse("OK: #e5df804b080de139b62f6987059cc35229d63bf8")
urlpatterns = [ urlpatterns = [
@@ -23,6 +23,8 @@ urlpatterns = [
path("api/v1/", include("core.apps.evaluation.urls")), path("api/v1/", include("core.apps.evaluation.urls")),
path("api/v1/", include("core.apps.payment.urls")), path("api/v1/", include("core.apps.payment.urls")),
path("api/v1/", include("core.apps.chat.urls")), path("api/v1/", include("core.apps.chat.urls")),
path("api/v1/tasks/", include("core.apps.tasks.urls")),
path("api/v1/documents/", include("core.apps.documents.urls")),
] ]
urlpatterns += [ urlpatterns += [
path("admin/", admin.site.urls), path("admin/", admin.site.urls),

View File

@@ -1,2 +1,3 @@
from .core import * # noqa from .core import * # noqa
from .user import * # noqa from .user import * # noqa
from .permission import *

View File

@@ -0,0 +1,77 @@
from django.contrib import admin
from core.apps.accounts.models.permission import (
PermissionToAction,
PermissionToTab,
Permission,
Role,
)
@admin.register(PermissionToAction)
class PermissionToActionAdmin(admin.ModelAdmin):
list_display = ("id", "name", "code", "created_at")
search_fields = ("name", "code")
fieldsets = (
("Asosiy", {
"fields": ("name", "code"),
}),
)
@admin.register(PermissionToTab)
class PermissionToTabAdmin(admin.ModelAdmin):
list_display = ("id", "name", "code", "created_at")
search_fields = ("name", "code")
filter_horizontal = ("permission_to_actions",)
fieldsets = (
("Asosiy", {
"fields": ("name", "code"),
}),
("Harakatlar", {
"fields": ("permission_to_actions",),
}),
)
@admin.register(Permission)
class PermissionAdmin(admin.ModelAdmin):
list_display = ("id", "name", "code", "created_at")
search_fields = ("name", "code")
filter_horizontal = ("permission_tabs",)
fieldsets = (
("Asosiy", {
"fields": ("name", "code"),
}),
("Boglanishlar", {
"fields": ("permission_tabs",),
}),
)
@admin.register(Role)
class RoleAdmin(admin.ModelAdmin):
list_display = ("id", "name")
search_fields = ("name",)
filter_horizontal = (
"permissions",
"permission_to_tabs",
"permission_to_actions",
)
fieldsets = (
("Asosiy ma'lumotlar", {
"fields": ("name", "comment"),
}),
("Sahifa ruxsatlari", {
"fields": ("permissions",),
}),
("Bolim ruxsatlari", {
"fields": ("permission_to_tabs",),
}),
("Harakat ruxsatlari", {
"fields": ("permission_to_actions",),
}),
)

View File

@@ -1,4 +1,5 @@
from django.contrib.auth import admin from django.contrib.auth import admin
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from unfold.admin import ModelAdmin from unfold.admin import ModelAdmin

View File

@@ -1,4 +1,4 @@
# Generated by Django 5.2.7 on 2026-04-24 12:55 # Generated by Django 5.2.7 on 2026-04-27 09:33
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.7 on 2026-04-28 11:02
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('accounts', '0004_permissiontoaction_permissiontotab_permission_role_and_more'),
]
operations = [
migrations.RenameField(
model_name='permission',
old_name='permission_tab',
new_name='permission_tabs',
),
]

View File

@@ -33,7 +33,7 @@ class PermissionToTab(AbstractBaseModel):
class Permission(AbstractBaseModel): class Permission(AbstractBaseModel):
name = models.CharField(max_length=200) name = models.CharField(max_length=200)
code = models.CharField(max_length=100, unique=True) code = models.CharField(max_length=100, unique=True)
permission_tab = models.ManyToManyField(PermissionToTab, related_name='permissions') permission_tabs = models.ManyToManyField(PermissionToTab, related_name='permissions')
def __str__(self): def __str__(self):
return f'{self.name} - {self.code}' return f'{self.name} - {self.code}'

View File

@@ -0,0 +1,15 @@
from rest_framework.exceptions import PermissionDenied
from rest_framework.permissions import BasePermission
from core.apps.accounts.choices import RoleChoice
class IsAdminRole(BasePermission):
def has_permission(self, request, view):
if not request.user.is_authenticated:
return False
if request.user.role != RoleChoice.ADMIN:
raise PermissionDenied("Only admin can access this")
return True

View File

@@ -1,8 +1,55 @@
from rest_framework import serializers from rest_framework import serializers
from core.apps.accounts.models.permission import PermissionToAction from core.apps.accounts.models.permission import PermissionToAction, PermissionToTab, Permission, Role
class PermissionToActionSerializer(serializers.ModelSerializer):
class Meta:
model = PermissionToAction
fields = ['id', 'name']
class PermissionToTabSerializer(serializers.ModelSerializer):
permission_to_actions = PermissionToActionSerializer(many=True, read_only=True)
class Meta:
model = PermissionToTab
fields = ['id', 'name', 'permission_to_actions']
class PermissionSerializer(serializers.ModelSerializer): class PermissionSerializer(serializers.ModelSerializer):
permission_tabs = PermissionToTabSerializer(many=True, read_only=True)
class Meta:
model = Permission
fields = ['id', 'name', 'permission_tabs']
class PermissionToActionListSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = PermissionToAction model = PermissionToAction
fields = ['id', 'name']
class PermissionToTabListSerializer(serializers.ModelSerializer):
class Meta:
model = PermissionToTab
fields = ['id', 'name']
class PermissionListSerializer(serializers.ModelSerializer):
class Meta:
model = Permission
fields = ['id', 'name']
class RoleListSerializer(serializers.ModelSerializer):
permissions = PermissionListSerializer(many=True)
permission_to_tabs = PermissionToTabListSerializer(many=True)
permission_to_actions = PermissionToActionListSerializer(many=True)
class Meta:
model = Role
fields = [
'id', 'name', 'comment', 'permissions', 'permission_to_tabs', 'permission_to_actions',
]

View File

@@ -54,4 +54,22 @@ class UserCreateSerializer(serializers.ModelSerializer):
"first_name", "first_name",
"last_name", "last_name",
"password", "password",
"role"] "role"
]
class ShortUserSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = [
'id',
'first_name',
'last_name',
'avatar',
]
def get_avatar(self, obj):
request = self.context.get('request')
if obj.avatar:
return request.build_absolute_uri(obj.avatar.url)
return None

View File

@@ -8,7 +8,8 @@ from .views import RegisterView, ResetPasswordView, MeView, ChangePasswordView,
AdminUserView, AdminCreateAPIView, AdminUpdateAPIView AdminUserView, AdminCreateAPIView, AdminUpdateAPIView
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
# from .views.permission import PermissionToActionViewSetMixin from .views.permission import PermissionToActionViewSet, PermissionToTabViewSet, PermissionViewSet, RoleViewSet
from core.apps.accounts.views.user import DeleteAdminUserApiView, UserDetailAPIView
router = DefaultRouter() router = DefaultRouter()
router.register("auth", RegisterView, basename="auth") router.register("auth", RegisterView, basename="auth")
@@ -16,20 +17,21 @@ router.register("auth", ResetPasswordView, basename="reset-password")
router.register("auth", MeView, basename="me") router.register("auth", MeView, basename="me")
router.register("auth", ChangePasswordView, basename="change-password") router.register("auth", ChangePasswordView, basename="change-password")
router.register("user", AdminUserView, basename="user-crud") router.register("user", AdminUserView, basename="user-crud")
# router.register("user", PermissionToActionViewSetMixin, basename="permission") router.register("action", PermissionToActionViewSet, basename="action")
router.register("permission-to-tab", PermissionToTabViewSet, basename="permission-to-tab")
router.register("permission", PermissionViewSet, basename="permission")
router.register("permission-role", RoleViewSet, basename="permission-role")
urlpatterns = [ urlpatterns = [
path("", include(router.urls)), path("", include(router.urls)),
path("auth/token/", jwt_views.TokenObtainPairView.as_view(), name="token_obtain_pair"), path("auth/token/", jwt_views.TokenObtainPairView.as_view(), name="token_obtain_pair"),
path("auth/token/verify/", jwt_views.TokenVerifyView.as_view(), name="token_verify"), path("auth/token/verify/", jwt_views.TokenVerifyView.as_view(), name="token_verify"),
path( path("auth/token/refresh/", jwt_views.TokenRefreshView.as_view()),
"auth/token/refresh/",
jwt_views.TokenRefreshView.as_view(),
name="token_refresh",
),
path("user/list/", UserListApiView.as_view(), name="user-list"), path("user/list/", UserListApiView.as_view(), name="user-list"),
path("admin-user/list/", AdminUserListApiView.as_view(), name="admin-user-list"), path("admin-user/list/", AdminUserListApiView.as_view(), name="admin-user-list"),
path("admin/create/", AdminCreateAPIView.as_view(), name="user-create"), path("admin/create/", AdminCreateAPIView.as_view(), name="user-create"),
path("admin/update/", AdminUpdateAPIView.as_view(), name="user-update"), path("admin/update/<int:pk>/", AdminUpdateAPIView.as_view(), name="user-update"),
path('user/admin/<int:pk>/delete/', DeleteAdminUserApiView.as_view(), name='user-delete'),
path('user/<int:pk>/', UserDetailAPIView.as_view(), name='user-detail'),
] ]

View File

@@ -1,18 +1,41 @@
from django_core.mixins import BaseViewSetMixin from django_core.mixins import BaseViewSetMixin
from rest_framework.permissions import AllowAny from drf_spectacular.utils import extend_schema
from rest_framework.permissions import AllowAny, IsAdminUser
from rest_framework.viewsets import ModelViewSet
from core.apps.accounts.models.permission import PermissionToAction from core.apps.accounts.models.permission import PermissionToAction, PermissionToTab, Permission, Role
from core.apps.accounts.serializers.permission import PermissionSerializer from core.apps.accounts.serializers.permission import PermissionToActionSerializer, PermissionToTabSerializer, \
PermissionSerializer, RoleListSerializer
# class PermissionToActionViewSetMixin(BaseViewSetMixin): @extend_schema(tags=["permission"])
# queryset = PermissionToAction.objects.all() class PermissionToActionViewSet(BaseViewSetMixin, ModelViewSet):
# permission_classes = [AllowAny] queryset = PermissionToAction.objects.all()
# serializer_class = PermissionSerializer serializer_class = PermissionToActionSerializer
#
# action_permission_classes = {} action_serializer_class = {
# action_serializer_class = { 'create': PermissionToActionSerializer,
# "list": PermissionSerializer, 'update': PermissionToActionSerializer,
# "retrieve": PermissionSerializer, }
# "create": PermissionSerializer,
# } action_permission_classes = {
'create': [AllowAny],
'destroy': [IsAdminUser],
}
@extend_schema(tags=["permission"])
class PermissionToTabViewSet(BaseViewSetMixin, ModelViewSet):
queryset = PermissionToTab.objects.all()
serializer_class = PermissionToTabSerializer
@extend_schema(tags=["permission"])
class PermissionViewSet(BaseViewSetMixin, ModelViewSet):
queryset = Permission.objects.all()
serializer_class = PermissionSerializer
class RoleViewSet(BaseViewSetMixin, ModelViewSet):
queryset = Role.objects.all()
serializer_class = RoleListSerializer

View File

@@ -9,6 +9,8 @@ from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from core.apps.accounts.choices.user import RoleChoice from core.apps.accounts.choices.user import RoleChoice
from core.apps.accounts.models import Role
from core.apps.accounts.serializers.permission import RoleListSerializer
from core.apps.accounts.serializers.user import UserSerializer, AdminUserSerializer, UserCreateSerializer from core.apps.accounts.serializers.user import UserSerializer, AdminUserSerializer, UserCreateSerializer
User = get_user_model() User = get_user_model()
@@ -64,11 +66,10 @@ class AdminCreateAPIView(APIView):
return Response(serializer.data, status=201) return Response(serializer.data, status=201)
@extend_schema(tags=['User'], @extend_schema(tags=['User'], )
responses={200: UserSerializer}, class AdminUpdateAPIView(generics.GenericAPIView):
request=UserCreateSerializer)
class AdminUpdateAPIView(APIView):
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
serializer_class = UserCreateSerializer
def put(self, request, pk): def put(self, request, pk):
if request.user.role not in (RoleChoice.SUPERUSER, RoleChoice.ADMIN): if request.user.role not in (RoleChoice.SUPERUSER, RoleChoice.ADMIN):
@@ -80,3 +81,32 @@ class AdminUpdateAPIView(APIView):
serializer.save() serializer.save()
return Response(serializer.data, status=200) return Response(serializer.data, status=200)
class DeleteAdminUserApiView(APIView):
permission_classes = [IsAuthenticated]
def delete(self, request, pk):
user = get_object_or_404(User, pk=pk)
user.delete()
return Response(status=204)
class UserDetailAPIView(generics.RetrieveAPIView):
permission_classes = [IsAuthenticated]
serializer_class = UserSerializer
lookup_field = 'id'
class AdminPermissionsAPIView(generics.GenericAPIView):
permission_classes = [IsAuthenticated]
queryset = User.objects.all()
def get(self, request):
if request.user.role.name != RoleChoice.ADMIN:
return Response({'detail': 'Forbidden'}, status=403)
admin_role = Role.objects.get(name=RoleChoice.ADMIN)
serializer = RoleListSerializer(admin_role)
return Response(serializer.data)

View File

@@ -0,0 +1,5 @@
from django.apps import AppConfig
class DocumentsConfig(AppConfig):
name = "core.apps.documents"

View File

@@ -0,0 +1,13 @@
from rest_framework import serializers
class InspectionSerializer(serializers.Serializer):
tires = serializers.CharField(required=False, allow_blank=True, default="Qoniqarli")
engine = serializers.CharField(required=False, allow_blank=True, default="Qoniqarli")
chassis = serializers.CharField(required=False, allow_blank=True, default="Qoniqarli")
transmission = serializers.CharField(required=False, allow_blank=True, default="Qoniqarli")
body = serializers.CharField(required=False, allow_blank=True, default="Qoniqarli")
class ContractPDFRequestSerializer(serializers.Serializer):
inspection = InspectionSerializer(required=False)

View File

View File

@@ -0,0 +1,33 @@
from datetime import date
import requests
CBU_URL = "https://cbu.uz/oz/arkhiv-kursov-valyut/json/{code}/{date}/"
TIMEOUT_SECONDS = 5
CURRENCY_CODES = ("USD", "EUR", "RUB")
def fetch_rates(target_date):
"""CBU.uz dan berilgan sanaga oid USD, EUR, RUB kurslarini olish.
Tarmoq xatosi yoki notogri javob bolsa bosh dict qaytadi.
"""
if target_date is None:
target_date = date.today()
date_str = target_date.strftime("%Y-%m-%d")
rates = {}
for code in CURRENCY_CODES:
try:
resp = requests.get(
CBU_URL.format(code=code, date=date_str),
timeout=TIMEOUT_SECONDS,
)
resp.raise_for_status()
data = resp.json()
if isinstance(data, list) and data:
rate_value = data[0].get("Rate")
if rate_value:
rates[code] = rate_value
except (requests.RequestException, ValueError):
continue
return rates

View File

@@ -0,0 +1,7 @@
from django.urls import path
from core.apps.documents.views.contract import ValuationReportPDFView
urlpatterns = [
path('generate-contract-pdf/<int:pk>/', ValuationReportPDFView.as_view(), name='generate_contract_pdf'),
]

View File

@@ -0,0 +1,386 @@
from datetime import date
from decimal import Decimal
from django.shortcuts import get_object_or_404
from django.template.loader import render_to_string
from django.http import HttpResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from weasyprint import HTML
from core.apps.evaluation.models import AutoEvaluationModel
from core.apps.evaluation.choices.auto import ObjectOwnerType
from core.apps.documents.serializers.contract import ContractPDFRequestSerializer
from core.apps.documents.services.cbu_rates import fetch_rates
UZ_MONTHS = {
1: "yanvar", 2: "fevral", 3: "mart", 4: "aprel",
5: "may", 6: "iyun", 7: "iyul", 8: "avgust",
9: "sentabr", 10: "oktabr", 11: "noyabr", 12: "dekabr",
}
UZ_ONES = [
"", "bir", "ikki", "uch", "to'rt", "besh",
"olti", "yetti", "sakkiz", "to'qqiz",
]
UZ_TENS = [
"", "o'n", "yigirma", "o'ttiz", "qirq", "ellik",
"oltmish", "yetmish", "sakson", "to'qson",
]
DEFAULT_INSPECTION = {
"tires": "Qoniqarli",
"engine": "Qoniqarli",
"chassis": "Qoniqarli",
"transmission": "Qoniqarli",
"body": "Qoniqarli",
}
def _format_currency(value):
if value is None:
return "0"
try:
int_val = int(Decimal(value))
except (ValueError, TypeError):
return "0"
return f"{int_val:,}".replace(",", " ")
def _format_date(value):
if not value:
return ""
return value.strftime("%d.%m.%Y")
def _three_digit_words(num):
if num == 0:
return ""
words = []
hundreds = num // 100
rest = num % 100
if hundreds:
if hundreds == 1:
words.append("bir yuz")
else:
words.append(f"{UZ_ONES[hundreds]} yuz")
tens = rest // 10
ones = rest % 10
if tens:
words.append(UZ_TENS[tens])
if ones:
words.append(UZ_ONES[ones])
return " ".join(words)
def _number_to_uzbek_words(value):
if value is None:
return ""
try:
num = int(Decimal(value))
except (ValueError, TypeError):
return ""
if num == 0:
return "nol"
parts = []
billions = num // 1_000_000_000
millions = (num % 1_000_000_000) // 1_000_000
thousands = (num % 1_000_000) // 1_000
rest = num % 1_000
if billions:
parts.append(f"{_three_digit_words(billions)} milliard")
if millions:
parts.append(f"{_three_digit_words(millions)} million")
if thousands:
parts.append(f"{_three_digit_words(thousands)} ming")
if rest:
parts.append(_three_digit_words(rest))
text = " ".join(parts).strip()
return text[0].upper() + text[1:] if text else ""
class ValuationReportPDFView(APIView):
"""
Baholash hisobotini PDF formatida yuklab olish uchun API.
GET /api/documents/generate-contract-pdf/<pk>/
POST /api/documents/generate-contract-pdf/<pk>/
POST so'rov tanasida inspection malumotlarini yuborish mumkin:
{
"inspection": {
"tires": "...",
"engine": "...",
"chassis": "...",
"transmission": "...",
"body": "..."
}
}
"""
def get(self, request, pk, *args, **kwargs):
return self._generate_pdf(request, pk, payload={})
def post(self, request, pk, *args, **kwargs):
serializer = ContractPDFRequestSerializer(data=request.data or {})
serializer.is_valid(raise_exception=True)
return self._generate_pdf(request, pk, payload=serializer.validated_data)
def _generate_pdf(self, request, pk, payload):
auto_evaluation = get_object_or_404(
AutoEvaluationModel.objects.select_related(
"user",
"vehicle",
"vehicle__brand",
"vehicle__model",
"vehicle__color",
"vehicle__fuel_type",
"vehicle__body_type",
),
pk=pk,
)
context = self._build_context(auto_evaluation, payload)
html_string = render_to_string("documents/contract.html", context)
base_url = request.build_absolute_uri("/")
try:
pdf_file = HTML(string=html_string, base_url=base_url).write_pdf()
except Exception as e:
return Response(
{"error": f"PDF yaratishda xatolik: {str(e)}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
)
report_number = context["report"]["number"]
filename = f"baholash_hisoboti_{report_number}.pdf"
response = HttpResponse(pdf_file, content_type="application/pdf")
response["Content-Disposition"] = f'attachment; filename="{filename}"'
response["Content-Length"] = len(pdf_file)
return response
def _build_context(self, auto, payload):
vehicle = auto.vehicle
user = auto.user
report_date = auto.rate_report_date or auto.contract_date or date.today()
valuation_date = auto.rate_date or report_date
inspection_date = auto.object_inspection_date or report_date
report_number = auto.registration_number or f"{auto.pk}/{report_date.year}"
# Bozor qiymati hozircha hisoblanmagan — default 0.
final_value = None
market_value_formatted = (
f"{_format_currency(final_value)} so'm" if final_value is not None else "0 so'm"
)
market_value_words = (
f"{_number_to_uzbek_words(final_value)} so'm" if final_value is not None else ""
)
cost_final = final_value
comparative_final = final_value
vehicle_ctx = self._vehicle_context(auto, vehicle)
customer_ctx = self._customer_context(user)
owner_ctx = self._owner_context(auto)
contract_ctx = self._contract_context(auto, report_date)
inspection_ctx = self._inspection_context(payload)
rates_ctx = self._rates_context(valuation_date)
ctx = {
"logo_url": "",
"report": {
"number": report_number,
"date": _format_date(report_date),
"valuation_date": _format_date(valuation_date),
"inspection_date": _format_date(inspection_date),
"year": str(report_date.year or date.today().year),
"market_value_formatted": market_value_formatted,
"market_value_words": market_value_words,
},
"vehicle": vehicle_ctx,
"customer": customer_ctx,
"owner": owner_ctx,
"contract": contract_ctx,
"company": {
"director": "",
},
"rates": rates_ctx,
"inspection": inspection_ctx,
"cost": {
"engine_volume": "",
"factory_price": _format_currency(cost_final),
"replacement_value": _format_currency(cost_final),
"wear_percent": "",
"final_value": _format_currency(cost_final),
"final_value_words": _number_to_uzbek_words(cost_final) + (" so'm" if cost_final else ""),
},
"comparative": {
"final_value": _format_currency(comparative_final),
"final_value_usd": "",
"final_value_words": _number_to_uzbek_words(comparative_final) + (" so'm" if comparative_final else ""),
},
"approach": {
"cost": {
"value": _format_currency(cost_final),
"weight": "30%",
"weighted": "",
},
"comparative": {
"value": _format_currency(comparative_final),
"weight": "70%",
"weighted": "",
},
"weighted_total": _format_currency(final_value),
},
"analog_1": self._empty_analog(),
"analog_2": self._empty_analog(),
"analog_3": self._empty_analog(),
}
return ctx
def _vehicle_context(self, auto, vehicle):
brand_name = ""
model_name = ""
if vehicle:
brand_name = vehicle.brand.name if vehicle.brand else ""
model_name = vehicle.model.name if vehicle.model else ""
if not brand_name:
brand_name = auto.car_brand or ""
if not model_name:
model_name = auto.car_model or ""
full_brand = f"{brand_name} {model_name}".strip()
plate_number = (vehicle.license_plate if vehicle else None) or auto.car_number or ""
manufacture_year = ""
if vehicle and vehicle.manufacture_year:
manufacture_year = str(vehicle.manufacture_year)
elif auto.manufacture_year:
manufacture_year = str(auto.manufacture_year)
production_date = f"{manufacture_year}-yil" if manufacture_year else ""
engine_number = (vehicle.engine_number if vehicle else None) or auto.car_dvigatel_number or ""
body_number = vehicle.vin_number if vehicle and vehicle.vin_number else ""
color_value = ""
if vehicle and vehicle.color:
color_value = vehicle.color.name
elif auto.car_color:
color_value = auto.car_color
fuel_type_value = ""
if vehicle and vehicle.fuel_type:
fuel_type_value = vehicle.fuel_type.name
tech_passport_value = ""
if vehicle and (vehicle.tech_passport_series or vehicle.tech_passport_number):
tech_passport_value = (
f"{vehicle.tech_passport_series or ''}{vehicle.tech_passport_number or ''}"
).strip()
elif auto.tex_passport_serie_num:
tech_passport_value = auto.tex_passport_serie_num
return {
"brand": full_brand,
"plate_number": plate_number,
"production_date": production_date,
"production_year": manufacture_year,
"type": auto.get_object_type_display() if auto.object_type else "",
"engine_number": engine_number,
"body_number": body_number,
"chassis_number": body_number,
"color": color_value,
"tech_passport": tech_passport_value,
"fuel_type": fuel_type_value,
"engine_power": "",
"full_weight": "",
"empty_weight": "",
}
def _customer_context(self, user):
empty = {
"name": "",
"address": "",
"phone": "",
"tin": "",
"account": "",
"bank": "",
"mfo": "",
}
if not user:
return empty
full_name = " ".join(filter(None, [user.last_name, user.first_name])).strip()
if not full_name:
full_name = user.username or user.phone or ""
return {
"name": full_name,
"address": "",
"phone": user.phone or "",
"tin": "",
"account": "",
"bank": "",
"mfo": "",
}
def _owner_context(self, auto):
if auto.object_owner_type == ObjectOwnerType.LEGAL:
return {
"name": auto.object_owner_legal_entity or "",
"address": auto.object_owner_residence or "",
}
full_name = " ".join(
filter(None, [
auto.object_owner_individual_person_l_name,
auto.object_owner_individual_person_f_name,
auto.object_owner_individual_person_p_name,
])
).strip()
return {
"name": full_name,
"address": auto.object_owner_residence or "",
}
def _contract_context(self, auto, fallback_date):
contract_date = auto.contract_date or auto.rate_report_date or fallback_date
return {
"number": auto.registration_number or str(auto.pk),
"day": f"{contract_date.day:02d}",
"month": UZ_MONTHS.get(contract_date.month, ""),
"year": str(contract_date.year),
}
def _inspection_context(self, payload):
provided = (payload or {}).get("inspection") or {}
return {
key: provided.get(key) or default
for key, default in DEFAULT_INSPECTION.items()
}
def _rates_context(self, target_date):
rates = fetch_rates(target_date)
return {
"rur": rates.get("RUB", ""),
"usd": rates.get("USD", ""),
"eur": rates.get("EUR", ""),
}
def _empty_analog(self):
return {
"source": "",
"phone": "",
"description": "",
"year": "",
"mileage": "",
"price": "",
"adjusted_price_1": "",
"final_price": "",
"weight": "",
}

View File

@@ -55,7 +55,7 @@ class AutoEvaluationAdmin(ModelAdmin):
"fields": ( "fields": (
"tex_passport_serie_num", "tex_passport_serie_num",
("tex_passport_gived_date", "tex_passport_gived_location"), ("tex_passport_gived_date", "tex_passport_gived_location"),
("car_type", "car_wheel"), ("car_wheel",),
("car_brand", "car_model"), ("car_brand", "car_model"),
("car_number", "manufacture_year"), ("car_number", "manufacture_year"),
("car_dvigatel_number", "car_color"), ("car_dvigatel_number", "car_color"),

View File

@@ -6,6 +6,8 @@ class AutoObjectType(models.TextChoices):
LIGHTWEIGHT_AUTO = "lightweight_auto", _("Yengil automobil") LIGHTWEIGHT_AUTO = "lightweight_auto", _("Yengil automobil")
TRUCK_CAR = "truck_car", _("Yuk automobil") TRUCK_CAR = "truck_car", _("Yuk automobil")
SPECIAL_TECH = "special_tech", _("Maxsus texnika") SPECIAL_TECH = "special_tech", _("Maxsus texnika")
BUS = "bus", _("Avtobus")
MOTO = "moto", _("Mototsikl")
class AutoEvaluationStatus(models.TextChoices): class AutoEvaluationStatus(models.TextChoices):

View File

@@ -14,3 +14,4 @@ class ReferenceType(models.TextChoices):
DETERMINED_VALUE = "determined_value", _("Determined value type") DETERMINED_VALUE = "determined_value", _("Determined value type")
PROPERTY_RIGHTS = "property_rights", _("Property rights") PROPERTY_RIGHTS = "property_rights", _("Property rights")
OWNERSHIP_FORM = "ownership_form", _("Ownership form") OWNERSHIP_FORM = "ownership_form", _("Ownership form")
ASSESSMENT_TASK_TYPE = "assessment_task_type", _("Assessment task type")

View File

@@ -19,3 +19,8 @@ class RequestStatus(models.TextChoices):
IN_PROGRESS = "in_progress", _("Jarayonda") IN_PROGRESS = "in_progress", _("Jarayonda")
COMPLETED = "completed", _("Bajarildi") COMPLETED = "completed", _("Bajarildi")
REJECTED = "rejected", _("Rad etildi") REJECTED = "rejected", _("Rad etildi")
class RequestPersonType(models.TextChoices):
INDIVIDUAL_PERSON = "individual_person", "Jismoniy shaxs"
LEGAL_PERSON = "legal_person", 'Yuridik shaxs',

View File

@@ -1,4 +1,5 @@
from .auto import * # noqa from .auto import * # noqa
from .mechanic_auto import * # noqa
from .customer import * # noqa from .customer import * # noqa
from .document import * # noqa from .document import * # noqa
from .documentcategory import * # noqa from .documentcategory import * # noqa

View File

@@ -1,7 +1,11 @@
from django_filters import rest_framework as filters from django_filters import rest_framework as filters
from core.apps.evaluation.choices.history import EvaluationEventType from core.apps.evaluation.choices.history import EvaluationEventType
from core.apps.evaluation.models import AutoevaluationhistoryModel, QuickevaluationhistoryModel from core.apps.evaluation.models import (
AutoevaluationhistoryModel,
MechanicAutoevaluationhistoryModel,
QuickevaluationhistoryModel,
)
class AutoevaluationhistoryFilter(filters.FilterSet): class AutoevaluationhistoryFilter(filters.FilterSet):
@@ -29,6 +33,31 @@ class AutoevaluationhistoryFilter(filters.FilterSet):
] ]
class MechanicAutoevaluationhistoryFilter(filters.FilterSet):
mechanic_auto_evaluation = filters.NumberFilter(
field_name="mechanic_auto_evaluation", lookup_expr="exact"
)
event_type = filters.ChoiceFilter(
field_name="event_type",
choices=EvaluationEventType.choices,
)
created_from = filters.DateTimeFilter(
field_name="created_at", lookup_expr="gte"
)
created_to = filters.DateTimeFilter(
field_name="created_at", lookup_expr="lte"
)
class Meta:
model = MechanicAutoevaluationhistoryModel
fields = [
"mechanic_auto_evaluation",
"event_type",
"created_from",
"created_to",
]
class QuickevaluationhistoryFilter(filters.FilterSet): class QuickevaluationhistoryFilter(filters.FilterSet):
quick_evaluation = filters.NumberFilter( quick_evaluation = filters.NumberFilter(
field_name="quick_evaluation", lookup_expr="exact" field_name="quick_evaluation", lookup_expr="exact"

View File

@@ -0,0 +1,37 @@
from django_filters import rest_framework as filters
from core.apps.evaluation.models import MechanicAutoEvaluationModel
class MechanicAutoevaluationFilter(filters.FilterSet):
status = filters.CharFilter(method="filter_status")
object_type = filters.CharFilter(field_name="object_type", lookup_expr="exact")
object_owner_type = filters.NumberFilter(field_name="object_owner_type", lookup_expr="exact")
rate_type = filters.NumberFilter(field_name="rate_type", lookup_expr="exact")
value_determined = filters.NumberFilter(field_name="value_determined", lookup_expr="exact")
client = filters.NumberFilter(field_name="valuation__customer", lookup_expr="exact")
created_from = filters.DateFilter(field_name="created_at", lookup_expr="gte")
created_to = filters.DateFilter(field_name="created_at", lookup_expr="lte")
rate_date_from = filters.DateFilter(field_name="rate_date", lookup_expr="gte")
rate_date_to = filters.DateFilter(field_name="rate_date", lookup_expr="lte")
def filter_status(self, queryset, name, value):
if value:
statuses = [s.strip() for s in value.split(",") if s.strip()]
return queryset.filter(status__in=statuses)
return queryset
class Meta:
model = MechanicAutoEvaluationModel
fields = [
"status",
"object_type",
"object_owner_type",
"rate_type",
"value_determined",
"client",
"created_from",
"created_to",
"rate_date_from",
"rate_date_to",
]

View File

@@ -1,4 +1,4 @@
# Generated by Django 5.2.7 on 2026-04-24 12:55 # Generated by Django 5.2.7 on 2026-04-27 09:33
from django.db import migrations, models from django.db import migrations, models

View File

@@ -0,0 +1,53 @@
# Generated by Django 5.2.7 on 2026-04-28 11:07
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('evaluation', '0035_autoevaluationmodel_is_archived'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.RemoveField(
model_name='autoevaluationmodel',
name='form_ownership',
),
migrations.RemoveField(
model_name='autoevaluationmodel',
name='property_rights',
),
migrations.RemoveField(
model_name='autoevaluationmodel',
name='rate_object_name',
),
migrations.AlterField(
model_name='autoevaluationmodel',
name='appraisers',
field=models.ManyToManyField(blank=True, null=True, to=settings.AUTH_USER_MODEL, verbose_name='appraisers'),
),
migrations.AlterField(
model_name='autoevaluationmodel',
name='evaluation_request',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='auto_evaluations_request', to='evaluation.evaluationrequestmodel'),
),
migrations.AlterField(
model_name='autoevaluationmodel',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='auto_evaluations_user', to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='autoevaluationmodel',
name='valuation',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='auto_detail', to='evaluation.valuationmodel'),
),
migrations.AlterField(
model_name='autoevaluationmodel',
name='vehicle',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='evaluation', to='evaluation.vehiclemodel'),
),
]

View File

@@ -0,0 +1,49 @@
# Generated by Django 5.2.7 on 2026-04-28 11:41
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('evaluation', '0036_remove_autoevaluationmodel_form_ownership_and_more'),
]
operations = [
migrations.AddField(
model_name='evaluationrequestmodel',
name='customer_and_owner_same',
field=models.BooleanField(default=False),
),
migrations.CreateModel(
name='EvaluationRequestCustomerModel',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('type', models.CharField(choices=[('individual_person', 'Jismoniy shaxs'), ('legal_person', 'Yuridik shaxs')], max_length=100)),
('jshshir', models.CharField(max_length=100)),
('evaluation_request', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='customer', to='evaluation.evaluationrequestmodel')),
],
options={
'verbose_name': 'Evaluation Request Customer',
'verbose_name_plural': 'Evaluation Request Customers',
},
),
migrations.CreateModel(
name='EvaluationRequestOwnerModel',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('type', models.CharField(choices=[('individual_person', 'Jismoniy shaxs'), ('legal_person', 'Yuridik shaxs')], max_length=100)),
('jshshir', models.CharField(max_length=100)),
('evaluation_request', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='owner', to='evaluation.evaluationrequestmodel')),
],
options={
'verbose_name': 'Evaluation Request Owner',
'verbose_name_plural': 'Evaluation Request Owners',
},
),
]

View File

@@ -0,0 +1,88 @@
# Generated by Django 5.2.7 on 2026-04-28 11:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('evaluation', '0037_evaluationrequestmodel_customer_and_owner_same_and_more'),
]
operations = [
migrations.AddField(
model_name='evaluationrequestmodel',
name='distance_covered',
field=models.FloatField(blank=True, default=0.0, null=True),
),
migrations.AddField(
model_name='evaluationrequestmodel',
name='gov_number',
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AlterField(
model_name='evaluationrequestmodel',
name='chassi',
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AlterField(
model_name='evaluationrequestmodel',
name='customer_inn_number',
field=models.CharField(max_length=20),
),
migrations.AlterField(
model_name='evaluationrequestmodel',
name='is_archive',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='evaluationrequestmodel',
name='location_lat',
field=models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True),
),
migrations.AlterField(
model_name='evaluationrequestmodel',
name='location_lng',
field=models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True),
),
migrations.AlterField(
model_name='evaluationrequestmodel',
name='location_name',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.AlterField(
model_name='evaluationrequestmodel',
name='need_delivering',
field=models.BooleanField(default=True),
),
migrations.AlterField(
model_name='evaluationrequestmodel',
name='object_type',
field=models.CharField(blank=True, choices=[('lightweight_auto', 'Yengil automobil'), ('truck_car', 'Yuk automobil'), ('special_tech', 'Maxsus texnika')], max_length=50, null=True),
),
migrations.AlterField(
model_name='evaluationrequestmodel',
name='owner_inn_number',
field=models.CharField(max_length=20),
),
migrations.AlterField(
model_name='evaluationrequestmodel',
name='rate_type',
field=models.CharField(choices=[('auto', 'Automobil'), ('real_estate', "Ko'chmas mulk"), ('equipment', 'Uskuna')], max_length=50),
),
migrations.AlterField(
model_name='evaluationrequestmodel',
name='status',
field=models.CharField(choices=[('pending', 'Kutilmoqda'), ('in_progress', 'Jarayonda'), ('completed', 'Bajarildi'), ('rejected', 'Rad etildi')], default='pending', max_length=50),
),
migrations.AlterField(
model_name='evaluationrequestmodel',
name='tex_passport',
field=models.CharField(blank=True, max_length=20, null=True),
),
migrations.AlterField(
model_name='evaluationrequestmodel',
name='worked_hours',
field=models.IntegerField(blank=True, null=True),
),
]

View File

@@ -0,0 +1,31 @@
# Generated by Django 5.2.7 on 2026-05-01 06:45
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('evaluation', '0038_evaluationrequestmodel_distance_covered_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Bonus',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('updated_at', models.DateTimeField(auto_now=True)),
('bonus_type', models.CharField(choices=[('lightweight_auto', 'Yengil automobil'), ('truck_car', 'Yuk automobil'), ('special_tech', 'Maxsus texnika')], max_length=50)),
('percentage', models.FloatField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('price', models.FloatField()),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bonuses', to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
},
),
]

View File

@@ -0,0 +1,59 @@
# Generated by Django 5.2.7 on 2026-05-01 11:43
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('evaluation', '0039_bonus'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='BaseValueBonus',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('base_price', models.DecimalField(decimal_places=2, max_digits=12)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='BonusType',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=255)),
('category', models.CharField(choices=[('auto_transport', 'Avtotransport'), ('real estate', "ko'chmas mulk"), ('equipment', 'uskuna va jihozlar')], max_length=50)),
('percentage', models.PositiveIntegerField()),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='EmployeeBonus',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('percentage', models.PositiveIntegerField()),
('bonus_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='evaluation.bonustype')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bonuses', to=settings.AUTH_USER_MODEL)),
],
options={
'unique_together': {('user', 'bonus_type')},
},
),
migrations.DeleteModel(
name='Bonus',
),
]

View File

@@ -0,0 +1,17 @@
# Generated by Django 5.2.7 on 2026-05-01 12:06
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('evaluation', '0040_basevaluebonus_bonustype_employeebonus_delete_bonus'),
]
operations = [
migrations.RenameModel(
old_name='BonusType',
new_name='BonusCategory',
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.7 on 2026-05-04 12:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('evaluation', '0041_rename_bonustype_bonuscategory'),
]
operations = [
migrations.AlterField(
model_name='bonuscategory',
name='category',
field=models.CharField(choices=[('lightweight_auto', 'Yengil automobil'), ('truck_car', 'Yuk automobil'), ('special_tech', 'Maxsus texnika')], max_length=50),
),
]

View File

@@ -0,0 +1,268 @@
# Generated by Django 5.2.7 on 2026-05-05 11:52
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("evaluation", "0042_alter_bonuscategory_category"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="MechanicAutoEvaluationModel",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"tex_passport_file",
models.FileField(
blank=True,
null=True,
upload_to="mechanic_evaluation/tech_passports/%Y/%m/",
verbose_name="tech passport file",
),
),
(
"registration_number",
models.CharField(blank=True, max_length=50, null=True, verbose_name="registration number"),
),
("contract_date", models.DateField(blank=True, null=True, verbose_name="contract date")),
(
"object_inspection_date",
models.DateField(blank=True, null=True, verbose_name="object inspection date"),
),
("rate_date", models.DateField(blank=True, null=True, verbose_name="rate date")),
("rate_report_date", models.DateField(blank=True, null=True, verbose_name="rate report date")),
(
"object_type",
models.CharField(
blank=True,
choices=[
("lightweight_auto", "Yengil automobil"),
("truck_car", "Yuk automobil"),
("special_tech", "Maxsus texnika"),
],
max_length=50,
null=True,
verbose_name="object type",
),
),
(
"object_owner_type",
models.IntegerField(
blank=True,
choices=[(1, "Jismoniy shaxs"), (2, "Yuridik shaxs")],
null=True,
verbose_name="object owner type",
),
),
(
"object_owner_individual_person_f_name",
models.CharField(blank=True, max_length=100, null=True, verbose_name="owner first name"),
),
(
"object_owner_individual_person_l_name",
models.CharField(blank=True, max_length=100, null=True, verbose_name="owner last name"),
),
(
"object_owner_individual_person_p_name",
models.CharField(blank=True, max_length=100, null=True, verbose_name="owner patronymic"),
),
(
"object_owner_individual_person_passport_num",
models.CharField(blank=True, max_length=20, null=True, verbose_name="owner passport number"),
),
(
"object_owner_legal_entity",
models.CharField(blank=True, max_length=255, null=True, verbose_name="legal entity name"),
),
(
"object_owner_legal_inn",
models.CharField(blank=True, max_length=20, null=True, verbose_name="legal entity INN"),
),
(
"tex_passport_serie_num",
models.CharField(
blank=True, max_length=20, null=True, verbose_name="tech passport series and number"
),
),
(
"tex_passport_gived_date",
models.DateField(blank=True, null=True, verbose_name="tech passport given date"),
),
(
"tex_passport_gived_location",
models.CharField(
blank=True, max_length=255, null=True, verbose_name="tech passport given location"
),
),
(
"car_type",
models.IntegerField(
blank=True, choices=[(1, "Xetchbek"), (2, "Universal")], null=True, verbose_name="car type"
),
),
(
"car_wheel",
models.IntegerField(blank=True, choices=[(1, "4x4")], null=True, verbose_name="car wheel"),
),
("car_brand", models.CharField(blank=True, max_length=100, null=True, verbose_name="car brand")),
("car_model", models.CharField(blank=True, max_length=100, null=True, verbose_name="car model")),
("car_number", models.CharField(blank=True, max_length=20, null=True, verbose_name="car number")),
(
"manufacture_year",
models.CharField(blank=True, max_length=10, null=True, verbose_name="manufacture year"),
),
(
"car_dvigatel_number",
models.CharField(blank=True, max_length=50, null=True, verbose_name="engine number"),
),
("car_color", models.CharField(blank=True, max_length=50, null=True, verbose_name="car color")),
("rating_goal", models.CharField(blank=True, max_length=50, null=True, verbose_name="rating goal")),
(
"status",
models.CharField(
choices=[
("yaratildi", "Yaratildi"),
("baxolovchi_biriktirildi", "Baholovchi biriktirildi"),
("baxolandi", "Baholandi"),
("rad_etildi", "Rad etildi"),
("tasdiqlandi", "Tasdiqlandi"),
],
default="yaratildi",
max_length=50,
verbose_name="status",
),
),
("is_archived", models.BooleanField(default=False, verbose_name="is archived")),
(
"appraisers",
models.ManyToManyField(
blank=True,
related_name="mechanic_auto_evaluation_appraisers",
to=settings.AUTH_USER_MODEL,
verbose_name="appraisers",
),
),
(
"evaluation_request",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="mechanic_auto_evaluations_request",
to="evaluation.evaluationrequestmodel",
),
),
(
"rate_type",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="evaluation_mechanic_auto_rate_type",
to="evaluation.referenceitemmodel",
verbose_name="rate type",
),
),
(
"user",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="mechanic_auto_evaluations_user",
to=settings.AUTH_USER_MODEL,
),
),
(
"valuation",
models.OneToOneField(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="mechanic_auto_detail",
to="evaluation.valuationmodel",
),
),
(
"value_determined",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="evaluation_mechanic_auto_value_determined",
to="evaluation.referenceitemmodel",
verbose_name="value determined",
),
),
(
"vehicle",
models.OneToOneField(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="mechanic_evaluation",
to="evaluation.vehiclemodel",
),
),
],
options={
"verbose_name": "Mechanic Auto Evaluation",
"verbose_name_plural": "Mechanic Auto Evaluations",
"db_table": "MechanicAutoEvaluation",
},
),
migrations.CreateModel(
name="MechanicAutoevaluationhistoryModel",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
(
"event_type",
models.CharField(
choices=[
("order_created", "Buyurtma yaratildi"),
("status_changed", "Status o'zgartirildi"),
("evaluator_assigned", "Baholovchi biriktirildi"),
("document_uploaded", "Hujjat yuklandi"),
("payment_made", "To'lov qilindi"),
],
max_length=50,
verbose_name="event type",
),
),
("actor_id", models.BigIntegerField(blank=True, null=True, verbose_name="actor id")),
("actor_full_name", models.CharField(default="Tizim", max_length=255, verbose_name="actor full name")),
("actor_role", models.CharField(default="system", max_length=50, verbose_name="actor role")),
("meta", models.JSONField(blank=True, default=dict, verbose_name="meta")),
("created_at", models.DateTimeField(auto_now_add=True, verbose_name="created at")),
(
"mechanic_auto_evaluation",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="history",
to="evaluation.mechanicautoevaluationmodel",
verbose_name="mechanic auto evaluation",
),
),
],
options={
"verbose_name": "Mechanic Auto Evaluation History",
"verbose_name_plural": "Mechanic Auto Evaluation Histories",
"db_table": "MechanicAutoEvaluationHistory",
"ordering": ["created_at"],
"indexes": [
models.Index(
fields=["mechanic_auto_evaluation_id", "created_at"], name="mech_auto_hist_eval_date_idx"
),
models.Index(fields=["event_type"], name="mech_auto_hist_event_type_idx"),
],
},
),
]

View File

@@ -0,0 +1,167 @@
# Generated by Django 5.2.7 on 2026-05-05 12:06
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("evaluation", "0043_mechanicautoevaluationmodel_and_more"),
]
operations = [
migrations.RemoveField(
model_name="autoevaluationmodel",
name="car_type",
),
migrations.RemoveField(
model_name="autoevaluationmodel",
name="tex_passport_file",
),
migrations.AddField(
model_name="autoevaluationmodel",
name="assessment_task_type",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="evaluation_auto_assessment_task_type",
to="evaluation.referenceitemmodel",
verbose_name="assessment task type",
),
),
migrations.AddField(
model_name="autoevaluationmodel",
name="body_type",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="evaluation_auto_body_type",
to="evaluation.referenceitemmodel",
verbose_name="body type",
),
),
migrations.AddField(
model_name="autoevaluationmodel",
name="car_position",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="evaluation_auto_car_position",
to="evaluation.referenceitemmodel",
verbose_name="car position",
),
),
migrations.AddField(
model_name="autoevaluationmodel",
name="distance_covered",
field=models.PositiveIntegerField(blank=True, null=True, verbose_name="distance covered"),
),
migrations.AddField(
model_name="autoevaluationmodel",
name="fuel_type",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="evaluation_auto_fuel_type",
to="evaluation.referenceitemmodel",
verbose_name="fuel type",
),
),
migrations.AddField(
model_name="autoevaluationmodel",
name="object_owner_residence",
field=models.CharField(blank=True, max_length=255, null=True, verbose_name="object owner residence"),
),
migrations.AddField(
model_name="autoevaluationmodel",
name="state_car",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="evaluation_auto_state_car",
to="evaluation.referenceitemmodel",
verbose_name="state car",
),
),
migrations.AlterField(
model_name="autoevaluationmodel",
name="object_type",
field=models.CharField(
blank=True,
choices=[
("lightweight_auto", "Yengil automobil"),
("truck_car", "Yuk automobil"),
("special_tech", "Maxsus texnika"),
("bus", "Avtobus"),
("moto", "Mototsikl"),
],
max_length=50,
null=True,
verbose_name="object type",
),
),
migrations.AlterField(
model_name="bonuscategory",
name="category",
field=models.CharField(
choices=[
("lightweight_auto", "Yengil automobil"),
("truck_car", "Yuk automobil"),
("special_tech", "Maxsus texnika"),
("bus", "Avtobus"),
("moto", "Mototsikl"),
],
max_length=50,
),
),
migrations.AlterField(
model_name="mechanicautoevaluationmodel",
name="object_type",
field=models.CharField(
blank=True,
choices=[
("lightweight_auto", "Yengil automobil"),
("truck_car", "Yuk automobil"),
("special_tech", "Maxsus texnika"),
("bus", "Avtobus"),
("moto", "Mototsikl"),
],
max_length=50,
null=True,
verbose_name="object type",
),
),
migrations.CreateModel(
name="AutoEvaluationTexPassportFile",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"file",
models.FileField(
upload_to="auto_evaluation/tech_passports/%Y/%m/", verbose_name="tech passport file"
),
),
(
"auto_evaluation",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="tex_passport_files",
to="evaluation.autoevaluationmodel",
),
),
],
options={
"verbose_name": "Auto Evaluation Tex Passport File",
"verbose_name_plural": "Auto Evaluation Tex Passport Files",
"db_table": "AutoEvaluationTexPassportFile",
},
),
]

View File

@@ -0,0 +1,16 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('evaluation', '0044_remove_autoevaluationmodel_car_type_and_more'),
]
operations = [
migrations.AlterField(
model_name='referenceitemmodel',
name='type',
field=models.CharField(choices=[('brand', 'Brand'), ('marka', 'Marka'), ('color', 'Color'), ('fuel_type', 'Fuel type'), ('body_type', 'Body type'), ('car_position', 'Car position'), ('state_car', 'Car state'), ('evaluation_purpose', 'Evaluation purpose'), ('determined_value', 'Determined value type'), ('property_rights', 'Property rights'), ('ownership_form', 'Ownership form'), ('assessment_task_type', 'Assessment task type')], max_length=50, verbose_name='type'),
),
]

View File

@@ -0,0 +1,118 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("evaluation", "0045_alter_referenceitemmodel_type"),
]
operations = [
migrations.RemoveField(
model_name="mechanicautoevaluationmodel",
name="car_type",
),
migrations.RemoveField(
model_name="mechanicautoevaluationmodel",
name="tex_passport_file",
),
migrations.AddField(
model_name="mechanicautoevaluationmodel",
name="distance_covered",
field=models.PositiveIntegerField(blank=True, null=True, verbose_name="distance covered"),
),
migrations.AddField(
model_name="mechanicautoevaluationmodel",
name="object_owner_residence",
field=models.CharField(blank=True, max_length=255, null=True, verbose_name="object owner residence"),
),
migrations.AddField(
model_name="mechanicautoevaluationmodel",
name="car_position",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="evaluation_mechanic_auto_car_position",
to="evaluation.referenceitemmodel",
verbose_name="car position",
),
),
migrations.AddField(
model_name="mechanicautoevaluationmodel",
name="body_type",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="evaluation_mechanic_auto_body_type",
to="evaluation.referenceitemmodel",
verbose_name="body type",
),
),
migrations.AddField(
model_name="mechanicautoevaluationmodel",
name="fuel_type",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="evaluation_mechanic_auto_fuel_type",
to="evaluation.referenceitemmodel",
verbose_name="fuel type",
),
),
migrations.AddField(
model_name="mechanicautoevaluationmodel",
name="state_car",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="evaluation_mechanic_auto_state_car",
to="evaluation.referenceitemmodel",
verbose_name="state car",
),
),
migrations.AddField(
model_name="mechanicautoevaluationmodel",
name="assessment_task_type",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="evaluation_mechanic_auto_assessment_task_type",
to="evaluation.referenceitemmodel",
verbose_name="assessment task type",
),
),
migrations.CreateModel(
name="MechanicAutoEvaluationTexPassportFile",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"file",
models.FileField(
upload_to="mechanic_evaluation/tech_passports/%Y/%m/",
verbose_name="tech passport file",
),
),
(
"mechanic_auto_evaluation",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="tex_passport_files",
to="evaluation.mechanicautoevaluationmodel",
),
),
],
options={
"verbose_name": "Mechanic Auto Evaluation Tex Passport File",
"verbose_name_plural": "Mechanic Auto Evaluation Tex Passport Files",
"db_table": "MechanicAutoEvaluationTexPassportFile",
},
),
]

View File

@@ -1,4 +1,5 @@
from .auto import * # noqa from .auto import * # noqa
from .mechanic_auto import * # noqa
from .customer import * # noqa from .customer import * # noqa
from .document import * # noqa from .document import * # noqa
from .documentcategory import * # noqa from .documentcategory import * # noqa

View File

@@ -4,19 +4,15 @@ from django_core.models import AbstractBaseModel
from model_bakery import baker from model_bakery import baker
from core.apps.evaluation.choices.auto import ( from core.apps.evaluation.choices.auto import (
AutoCarType,
AutoCarWheel, AutoCarWheel,
AutoEvaluationStatus, AutoEvaluationStatus,
AutoObjectType, AutoObjectType,
# FormOwnership, # FormOwnership,
LocationConvenience,
LocationHighways,
ObjectOwnerType, ObjectOwnerType,
# PropertyRights, # PropertyRights,
# RateType, # RateType,
# ValueDetermined, # ValueDetermined,
) )
from .valuation import ValuationModel from .valuation import ValuationModel
from .vehicle import VehicleModel from .vehicle import VehicleModel
@@ -26,7 +22,6 @@ class AutoEvaluationModel(AbstractBaseModel):
"accounts.User", "accounts.User",
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
related_name="auto_evaluations_user", related_name="auto_evaluations_user",
verbose_name=_("user"),
null=True, null=True,
blank=True, blank=True,
) )
@@ -34,7 +29,6 @@ class AutoEvaluationModel(AbstractBaseModel):
"evaluation.EvaluationRequestModel", "evaluation.EvaluationRequestModel",
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
related_name="auto_evaluations_request", related_name="auto_evaluations_request",
verbose_name=_("evaluation request"),
null=True, null=True,
blank=True, blank=True,
) )
@@ -42,7 +36,6 @@ class AutoEvaluationModel(AbstractBaseModel):
ValuationModel, ValuationModel,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name="auto_detail", related_name="auto_detail",
verbose_name=_("valuation"),
null=True, null=True,
blank=True, blank=True,
) )
@@ -50,24 +43,67 @@ class AutoEvaluationModel(AbstractBaseModel):
VehicleModel, VehicleModel,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name="evaluation", related_name="evaluation",
verbose_name=_("vehicle"),
null=True, null=True,
blank=True, blank=True,
) )
appraisers = models.ManyToManyField( appraisers = models.ManyToManyField(
"accounts.User", "accounts.User",
verbose_name=_("appraisers"), verbose_name=_("appraisers"),
related_name="auto_evaluations",
blank=True, blank=True,
null=True, null=True,
) )
tex_passport_file = models.FileField( distance_covered = models.PositiveIntegerField(
verbose_name=_("tech passport file"), verbose_name=_("distance covered"),
upload_to="quick_evaluation/tech_passports/%Y/%m/",
blank=True, blank=True,
null=True, null=True,
) )
object_owner_residence = models.CharField(
verbose_name=_("object owner residence"),
max_length=255,
blank=True,
null=True,
)
car_position = models.ForeignKey(
'evaluation.ReferenceitemModel',
verbose_name=_("car position"),
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='evaluation_auto_car_position',
)
body_type = models.ForeignKey(
'evaluation.ReferenceitemModel',
verbose_name=_("body type"),
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='evaluation_auto_body_type',
)
fuel_type = models.ForeignKey(
'evaluation.ReferenceitemModel',
verbose_name=_("fuel type"),
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='evaluation_auto_fuel_type',
)
state_car = models.ForeignKey(
'evaluation.ReferenceitemModel',
verbose_name=_("state car"),
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='evaluation_auto_state_car',
)
assessment_task_type = models.ForeignKey(
'evaluation.ReferenceitemModel',
verbose_name=_("assessment task type"),
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='evaluation_auto_assessment_task_type',
)
# ── Step 1 — Umumiy ma'lumotlar ────────────────────────────────── # ── Step 1 — Umumiy ma'lumotlar ──────────────────────────────────
registration_number = models.CharField( registration_number = models.CharField(
@@ -96,12 +132,6 @@ class AutoEvaluationModel(AbstractBaseModel):
blank=True, blank=True,
null=True, null=True,
) )
rate_object_name = models.CharField(
verbose_name=_("rate object name"),
max_length=255,
blank=True,
null=True,
)
object_type = models.CharField( object_type = models.CharField(
verbose_name=_("object type"), verbose_name=_("object type"),
max_length=50, max_length=50,
@@ -153,23 +183,6 @@ class AutoEvaluationModel(AbstractBaseModel):
blank=True, blank=True,
null=True, null=True,
) )
property_rights = models.ForeignKey(
'evaluation.ReferenceitemModel',
verbose_name=_("property rights"),
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='evaluation_auto_property_rights'
)
form_ownership = models.ForeignKey(
'evaluation.ReferenceitemModel',
verbose_name=_("form of ownership"),
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='evaluation_auto_form_ownership'
)
value_determined = models.ForeignKey( value_determined = models.ForeignKey(
'evaluation.ReferenceitemModel', 'evaluation.ReferenceitemModel',
verbose_name=_("value determined"), verbose_name=_("value determined"),
@@ -205,12 +218,6 @@ class AutoEvaluationModel(AbstractBaseModel):
blank=True, blank=True,
null=True, null=True,
) )
car_type = models.IntegerField(
verbose_name=_("car type"),
choices=AutoCarType.choices,
blank=True,
null=True,
)
car_wheel = models.IntegerField( car_wheel = models.IntegerField(
verbose_name=_("car wheel"), verbose_name=_("car wheel"),
choices=AutoCarWheel.choices, choices=AutoCarWheel.choices,
@@ -272,8 +279,6 @@ class AutoEvaluationModel(AbstractBaseModel):
default=False, default=False,
) )
def __str__(self): def __str__(self):
return f"Auto Evaluation {self.registration_number or self.pk}" return f"Auto Evaluation {self.registration_number or self.pk}"
@@ -285,3 +290,27 @@ class AutoEvaluationModel(AbstractBaseModel):
db_table = "AutoEvaluation" db_table = "AutoEvaluation"
verbose_name = _("Auto Evaluation") verbose_name = _("Auto Evaluation")
verbose_name_plural = _("Auto Evaluations") verbose_name_plural = _("Auto Evaluations")
class AutoEvaluationTexPassportFile(AbstractBaseModel):
auto_evaluation = models.ForeignKey(
AutoEvaluationModel,
on_delete=models.CASCADE,
related_name="tex_passport_files",
)
file = models.FileField(
verbose_name=_("tech passport file"),
upload_to="auto_evaluation/tech_passports/%Y/%m/",
)
def __str__(self):
return f"Tex passport file for AutoEvaluation #{self.auto_evaluation_id}"
@classmethod
def _baker(cls):
return baker.make(cls)
class Meta:
db_table = "AutoEvaluationTexPassportFile"
verbose_name = _("Auto Evaluation Tex Passport File")
verbose_name_plural = _("Auto Evaluation Tex Passport Files")

View File

@@ -0,0 +1,33 @@
from django.db import models
from django.db.models.fields import PositiveIntegerField
from django_core.models import AbstractBaseModel
from core.apps.evaluation.choices.auto import AutoObjectType
class BaseValueBonus(AbstractBaseModel):
base_price = models.DecimalField(max_digits=12, decimal_places=2)
def __str__(self):
return f"Base: {self.base_price}"
class BonusCategory(AbstractBaseModel):
name = models.CharField(max_length=255)
category = models.CharField(
max_length=50,
choices=AutoObjectType.choices
)
percentage = PositiveIntegerField()
def __str__(self):
return self.name
class EmployeeBonus(AbstractBaseModel):
user = models.ForeignKey("accounts.User", on_delete=models.CASCADE, related_name="bonuses", )
bonus_type = models.ForeignKey(BonusCategory, on_delete=models.CASCADE)
percentage = models.PositiveIntegerField()
class Meta:
unique_together = ("user", "bonus_type")

View File

@@ -69,6 +69,66 @@ class AutoevaluationhistoryModel(models.Model):
] ]
class MechanicAutoevaluationhistoryModel(models.Model):
"""MechanicAutoEvaluation bo'yicha barcha harakatlar logi — faqat o'qiladi, signallar tomonidan yoziladi."""
mechanic_auto_evaluation = models.ForeignKey(
"evaluation.MechanicAutoEvaluationModel",
on_delete=models.CASCADE,
related_name="history",
verbose_name=_("mechanic auto evaluation"),
)
event_type = models.CharField(
verbose_name=_("event type"),
max_length=50,
choices=EvaluationEventType.choices,
)
actor_id = models.BigIntegerField(
verbose_name=_("actor id"),
null=True,
blank=True,
)
actor_full_name = models.CharField(
verbose_name=_("actor full name"),
max_length=255,
default="Tizim",
)
actor_role = models.CharField(
verbose_name=_("actor role"),
max_length=50,
default="system",
)
meta = models.JSONField(
verbose_name=_("meta"),
default=dict,
blank=True,
)
created_at = models.DateTimeField(
verbose_name=_("created at"),
auto_now_add=True,
)
def __str__(self):
return f"{self.get_event_type_display()} — MechanicAutoEval #{self.mechanic_auto_evaluation_id}"
@classmethod
def _baker(cls):
return baker.make(cls)
class Meta:
db_table = "MechanicAutoEvaluationHistory"
verbose_name = _("Mechanic Auto Evaluation History")
verbose_name_plural = _("Mechanic Auto Evaluation Histories")
ordering = ["created_at"]
indexes = [
models.Index(fields=["mechanic_auto_evaluation_id", "created_at"], name="mech_auto_hist_eval_date_idx"),
models.Index(fields=["event_type"], name="mech_auto_hist_event_type_idx"),
]
class QuickevaluationhistoryModel(models.Model): class QuickevaluationhistoryModel(models.Model):
"""QuickEvaluation bo'yicha barcha harakatlar logi — faqat o'qiladi, signallar tomonidan yoziladi.""" """QuickEvaluation bo'yicha barcha harakatlar logi — faqat o'qiladi, signallar tomonidan yoziladi."""

View File

@@ -0,0 +1,312 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from django_core.models import AbstractBaseModel
from model_bakery import baker
from core.apps.evaluation.choices.auto import (
AutoCarWheel,
AutoEvaluationStatus,
AutoObjectType,
ObjectOwnerType,
)
from .valuation import ValuationModel
from .vehicle import VehicleModel
class MechanicAutoEvaluationModel(AbstractBaseModel):
user = models.ForeignKey(
"accounts.User",
on_delete=models.SET_NULL,
related_name="mechanic_auto_evaluations_user",
null=True,
blank=True,
)
evaluation_request = models.ForeignKey(
"evaluation.EvaluationRequestModel",
on_delete=models.SET_NULL,
related_name="mechanic_auto_evaluations_request",
null=True,
blank=True,
)
valuation = models.OneToOneField(
ValuationModel,
on_delete=models.CASCADE,
related_name="mechanic_auto_detail",
null=True,
blank=True,
)
vehicle = models.OneToOneField(
VehicleModel,
on_delete=models.CASCADE,
related_name="mechanic_evaluation",
null=True,
blank=True,
)
appraisers = models.ManyToManyField(
"accounts.User",
verbose_name=_("appraisers"),
related_name="mechanic_auto_evaluation_appraisers",
blank=True,
)
distance_covered = models.PositiveIntegerField(
verbose_name=_("distance covered"),
blank=True,
null=True,
)
object_owner_residence = models.CharField(
verbose_name=_("object owner residence"),
max_length=255,
blank=True,
null=True,
)
car_position = models.ForeignKey(
'evaluation.ReferenceitemModel',
verbose_name=_("car position"),
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='evaluation_mechanic_auto_car_position',
)
body_type = models.ForeignKey(
'evaluation.ReferenceitemModel',
verbose_name=_("body type"),
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='evaluation_mechanic_auto_body_type',
)
fuel_type = models.ForeignKey(
'evaluation.ReferenceitemModel',
verbose_name=_("fuel type"),
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='evaluation_mechanic_auto_fuel_type',
)
state_car = models.ForeignKey(
'evaluation.ReferenceitemModel',
verbose_name=_("state car"),
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='evaluation_mechanic_auto_state_car',
)
assessment_task_type = models.ForeignKey(
'evaluation.ReferenceitemModel',
verbose_name=_("assessment task type"),
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='evaluation_mechanic_auto_assessment_task_type',
)
# ── Step 1 — Umumiy ma'lumotlar ──────────────────────────────────
registration_number = models.CharField(
verbose_name=_("registration number"),
max_length=50,
blank=True,
null=True,
)
contract_date = models.DateField(
verbose_name=_("contract date"),
blank=True,
null=True,
)
object_inspection_date = models.DateField(
verbose_name=_("object inspection date"),
blank=True,
null=True,
)
rate_date = models.DateField(
verbose_name=_("rate date"),
blank=True,
null=True,
)
rate_report_date = models.DateField(
verbose_name=_("rate report date"),
blank=True,
null=True,
)
object_type = models.CharField(
verbose_name=_("object type"),
max_length=50,
choices=AutoObjectType.choices,
blank=True,
null=True,
)
# ── Step 2 — Shaxs ma'lumotlari ─────────────────────────────────
object_owner_type = models.IntegerField(
verbose_name=_("object owner type"),
choices=ObjectOwnerType.choices,
blank=True,
null=True,
)
object_owner_individual_person_f_name = models.CharField(
verbose_name=_("owner first name"),
max_length=100,
blank=True,
null=True,
)
object_owner_individual_person_l_name = models.CharField(
verbose_name=_("owner last name"),
max_length=100,
blank=True,
null=True,
)
object_owner_individual_person_p_name = models.CharField(
verbose_name=_("owner patronymic"),
max_length=100,
blank=True,
null=True,
)
object_owner_individual_person_passport_num = models.CharField(
verbose_name=_("owner passport number"),
max_length=20,
blank=True,
null=True,
)
object_owner_legal_entity = models.CharField(
verbose_name=_("legal entity name"),
max_length=255,
blank=True,
null=True,
)
object_owner_legal_inn = models.CharField(
verbose_name=_("legal entity INN"),
max_length=20,
blank=True,
null=True,
)
value_determined = models.ForeignKey(
'evaluation.ReferenceitemModel',
verbose_name=_("value determined"),
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='evaluation_mechanic_auto_value_determined'
)
rate_type = models.ForeignKey(
'evaluation.ReferenceitemModel',
verbose_name=_("rate type"),
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='evaluation_mechanic_auto_rate_type'
)
# ── Step 4 — Avtomobil ma'lumotlari ─────────────────────────────
tex_passport_serie_num = models.CharField(
verbose_name=_("tech passport series and number"),
max_length=20,
blank=True,
null=True,
)
tex_passport_gived_date = models.DateField(
verbose_name=_("tech passport given date"),
blank=True,
null=True,
)
tex_passport_gived_location = models.CharField(
verbose_name=_("tech passport given location"),
max_length=255,
blank=True,
null=True,
)
car_wheel = models.IntegerField(
verbose_name=_("car wheel"),
choices=AutoCarWheel.choices,
blank=True,
null=True,
)
car_brand = models.CharField(
verbose_name=_("car brand"),
max_length=100,
blank=True,
null=True,
)
car_model = models.CharField(
verbose_name=_("car model"),
max_length=100,
blank=True,
null=True,
)
car_number = models.CharField(
verbose_name=_("car number"),
max_length=20,
blank=True,
null=True,
)
manufacture_year = models.CharField(
verbose_name=_("manufacture year"),
max_length=10,
blank=True,
null=True,
)
car_dvigatel_number = models.CharField(
verbose_name=_("engine number"),
max_length=50,
blank=True,
null=True,
)
car_color = models.CharField(
verbose_name=_("car color"),
max_length=50,
blank=True,
null=True,
)
# ── Natija ───────────────────────────────────────────────────────
rating_goal = models.CharField(
verbose_name=_("rating goal"),
max_length=50,
blank=True,
null=True,
)
status = models.CharField(
verbose_name=_("status"),
max_length=50,
choices=AutoEvaluationStatus.choices,
default=AutoEvaluationStatus.CREATED,
)
is_archived = models.BooleanField(
verbose_name=_("is archived"),
default=False,
)
def __str__(self):
return f"Mechanic Auto Evaluation {self.registration_number or self.pk}"
@classmethod
def _baker(cls):
return baker.make(cls)
class Meta:
db_table = "MechanicAutoEvaluation"
verbose_name = _("Mechanic Auto Evaluation")
verbose_name_plural = _("Mechanic Auto Evaluations")
class MechanicAutoEvaluationTexPassportFile(AbstractBaseModel):
mechanic_auto_evaluation = models.ForeignKey(
MechanicAutoEvaluationModel,
on_delete=models.CASCADE,
related_name="tex_passport_files",
)
file = models.FileField(
verbose_name=_("tech passport file"),
upload_to="mechanic_evaluation/tech_passports/%Y/%m/",
)
def __str__(self):
return f"Tex passport file for MechanicAutoEvaluation #{self.mechanic_auto_evaluation_id}"
@classmethod
def _baker(cls):
return baker.make(cls)
class Meta:
db_table = "MechanicAutoEvaluationTexPassportFile"
verbose_name = _("Mechanic Auto Evaluation Tex Passport File")
verbose_name_plural = _("Mechanic Auto Evaluation Tex Passport Files")

View File

@@ -3,12 +3,11 @@ from django.utils.translation import gettext_lazy as _
from django_core.models import AbstractBaseModel from django_core.models import AbstractBaseModel
from model_bakery import baker from model_bakery import baker
from .valuation import ValuationModel
from core.apps.evaluation.choices.movable import ( from core.apps.evaluation.choices.movable import (
MovablePropertyCategory, MovablePropertyCategory,
MovablePropertyCondition, MovablePropertyCondition,
) )
from .valuation import ValuationModel
class MovablePropertyEvaluationModel(AbstractBaseModel): class MovablePropertyEvaluationModel(AbstractBaseModel):
@@ -51,4 +50,3 @@ class MovablePropertyEvaluationModel(AbstractBaseModel):
db_table = "MovablePropertyEvaluation" db_table = "MovablePropertyEvaluation"
verbose_name = _("Movable Property Evaluation") verbose_name = _("Movable Property Evaluation")
verbose_name_plural = _("Movable Property Evaluations") verbose_name_plural = _("Movable Property Evaluations")

View File

@@ -8,43 +8,41 @@ from core.apps.evaluation.choices.request import (
EvaluationRateType, EvaluationRateType,
RequestObjectType, RequestObjectType,
RequestStatus, RequestStatus,
RequestPersonType,
) )
from core.apps.evaluation.models import ReferenceitemModel from core.apps.evaluation.models import ReferenceitemModel
class EvaluationrequestModel(AbstractBaseModel): class EvaluationrequestModel(AbstractBaseModel):
rate_type = models.CharField(max_length=50,choices=EvaluationRateType.choices)
object_type = models.CharField(max_length=50,choices=RequestObjectType.choices,blank=True,null=True)
status = models.CharField(max_length=50, choices=RequestStatus.choices, default=RequestStatus.PENDING)
distance_covered = models.FloatField(default=0.0, null=True, blank=True)
worked_hours = models.IntegerField(blank=True,null=True)
customer_inn_number = models.CharField(max_length=20)
owner_inn_number = models.CharField(max_length=20)
tex_passport = models.CharField(max_length=20,blank=True,null=True)
chassi = models.CharField(max_length=100,blank=True,null=True)
gov_number = models.CharField(max_length=100, null=True, blank=True)
location_name = models.CharField(max_length=255,blank=True,null=True)
location_lat = models.DecimalField(max_digits=9,decimal_places=6,blank=True, null=True)
location_lng = models.DecimalField(max_digits=9,decimal_places=6,blank=True,null=True)
need_delivering = models.BooleanField(default=True)
is_archive = models.BooleanField(default=False)
customer_and_owner_same = models.BooleanField(default=False)
###################
# Foreign Keys
###################
user = models.ForeignKey( user = models.ForeignKey(
"accounts.User", "accounts.User",
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name="evaluation_requests", related_name="evaluation_requests",
verbose_name=_("user"), verbose_name=_("user"),
) )
rate_type = models.CharField(
verbose_name=_("rate type"),
max_length=50,
choices=EvaluationRateType.choices,
)
object_type = models.CharField(
verbose_name=_("object type"),
max_length=50,
choices=RequestObjectType.choices,
blank=True,
null=True,
)
customer_inn_number = models.CharField(
verbose_name=_("customer INN number"),
max_length=20,
)
owner_inn_number = models.CharField(
verbose_name=_("owner INN number"),
max_length=20,
)
tex_passport = models.CharField(
verbose_name=_("tex passport"),
max_length=20,
blank=True,
null=True,
)
value_determined = models.ForeignKey( value_determined = models.ForeignKey(
"evaluation.ReferenceitemModel", "evaluation.ReferenceitemModel",
verbose_name=_("value determined"), verbose_name=_("value determined"),
@@ -77,51 +75,8 @@ class EvaluationrequestModel(AbstractBaseModel):
blank=True, blank=True,
null=True, null=True,
) )
worked_hours = models.IntegerField(
verbose_name=_("worked hours"),
blank=True,
null=True,
)
chassi = models.CharField(
verbose_name=_("chassi"),
max_length=100,
blank=True,
null=True,
)
need_delivering = models.BooleanField(
verbose_name=_("need delivering"),
default=True,
)
location_name = models.CharField(
verbose_name=_("location name"),
max_length=255,
blank=True,
null=True,
)
location_lat = models.DecimalField(
verbose_name=_("location latitude"),
max_digits=9,
decimal_places=6,
blank=True,
null=True,
)
location_lng = models.DecimalField(
verbose_name=_("location longitude"),
max_digits=9,
decimal_places=6,
blank=True,
null=True,
)
status = models.CharField(
verbose_name=_("status"),
max_length=50,
choices=RequestStatus.choices,
default=RequestStatus.PENDING,
)
is_archive = models.BooleanField(
verbose_name=_("is archive"),
default=False,
)
def __str__(self): def __str__(self):
return f"Requests #{self.pk}{self.get_rate_type_display()}" return f"Requests #{self.pk}{self.get_rate_type_display()}"
@@ -147,3 +102,29 @@ class EvaluationrequestModel(AbstractBaseModel):
db_table = "EvaluationRequest" db_table = "EvaluationRequest"
verbose_name = _("Evaluation Request") verbose_name = _("Evaluation Request")
verbose_name_plural = _("Evaluation Requests") verbose_name_plural = _("Evaluation Requests")
class EvaluationRequestOwnerModel(AbstractBaseModel):
evaluation_request = models.OneToOneField(EvaluationrequestModel, on_delete=models.CASCADE, related_name='owner')
type = models.CharField(max_length=100, choices=RequestPersonType.choices)
jshshir = models.CharField(max_length=100)
def __str__(self):
return f"Owner #{self.pk}{self.type}"
class Meta:
verbose_name = _("Evaluation Request Owner")
verbose_name_plural = _("Evaluation Request Owners")
class EvaluationRequestCustomerModel(AbstractBaseModel):
evaluation_request = models.OneToOneField(EvaluationrequestModel, on_delete=models.CASCADE, related_name='customer')
type = models.CharField(max_length=100, choices=RequestPersonType.choices)
jshshir = models.CharField(max_length=100)
def __str__(self):
return f"Customer #{self.pk}{self.type}"
class Meta:
verbose_name = _("Evaluation Request Customer")
verbose_name_plural = _("Evaluation Request Customers")

View File

@@ -3,13 +3,36 @@ import re
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from rest_framework import serializers from rest_framework import serializers
from django.db import transaction
from core.apps.evaluation.choices.request import RequestStatus from core.apps.evaluation.choices.request import RequestStatus
from core.apps.evaluation.models import AutoEvaluationModel, ReferenceitemModel, EvaluationrequestModel from core.apps.evaluation.models import (
AutoEvaluationModel,
AutoEvaluationTexPassportFile,
ReferenceitemModel,
EvaluationrequestModel,
)
from core.apps.evaluation.serializers.reference import ListReferenceitemSerializer from core.apps.evaluation.serializers.reference import ListReferenceitemSerializer
User = get_user_model() User = get_user_model()
class AutoEvaluationTexPassportFileSerializer(serializers.ModelSerializer):
file = serializers.SerializerMethodField()
class Meta:
model = AutoEvaluationTexPassportFile
fields = ["id", "file"]
def get_file(self, obj):
request = self.context.get("request")
if not obj.file:
return None
if request:
return request.build_absolute_uri(obj.file.url)
return obj.file.url
class BaseAutoevaluationSerializer(serializers.ModelSerializer): class BaseAutoevaluationSerializer(serializers.ModelSerializer):
status_display = serializers.CharField(source="get_status_display", read_only=True) status_display = serializers.CharField(source="get_status_display", read_only=True)
object_type_display = serializers.CharField(source="get_object_type_display", read_only=True, default=None) object_type_display = serializers.CharField(source="get_object_type_display", read_only=True, default=None)
@@ -17,8 +40,12 @@ class BaseAutoevaluationSerializer(serializers.ModelSerializer):
default=None) default=None)
rate_type = ListReferenceitemSerializer(read_only=True) rate_type = ListReferenceitemSerializer(read_only=True)
value_determined = ListReferenceitemSerializer(read_only=True) value_determined = ListReferenceitemSerializer(read_only=True)
property_rights = ListReferenceitemSerializer(read_only=True) car_position = ListReferenceitemSerializer(read_only=True)
form_ownership = ListReferenceitemSerializer(read_only=True) body_type = ListReferenceitemSerializer(read_only=True)
fuel_type = ListReferenceitemSerializer(read_only=True)
state_car = ListReferenceitemSerializer(read_only=True)
assessment_task_type = ListReferenceitemSerializer(read_only=True)
tex_passport_files = AutoEvaluationTexPassportFileSerializer(many=True, read_only=True)
user = serializers.SerializerMethodField(method_name="get_user", read_only=True) user = serializers.SerializerMethodField(method_name="get_user", read_only=True)
class Meta: class Meta:
@@ -34,7 +61,9 @@ class BaseAutoevaluationSerializer(serializers.ModelSerializer):
"object_owner_individual_person_p_name", "object_owner_individual_person_p_name",
"object_owner_legal_entity", "object_owner_legal_entity",
"object_owner_legal_inn", "object_owner_legal_inn",
"object_owner_residence",
"tex_passport_serie_num", "tex_passport_serie_num",
"tex_passport_files",
"rating_goal", "rating_goal",
"registration_number", "registration_number",
"object_type", "object_type",
@@ -44,13 +73,17 @@ class BaseAutoevaluationSerializer(serializers.ModelSerializer):
"car_number", "car_number",
"manufacture_year", "manufacture_year",
"car_color", "car_color",
"distance_covered",
"car_position",
"body_type",
"fuel_type",
"state_car",
"assessment_task_type",
"status", "status",
"status_display", "status_display",
"created_at", "created_at",
"value_determined", "value_determined",
"rate_type", "rate_type",
"property_rights",
"form_ownership",
"user", "user",
"evaluation_request", "evaluation_request",
] ]
@@ -72,16 +105,8 @@ class ListAutoevaluationSerializer(BaseAutoevaluationSerializer):
class RetrieveAutoevaluationSerializer(BaseAutoevaluationSerializer): class RetrieveAutoevaluationSerializer(BaseAutoevaluationSerializer):
car_type_display = serializers.CharField(source="get_car_type_display", read_only=True, default=None)
car_wheel_display = serializers.CharField(source="get_car_wheel_display", read_only=True, default=None) car_wheel_display = serializers.CharField(source="get_car_wheel_display", read_only=True, default=None)
# object_location_highways_display = serializers.CharField(
# source="get_object_location_highways_display", read_only=True, default=None
# )
# object_location_covenience_display = serializers.CharField(
# source="get_object_location_covenience_display", read_only=True, default=None
# )
class Meta(BaseAutoevaluationSerializer.Meta): class Meta(BaseAutoevaluationSerializer.Meta):
fields = BaseAutoevaluationSerializer.Meta.fields + [ fields = BaseAutoevaluationSerializer.Meta.fields + [
# Step 1 # Step 1
@@ -89,7 +114,6 @@ class RetrieveAutoevaluationSerializer(BaseAutoevaluationSerializer):
"object_inspection_date", "object_inspection_date",
"rate_date", "rate_date",
"rate_report_date", "rate_report_date",
"rate_object_name",
# Step 2 # Step 2
"object_owner_type", "object_owner_type",
"object_owner_type_display", "object_owner_type_display",
@@ -101,11 +125,8 @@ class RetrieveAutoevaluationSerializer(BaseAutoevaluationSerializer):
"object_owner_legal_inn", "object_owner_legal_inn",
# Step 4 # Step 4
"tex_passport_serie_num", "tex_passport_serie_num",
"tex_passport_file",
"tex_passport_gived_date", "tex_passport_gived_date",
"tex_passport_gived_location", "tex_passport_gived_location",
"car_type",
"car_type_display",
"car_wheel", "car_wheel",
"car_wheel_display", "car_wheel_display",
"car_dvigatel_number", "car_dvigatel_number",
@@ -118,21 +139,6 @@ class RetrieveAutoevaluationSerializer(BaseAutoevaluationSerializer):
class UpdateAutoevaluationSerializer(serializers.ModelSerializer): class UpdateAutoevaluationSerializer(serializers.ModelSerializer):
property_rights = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
value_determined = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
form_ownership = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
value_determined = serializers.PrimaryKeyRelatedField( value_determined = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(), queryset=ReferenceitemModel.objects.all(),
required=False, required=False,
@@ -143,6 +149,36 @@ class UpdateAutoevaluationSerializer(serializers.ModelSerializer):
required=False, required=False,
allow_null=True, allow_null=True,
) )
car_position = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
body_type = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
fuel_type = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
state_car = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
assessment_task_type = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
tex_passport_files = serializers.ListField(
child=serializers.FileField(),
required=False,
write_only=True,
)
class Meta: class Meta:
model = AutoEvaluationModel model = AutoEvaluationModel
@@ -153,7 +189,6 @@ class UpdateAutoevaluationSerializer(serializers.ModelSerializer):
"object_inspection_date", "object_inspection_date",
"rate_date", "rate_date",
"rate_report_date", "rate_report_date",
"rate_object_name",
"object_type", "object_type",
# Step 2 # Step 2
"object_owner_type", "object_owner_type",
@@ -163,16 +198,15 @@ class UpdateAutoevaluationSerializer(serializers.ModelSerializer):
"object_owner_individual_person_passport_num", "object_owner_individual_person_passport_num",
"object_owner_legal_entity", "object_owner_legal_entity",
"object_owner_legal_inn", "object_owner_legal_inn",
"property_rights", "object_owner_residence",
"form_ownership",
"value_determined", "value_determined",
"rate_type", "rate_type",
"assessment_task_type",
# Step 4 # Step 4
"tex_passport_file", "tex_passport_files",
"tex_passport_serie_num", "tex_passport_serie_num",
"tex_passport_gived_date", "tex_passport_gived_date",
"tex_passport_gived_location", "tex_passport_gived_location",
"car_type",
"car_wheel", "car_wheel",
"car_brand", "car_brand",
"car_model", "car_model",
@@ -180,6 +214,11 @@ class UpdateAutoevaluationSerializer(serializers.ModelSerializer):
"manufacture_year", "manufacture_year",
"car_dvigatel_number", "car_dvigatel_number",
"car_color", "car_color",
"distance_covered",
"car_position",
"body_type",
"fuel_type",
"state_car",
] ]
def validate_tex_passport_serie_num(self, value): def validate_tex_passport_serie_num(self, value):
@@ -224,23 +263,23 @@ class UpdateAutoevaluationSerializer(serializers.ModelSerializer):
return attrs return attrs
def update(self, instance, validated_data):
files = validated_data.pop("tex_passport_files", None)
with transaction.atomic():
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
if files is not None:
AutoEvaluationTexPassportFile.objects.bulk_create([
AutoEvaluationTexPassportFile(auto_evaluation=instance, file=f) for f in files
])
return instance
def to_representation(self, instance):
return RetrieveAutoevaluationSerializer(instance, context=self.context).data
class CreateAutoevaluationSerializer(serializers.ModelSerializer): class CreateAutoevaluationSerializer(serializers.ModelSerializer):
property_rights = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
value_determined = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
form_ownership = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
value_determined = serializers.PrimaryKeyRelatedField( value_determined = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(), queryset=ReferenceitemModel.objects.all(),
required=False, required=False,
@@ -251,11 +290,41 @@ class CreateAutoevaluationSerializer(serializers.ModelSerializer):
required=False, required=False,
allow_null=True, allow_null=True,
) )
car_position = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
body_type = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
fuel_type = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
state_car = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
assessment_task_type = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
evaluation_request = serializers.PrimaryKeyRelatedField( evaluation_request = serializers.PrimaryKeyRelatedField(
queryset=EvaluationrequestModel.objects.all(), queryset=EvaluationrequestModel.objects.all(),
required=False, required=False,
allow_null=True, allow_null=True,
) )
tex_passport_files = serializers.ListField(
child=serializers.FileField(),
required=False,
write_only=True,
)
class Meta: class Meta:
model = AutoEvaluationModel model = AutoEvaluationModel
@@ -267,7 +336,6 @@ class CreateAutoevaluationSerializer(serializers.ModelSerializer):
"object_inspection_date", "object_inspection_date",
"rate_date", "rate_date",
"rate_report_date", "rate_report_date",
"rate_object_name",
"object_type", "object_type",
# Step 2 # Step 2
"object_owner_type", "object_owner_type",
@@ -277,16 +345,15 @@ class CreateAutoevaluationSerializer(serializers.ModelSerializer):
"object_owner_individual_person_passport_num", "object_owner_individual_person_passport_num",
"object_owner_legal_entity", "object_owner_legal_entity",
"object_owner_legal_inn", "object_owner_legal_inn",
"property_rights", "object_owner_residence",
"form_ownership",
"value_determined", "value_determined",
"rate_type", "rate_type",
"assessment_task_type",
# Step 4 # Step 4
"tex_passport_serie_num", "tex_passport_serie_num",
"tex_passport_file", "tex_passport_files",
"tex_passport_gived_date", "tex_passport_gived_date",
"tex_passport_gived_location", "tex_passport_gived_location",
"car_type",
"car_wheel", "car_wheel",
"car_brand", "car_brand",
"car_model", "car_model",
@@ -294,6 +361,11 @@ class CreateAutoevaluationSerializer(serializers.ModelSerializer):
"manufacture_year", "manufacture_year",
"car_dvigatel_number", "car_dvigatel_number",
"car_color", "car_color",
"distance_covered",
"car_position",
"body_type",
"fuel_type",
"state_car",
] ]
def validate_tex_passport_serie_num(self, value): def validate_tex_passport_serie_num(self, value):
@@ -338,13 +410,23 @@ class CreateAutoevaluationSerializer(serializers.ModelSerializer):
return attrs return attrs
def create(self, validated_data): def create(self, validated_data):
files = validated_data.pop("tex_passport_files", [])
user = self.context.get('request').user user = self.context.get('request').user
validated_data['user'] = user validated_data['user'] = user
evaluation_req = validated_data.get("evaluation_request") evaluation_req = validated_data.get("evaluation_request")
if evaluation_req: if evaluation_req:
evaluation_req.status = RequestStatus.IN_PROGRESS evaluation_req.status = RequestStatus.IN_PROGRESS
evaluation_req.save() evaluation_req.save()
return super().create(validated_data) with transaction.atomic():
instance = super().create(validated_data)
if files:
AutoEvaluationTexPassportFile.objects.bulk_create([
AutoEvaluationTexPassportFile(auto_evaluation=instance, file=f) for f in files
])
return instance
def to_representation(self, instance):
return RetrieveAutoevaluationSerializer(instance, context=self.context).data
class AutoEvaluationAppraisersSerializer(serializers.Serializer): class AutoEvaluationAppraisersSerializer(serializers.Serializer):
@@ -359,6 +441,7 @@ class AutoEvaluationAppraisersSerializer(serializers.Serializer):
data['users'] = users data['users'] = users
return data return data
class AutoEvaluationSerializer(serializers.Serializer): class AutoEvaluationSerializer(serializers.Serializer):
brand = serializers.CharField() brand = serializers.CharField()
brand_model = serializers.CharField() brand_model = serializers.CharField()
@@ -368,3 +451,64 @@ class AutoEvaluationSerializer(serializers.Serializer):
condition = serializers.CharField() condition = serializers.CharField()
fuel_type = serializers.CharField() fuel_type = serializers.CharField()
mileage = serializers.CharField() mileage = serializers.CharField()
class AutoEvaluationModelSerializer(serializers.ModelSerializer):
user = serializers.StringRelatedField(read_only=True)
appraisers = serializers.PrimaryKeyRelatedField(
many=True,
queryset=User.objects.all(),
required=False
)
class Meta:
model = AutoEvaluationModel
fields = (
"registration_number",
"contract_date",
"object_inspection_date",
"rate_date",
"rate_report_date",
"object_type",
"object_owner_type",
"object_owner_individual_person_f_name",
"object_owner_individual_person_l_name",
"object_owner_individual_person_p_name",
"object_owner_individual_person_passport_num",
"object_owner_legal_entity",
"object_owner_legal_inn",
"object_owner_residence",
"value_determined",
"rate_type",
"assessment_task_type",
"tex_passport_serie_num",
"tex_passport_gived_date",
"tex_passport_gived_location",
"car_wheel",
"car_brand",
"car_model",
"car_number",
"manufacture_year",
"car_dvigatel_number",
"car_color",
"distance_covered",
"car_position",
"body_type",
"fuel_type",
"state_car",
"rating_goal",
"status",
"is_archived",
"created_at",
"updated_at",
)
read_only_fields = (
"id",
"created_at",
"updated_at",
)

View File

@@ -0,0 +1,501 @@
import re
from django.contrib.auth import get_user_model
from rest_framework import serializers
from django.db import transaction
from core.apps.evaluation.choices.request import RequestStatus
from core.apps.evaluation.models import (
MechanicAutoEvaluationModel,
MechanicAutoEvaluationTexPassportFile,
ReferenceitemModel,
EvaluationrequestModel,
)
from core.apps.evaluation.serializers.reference import ListReferenceitemSerializer
User = get_user_model()
class MechanicAutoEvaluationTexPassportFileSerializer(serializers.ModelSerializer):
file = serializers.SerializerMethodField()
class Meta:
model = MechanicAutoEvaluationTexPassportFile
fields = ["id", "file"]
def get_file(self, obj):
request = self.context.get("request")
if not obj.file:
return None
if request:
return request.build_absolute_uri(obj.file.url)
return obj.file.url
class BaseMechanicAutoevaluationSerializer(serializers.ModelSerializer):
status_display = serializers.CharField(source="get_status_display", read_only=True)
object_type_display = serializers.CharField(source="get_object_type_display", read_only=True, default=None)
object_owner_type_display = serializers.CharField(source="get_object_owner_type_display", read_only=True,
default=None)
rate_type = ListReferenceitemSerializer(read_only=True)
value_determined = ListReferenceitemSerializer(read_only=True)
car_position = ListReferenceitemSerializer(read_only=True)
body_type = ListReferenceitemSerializer(read_only=True)
fuel_type = ListReferenceitemSerializer(read_only=True)
state_car = ListReferenceitemSerializer(read_only=True)
assessment_task_type = ListReferenceitemSerializer(read_only=True)
tex_passport_files = MechanicAutoEvaluationTexPassportFileSerializer(many=True, read_only=True)
user = serializers.SerializerMethodField(method_name="get_user", read_only=True)
class Meta:
model = MechanicAutoEvaluationModel
fields = [
"id",
"contract_date",
"object_inspection_date",
"object_owner_individual_person_passport_num",
"object_owner_type",
"object_owner_individual_person_f_name",
"object_owner_individual_person_l_name",
"object_owner_individual_person_p_name",
"object_owner_legal_entity",
"object_owner_legal_inn",
"object_owner_residence",
"tex_passport_serie_num",
"tex_passport_files",
"rating_goal",
"registration_number",
"object_type",
"object_type_display",
"car_brand",
"car_model",
"car_number",
"manufacture_year",
"car_color",
"distance_covered",
"car_position",
"body_type",
"fuel_type",
"state_car",
"assessment_task_type",
"status",
"status_display",
"created_at",
"value_determined",
"rate_type",
"user",
"evaluation_request",
]
def get_user(self, obj):
request = self.context.get('request')
return {
"id": obj.user.id,
"phone": obj.user.phone,
"first_name": obj.user.first_name,
"last_name": obj.user.last_name,
"avatar": request.build_absolute_uri(obj.user.avatar.url) if obj.user.avatar else None
} if obj.user else None
class ListMechanicAutoevaluationSerializer(BaseMechanicAutoevaluationSerializer):
class Meta(BaseMechanicAutoevaluationSerializer.Meta):
pass
class RetrieveMechanicAutoevaluationSerializer(BaseMechanicAutoevaluationSerializer):
car_wheel_display = serializers.CharField(source="get_car_wheel_display", read_only=True, default=None)
class Meta(BaseMechanicAutoevaluationSerializer.Meta):
fields = BaseMechanicAutoevaluationSerializer.Meta.fields + [
"contract_date",
"object_inspection_date",
"rate_date",
"rate_report_date",
"object_owner_type",
"object_owner_type_display",
"object_owner_individual_person_f_name",
"object_owner_individual_person_l_name",
"object_owner_individual_person_p_name",
"object_owner_individual_person_passport_num",
"object_owner_legal_entity",
"object_owner_legal_inn",
"tex_passport_serie_num",
"tex_passport_gived_date",
"tex_passport_gived_location",
"car_wheel",
"car_wheel_display",
"car_dvigatel_number",
"valuation",
"vehicle",
"rating_goal",
"updated_at",
]
class UpdateMechanicAutoevaluationSerializer(serializers.ModelSerializer):
value_determined = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
rate_type = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
car_position = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
body_type = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
fuel_type = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
state_car = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
assessment_task_type = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
user = serializers.PrimaryKeyRelatedField(
queryset=User.objects.all(),
required=False,
allow_null=True,
)
tex_passport_files = serializers.ListField(
child=serializers.FileField(),
required=False,
write_only=True,
)
class Meta:
model = MechanicAutoEvaluationModel
fields = [
"user",
"registration_number",
"contract_date",
"object_inspection_date",
"rate_date",
"rate_report_date",
"object_type",
"object_owner_type",
"object_owner_individual_person_f_name",
"object_owner_individual_person_l_name",
"object_owner_individual_person_p_name",
"object_owner_individual_person_passport_num",
"object_owner_legal_entity",
"object_owner_legal_inn",
"object_owner_residence",
"value_determined",
"rate_type",
"assessment_task_type",
"tex_passport_files",
"tex_passport_serie_num",
"tex_passport_gived_date",
"tex_passport_gived_location",
"car_wheel",
"car_brand",
"car_model",
"car_number",
"manufacture_year",
"car_dvigatel_number",
"car_color",
"distance_covered",
"car_position",
"body_type",
"fuel_type",
"state_car",
]
def validate_tex_passport_serie_num(self, value):
if value and not re.match(r"^[A-Z]{3}\s?\d{7}$", value):
raise serializers.ValidationError(
"Format: AAA 1234567 (3 harf + 7 raqam)"
)
return value
def validate_object_owner_individual_person_passport_num(self, value):
if value and not re.match(r"^[A-Z]{2}\s?\d{7}$", value):
raise serializers.ValidationError(
"Format: AA 1234567 (2 harf + 7 raqam)"
)
return value
def validate(self, attrs):
owner_type = attrs.get("object_owner_type")
if owner_type == 1:
required_fields = {
"object_owner_individual_person_f_name": "Ismi",
"object_owner_individual_person_l_name": "Familiyasi",
"object_owner_individual_person_p_name": "Sharifi",
"object_owner_individual_person_passport_num": "Passport raqami",
}
for field, label in required_fields.items():
if not attrs.get(field):
raise serializers.ValidationError(
{field: f"Jismoniy shaxs uchun {label} majburiy."}
)
elif owner_type == 2:
if not attrs.get("object_owner_legal_entity"):
raise serializers.ValidationError(
{"object_owner_legal_entity": "Yuridik shaxs nomi majburiy."}
)
if not attrs.get("object_owner_legal_inn"):
raise serializers.ValidationError(
{"object_owner_legal_inn": "INN raqami majburiy."}
)
return attrs
def update(self, instance, validated_data):
files = validated_data.pop("tex_passport_files", None)
with transaction.atomic():
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
if files is not None:
MechanicAutoEvaluationTexPassportFile.objects.bulk_create([
MechanicAutoEvaluationTexPassportFile(mechanic_auto_evaluation=instance, file=f) for f in files
])
return instance
def to_representation(self, instance):
return RetrieveMechanicAutoevaluationSerializer(instance, context=self.context).data
class CreateMechanicAutoevaluationSerializer(serializers.ModelSerializer):
value_determined = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
rate_type = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
car_position = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
body_type = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
fuel_type = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
state_car = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
assessment_task_type = serializers.PrimaryKeyRelatedField(
queryset=ReferenceitemModel.objects.all(),
required=False,
allow_null=True,
)
evaluation_request = serializers.PrimaryKeyRelatedField(
queryset=EvaluationrequestModel.objects.all(),
required=False,
allow_null=True,
)
user = serializers.PrimaryKeyRelatedField(
queryset=User.objects.all(),
required=True,
allow_null=False,
)
tex_passport_files = serializers.ListField(
child=serializers.FileField(),
required=False,
write_only=True,
)
class Meta:
model = MechanicAutoEvaluationModel
fields = [
"user",
"registration_number",
"evaluation_request",
"contract_date",
"object_inspection_date",
"rate_date",
"rate_report_date",
"object_type",
"object_owner_type",
"object_owner_individual_person_f_name",
"object_owner_individual_person_l_name",
"object_owner_individual_person_p_name",
"object_owner_individual_person_passport_num",
"object_owner_legal_entity",
"object_owner_legal_inn",
"object_owner_residence",
"value_determined",
"rate_type",
"assessment_task_type",
"tex_passport_serie_num",
"tex_passport_files",
"tex_passport_gived_date",
"tex_passport_gived_location",
"car_wheel",
"car_brand",
"car_model",
"car_number",
"manufacture_year",
"car_dvigatel_number",
"car_color",
"distance_covered",
"car_position",
"body_type",
"fuel_type",
"state_car",
]
def validate_tex_passport_serie_num(self, value):
if value and not re.match(r"^[A-Z]{3}\s?\d{7}$", value):
raise serializers.ValidationError(
"Format: AAA 1234567 (3 harf + 7 raqam)"
)
return value
def validate_object_owner_individual_person_passport_num(self, value):
if value and not re.match(r"^[A-Z]{2}\s?\d{7}$", value):
raise serializers.ValidationError(
"Format: AA 1234567 (2 harf + 7 raqam)"
)
return value
def validate(self, attrs):
owner_type = attrs.get("object_owner_type")
if owner_type == 1:
required_fields = {
"object_owner_individual_person_f_name": "Ismi",
"object_owner_individual_person_l_name": "Familiyasi",
"object_owner_individual_person_p_name": "Sharifi",
"object_owner_individual_person_passport_num": "Passport raqami",
}
for field, label in required_fields.items():
if not attrs.get(field):
raise serializers.ValidationError(
{field: f"Jismoniy shaxs uchun {label} majburiy."}
)
elif owner_type == 2:
if not attrs.get("object_owner_legal_entity"):
raise serializers.ValidationError(
{"object_owner_legal_entity": "Yuridik shaxs nomi majburiy."}
)
if not attrs.get("object_owner_legal_inn"):
raise serializers.ValidationError(
{"object_owner_legal_inn": "INN raqami majburiy."}
)
return attrs
def create(self, validated_data):
files = validated_data.pop("tex_passport_files", [])
evaluation_req = validated_data.get("evaluation_request")
if evaluation_req:
evaluation_req.status = RequestStatus.IN_PROGRESS
evaluation_req.save()
with transaction.atomic():
instance = super().create(validated_data)
if files:
MechanicAutoEvaluationTexPassportFile.objects.bulk_create([
MechanicAutoEvaluationTexPassportFile(mechanic_auto_evaluation=instance, file=f) for f in files
])
return instance
def to_representation(self, instance):
return RetrieveMechanicAutoevaluationSerializer(instance, context=self.context).data
class MechanicAutoEvaluationAppraisersSerializer(serializers.Serializer):
ids = serializers.ListField(child=serializers.IntegerField())
def validate(self, data):
if not data.get("ids"):
raise serializers.ValidationError("Appraisers IDs are required.")
users = User.objects.filter(id__in=data["ids"])
if not users:
raise serializers.ValidationError("Invalid appraisers IDs.")
data['users'] = users
return data
class MechanicAutoEvaluationModelSerializer(serializers.ModelSerializer):
user = serializers.StringRelatedField(read_only=True)
appraisers = serializers.PrimaryKeyRelatedField(
many=True,
queryset=User.objects.all(),
required=False
)
class Meta:
model = MechanicAutoEvaluationModel
fields = (
"registration_number",
"contract_date",
"object_inspection_date",
"rate_date",
"rate_report_date",
"object_type",
"object_owner_type",
"object_owner_individual_person_f_name",
"object_owner_individual_person_l_name",
"object_owner_individual_person_p_name",
"object_owner_individual_person_passport_num",
"object_owner_legal_entity",
"object_owner_legal_inn",
"object_owner_residence",
"value_determined",
"rate_type",
"assessment_task_type",
"tex_passport_serie_num",
"tex_passport_gived_date",
"tex_passport_gived_location",
"car_wheel",
"car_brand",
"car_model",
"car_number",
"manufacture_year",
"car_dvigatel_number",
"car_color",
"distance_covered",
"car_position",
"body_type",
"fuel_type",
"state_car",
"rating_goal",
"status",
"is_archived",
"user",
"appraisers",
"created_at",
"updated_at",
)
read_only_fields = (
"id",
"created_at",
"updated_at",
)

View File

@@ -1 +1,2 @@
from .AutoEvaluation import * # noqa from .AutoEvaluation import * # noqa
from .MechanicAutoEvaluation import * # noqa

View File

@@ -0,0 +1,56 @@
from rest_framework import serializers
from core.apps.evaluation.models.bonus import BonusCategory, EmployeeBonus, BaseValueBonus
class BaseBonusSerializer(serializers.ModelSerializer):
class Meta:
model = BaseValueBonus
fields = ['id', 'base_price']
def create(self, validated_data):
if BaseValueBonus.objects.exists():
raise serializers.ValidationError("Base bonus already exists")
return super().create(validated_data)
class BonusCategorySerializer(serializers.ModelSerializer):
class Meta:
model = BonusCategory
fields = ['name', 'category', 'percentage']
class BonusCategoryListSerializer(serializers.ModelSerializer):
price = serializers.DecimalField(max_digits=12, decimal_places=2)
class Meta:
model = BonusCategory
fields = ['id', 'name', 'category', 'percentage' , 'price']
def get_price(self, obj):
base_obj = BaseValueBonus.objects.first()
if not base_obj:
return 0
return (base_obj.base_price * obj.percentage) / 100
class BonusEmployeeBonusSerializer(serializers.ModelSerializer):
class Meta:
model = EmployeeBonus
fields = ['user', 'bonus_type', 'percentage']
class EmployeeBonusListSerializer(serializers.ModelSerializer):
price = serializers.DecimalField(max_digits=12, decimal_places=2)
class Meta:
model = EmployeeBonus
fields = ['id', 'user', 'bonus_type', 'percentage' , 'price']
def get_price(self, obj):
base_obj = BaseValueBonus.objects.first()
if not base_obj:
return 0
return (base_obj.base_price * obj.percentage) / 100

View File

@@ -3,6 +3,7 @@ from core.apps.evaluation.models import CertificateModel
class BaseCertificateSerializer(serializers.ModelSerializer): class BaseCertificateSerializer(serializers.ModelSerializer):
file = serializers.SerializerMethodField(method_name='get_file', read_only=True)
class Meta: class Meta:
model = CertificateModel model = CertificateModel
@@ -11,3 +12,19 @@ class BaseCertificateSerializer(serializers.ModelSerializer):
"title", "title",
"file", "file",
] ]
def get_file(self, obj):
if obj.file:
request = self.context.get('request')
return request.build_absolute_uri(obj.file.url)
return None
class CreateCertificateSerializer(serializers.ModelSerializer):
class Meta:
model = CertificateModel
fields = [
"id",
"title",
"file",
]

View File

@@ -0,0 +1,39 @@
from rest_framework import serializers
from core.apps.evaluation.models import MechanicAutoevaluationhistoryModel
class BaseMechanicAutoevaluationhistorySerializer(serializers.ModelSerializer):
event_type_display = serializers.CharField(source="get_event_type_display", read_only=True)
actor = serializers.SerializerMethodField()
def get_actor(self, obj):
return {
"id": obj.actor_id,
"full_name": obj.actor_full_name,
"role": obj.actor_role,
}
class Meta:
model = MechanicAutoevaluationhistoryModel
fields = [
"id",
"event_type",
"event_type_display",
"actor",
"meta",
"created_at",
]
class ListMechanicAutoevaluationhistorySerializer(BaseMechanicAutoevaluationhistorySerializer):
class Meta(BaseMechanicAutoevaluationhistorySerializer.Meta): ...
class RetrieveMechanicAutoevaluationhistorySerializer(BaseMechanicAutoevaluationhistorySerializer):
class Meta(BaseMechanicAutoevaluationhistorySerializer.Meta):
fields = BaseMechanicAutoevaluationhistorySerializer.Meta.fields + ["mechanic_auto_evaluation"]
class CreateMechanicAutoevaluationhistorySerializer(BaseMechanicAutoevaluationhistorySerializer):
class Meta(BaseMechanicAutoevaluationhistorySerializer.Meta): ...

View File

@@ -1,2 +1,3 @@
from .AutoEvaluationHistory import * # noqa from .AutoEvaluationHistory import * # noqa
from .MechanicAutoEvaluationHistory import * # noqa
from .QuickEvaluationHistory import * # noqa from .QuickEvaluationHistory import * # noqa

View File

@@ -128,6 +128,42 @@ class CreateQuickevaluationSerializer(serializers.ModelSerializer):
return super().create(validated_data) return super().create(validated_data)
class ArchiveQuickevaluationSerializer(serializers.Serializer): class QuickEvaluationModelSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=True) class Meta:
is_archive = serializers.BooleanField(required=True) model = QuickEvaluationModel
fields = (
"id",
"created_by",
"brand",
"marka",
"car_position",
"body_type",
"color",
"fuel_type",
"state_car",
"tex_passport_serie_num",
"tech_passport_issued_date",
"tech_passport_issued_place",
"car_type",
"distance_covered",
"vin_number",
"car_number",
"car_manufactured_date",
"engine_number",
"estimated_price",
"status",
"is_archive",
"created_at",
"updated_at",
)
read_only_fields = (
"id",
"created_at",
"updated_at",
)

View File

@@ -4,8 +4,11 @@ from django.contrib.auth import get_user_model
from rest_framework import serializers from rest_framework import serializers
from core.apps.evaluation.models import EvaluationrequestModel, ReferenceitemModel from core.apps.evaluation.models import EvaluationrequestModel, ReferenceitemModel, EvaluationRequestOwnerModel, EvaluationRequestCustomerModel
from core.apps.evaluation.serializers.reference import ListReferenceitemSerializer from core.apps.evaluation.serializers.reference import ListReferenceitemSerializer
from core.apps.evaluation.serializers.request.owner import EvaluationRequestOwnerSerializer
from core.apps.evaluation.serializers.request.req_customer import EvaluationRequestCustomerSerializer
User = get_user_model() User = get_user_model()
@@ -29,6 +32,8 @@ class BaseEvaluationrequestSerializer(serializers.ModelSerializer):
property_rights = ListReferenceitemSerializer(read_only=True) property_rights = ListReferenceitemSerializer(read_only=True)
form_ownership = ListReferenceitemSerializer(read_only=True) form_ownership = ListReferenceitemSerializer(read_only=True)
user = serializers.SerializerMethodField(method_name="get_user") user = serializers.SerializerMethodField(method_name="get_user")
customer = EvaluationRequestCustomerSerializer(read_only=True)
owner = EvaluationRequestOwnerSerializer(read_only=True)
class Meta: class Meta:
model = EvaluationrequestModel model = EvaluationrequestModel
@@ -56,6 +61,8 @@ class BaseEvaluationrequestSerializer(serializers.ModelSerializer):
"created_at", "created_at",
"updated_at", "updated_at",
"is_archive", "is_archive",
"customer",
"owner",
] ]
def get_location(self, obj): def get_location(self, obj):
@@ -92,7 +99,6 @@ class RetrieveEvaluationrequestSerializer(BaseEvaluationrequestSerializer):
class CreateEvaluationrequestSerializer(serializers.ModelSerializer): class CreateEvaluationrequestSerializer(serializers.ModelSerializer):
location = serializers.DictField(required=False, allow_empty=True) location = serializers.DictField(required=False, allow_empty=True)
# locationName — string qabul qiladi, location ichida yoki tashqarida yuborsa bo'ladi
locationName = serializers.CharField( locationName = serializers.CharField(
write_only=True, write_only=True,
required=False, required=False,
@@ -100,8 +106,6 @@ class CreateEvaluationrequestSerializer(serializers.ModelSerializer):
allow_blank=True, allow_blank=True,
) )
# Faqat truck_car uchun majburiy — field darajasida optional qilib belgilaymiz,
# validate() da object_type ga qarab tekshiramiz
worked_hours = serializers.IntegerField( worked_hours = serializers.IntegerField(
required=False, required=False,
allow_null=True, allow_null=True,
@@ -116,6 +120,8 @@ class CreateEvaluationrequestSerializer(serializers.ModelSerializer):
rate_goal = serializers.PrimaryKeyRelatedField(required=False, queryset=ReferenceitemModel.objects.all()) rate_goal = serializers.PrimaryKeyRelatedField(required=False, queryset=ReferenceitemModel.objects.all())
property_rights = serializers.PrimaryKeyRelatedField(required=False, queryset=ReferenceitemModel.objects.all()) property_rights = serializers.PrimaryKeyRelatedField(required=False, queryset=ReferenceitemModel.objects.all())
form_ownership = serializers.PrimaryKeyRelatedField(required=False, queryset=ReferenceitemModel.objects.all()) form_ownership = serializers.PrimaryKeyRelatedField(required=False, queryset=ReferenceitemModel.objects.all())
customer = EvaluationRequestCustomerSerializer()
owner = EvaluationRequestOwnerSerializer()
class Meta: class Meta:
model = EvaluationrequestModel model = EvaluationrequestModel
@@ -134,6 +140,11 @@ class CreateEvaluationrequestSerializer(serializers.ModelSerializer):
"need_delivering", "need_delivering",
"location", "location",
"locationName", "locationName",
"customer",
"owner",
"customer_and_owner_same",
"distance_covered",
"gov_number"
] ]
def validate_tex_passport(self, value): def validate_tex_passport(self, value):
@@ -157,7 +168,6 @@ class CreateEvaluationrequestSerializer(serializers.ModelSerializer):
{"tex_passport": "rate_type 'auto' bo'lganda tex_passport majburiy."} {"tex_passport": "rate_type 'auto' bo'lganda tex_passport majburiy."}
) )
# worked_hours va chassi FAQAT yuk avtomobil (truck_car) uchun majburiy
if object_type == "truck_car": if object_type == "truck_car":
if attrs.get("worked_hours") is None: if attrs.get("worked_hours") is None:
raise serializers.ValidationError( raise serializers.ValidationError(
@@ -183,7 +193,31 @@ class CreateEvaluationrequestSerializer(serializers.ModelSerializer):
if location_name: if location_name:
validated_data["location_name"] = str(location_name) validated_data["location_name"] = str(location_name)
validated_data["user"] = self.context["request"].user validated_data["user"] = self.context["request"].user
return super().create(validated_data)
instance = super().create(validated_data)
customer = validated_data.pop("customer", None)
owner = validated_data.pop("owner", None)
EvaluationRequestCustomerModel.objects.create(
evaluation_request=instance,
type=customer.get("type"),
jshshir=customer.get("jshshir")
)
if not instance.customer_and_owner_same:
EvaluationRequestOwnerModel.objects.create(
evaluation_request=instance,
type=owner.get("type"),
jshshir=owner.get("jshshir")
)
else:
EvaluationRequestOwnerModel.objects.create(
evaluation_request=instance,
type=customer.get("type"),
jshshir=customer.get("jshshir")
)
return instance
class ArchiveEvaluationrequestSerializer(serializers.Serializer): class ArchiveEvaluationrequestSerializer(serializers.Serializer):
id = serializers.IntegerField(required=True) id = serializers.IntegerField(required=True)

View File

@@ -0,0 +1,9 @@
from rest_framework import serializers
from core.apps.evaluation.models.request import EvaluationRequestOwnerModel
class EvaluationRequestOwnerSerializer(serializers.ModelSerializer):
class Meta:
model = EvaluationRequestOwnerModel
fields = ["id", "evaluation_request", "type", "jshshir"]

View File

@@ -0,0 +1,9 @@
from rest_framework import serializers
from core.apps.evaluation.models.request import EvaluationRequestCustomerModel
class EvaluationRequestCustomerSerializer(serializers.ModelSerializer):
class Meta:
model = EvaluationRequestCustomerModel
fields = ["id", "evaluation_request", "type", "jshshir"]

View File

@@ -87,3 +87,29 @@ class CreateVehicleSerializer(serializers.ModelSerializer):
"condition", "condition",
"position", "position",
] ]
class VehicleApplicationSerializer(serializers.Serializer):
person_name = serializers.CharField()
property_owner = serializers.CharField(max_length=100)
address = serializers.CharField(max_length=255)
marka = serializers.CharField(max_length=100)
model = serializers.CharField(max_length=100)
configuration = serializers.CharField(max_length=100)
auto_number = serializers.CharField(max_length=100)
date_created = serializers.DateTimeField()
mileage = serializers.IntegerField()
vehicle_identification = serializers.CharField(max_length=100)
engine_number = serializers.CharField(max_length=100)
colour = serializers.CharField(max_length=100)
registration_certificate_series = serializers.CharField(max_length=100)
tec_passport_number = serializers.CharField(max_length=100)
tec_passport_date = serializers.DateTimeField()
tec_passport_place = serializers.CharField(max_length=255)
body_type = serializers.CharField(max_length=100)
chassis = serializers.CharField(max_length=100)
plate = serializers.CharField(max_length=100)
value_type = serializers.CharField(max_length=100)
evaluation_purpose = serializers.CharField(max_length=100)
personal_id_number = serializers.IntegerField()
id_number = serializers.CharField(max_length=9)

View File

@@ -1,90 +1,114 @@
from django.urls import include, path from django.urls import include, path
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from .views import ( from core.apps.evaluation import views
AdminEvaluationrequestView,
AutoEvaluationHistoryView,
AutoEvaluationView,
CustomerView,
DeterminedValueView,
DocumentCategoryView,
DocumentView,
EvaluationPurposeView,
EvaluationReportView,
EvaluationrequestView,
MovablePropertyEvaluationView,
OwnershipFormView,
PropertyOwnerView,
PropertyRightsView,
QuickEvaluationHistoryView,
QuickEvaluationView,
RealEstateEvaluationView,
ReferenceitemView,
ValuationDocumentView,
ValuationView,
VehicleView,
AutoEvaluationListAppraisersView,
AutoEvaluationSetAppraisersView,
AutoEvaluationRemoveAppraisersView,
DidoxCompanyInfoAPIView,
TechPassportAPIView,
EvaluationStatusChange,
CertificateView,
ArchiveQuickEvaluationView,
ArchiveEvaluationrequestView, GetArchivedEvaluationListAPIView, ArchivedEvaluation,
)
router = DefaultRouter() router = DefaultRouter()
router.register("document-category", DocumentCategoryView, basename="DocumentCategory") router.register("document-category", views.DocumentCategoryView, basename="DocumentCategory")
router.register("document", DocumentView, basename="Document") router.register("document", views.DocumentView, basename="Document")
router.register("auto-evaluation-history", AutoEvaluationHistoryView, basename="auto-evaluation-history") router.register("auto-evaluation-history", views.AutoEvaluationHistoryView, basename="auto-evaluation-history")
router.register("quick-evaluation-history", QuickEvaluationHistoryView, basename="quick-evaluation-history") router.register("mechanic-auto-evaluation-history", views.MechanicAutoEvaluationHistoryView, basename="mechanic-auto-evaluation-history")
router.register("determined-value", DeterminedValueView, basename="determined-value") router.register("quick-evaluation-history", views.QuickEvaluationHistoryView, basename="quick-evaluation-history")
router.register("evaluation-purpose", EvaluationPurposeView, basename="evaluation-purpose") router.register("determined-value", views.DeterminedValueView, basename="determined-value")
router.register("property-rights", PropertyRightsView, basename="property-rights") router.register("evaluation-purpose", views.EvaluationPurposeView, basename="evaluation-purpose")
router.register("ownership-form", OwnershipFormView, basename="ownership-form") router.register("property-rights", views.PropertyRightsView, basename="property-rights")
router.register("evaluation-request", EvaluationrequestView, basename="evaluation-request") router.register("ownership-form", views.OwnershipFormView, basename="ownership-form")
router.register("admin-evaluation-request", AdminEvaluationrequestView, basename="admin-evaluation-request") router.register("evaluation-request", views.EvaluationrequestView, basename="evaluation-request")
router.register("reference-item", ReferenceitemView, basename="reference-item") router.register("admin-evaluation-request", views.AdminEvaluationrequestView, basename="admin-evaluation-request")
router.register("valuation-document", ValuationDocumentView, basename="valuation-document") router.register("reference-item", views.ReferenceitemView, basename="reference-item")
router.register("evaluation-report", EvaluationReportView, basename="evaluation-report") router.register("valuation-document", views.ValuationDocumentView, basename="valuation-document")
router.register("quick-evaluation", QuickEvaluationView, basename="quick-evaluation") router.register("evaluation-report", views.EvaluationReportView, basename="evaluation-report")
router.register("movable-property-evaluation", MovablePropertyEvaluationView, basename="movable-property-evaluation") router.register("quick-evaluation", views.QuickEvaluationView, basename="quick-evaluation")
router.register("real-estate-evaluation", RealEstateEvaluationView, basename="real-estate-evaluation") router.register("movable-property-evaluation", views.MovablePropertyEvaluationView, basename="movable-property-evaluation")
router.register("auto-evaluation", AutoEvaluationView, basename="auto-evaluation") router.register("real-estate-evaluation", views.RealEstateEvaluationView, basename="real-estate-evaluation")
router.register("vehicle", VehicleView, basename="vehicle") router.register("auto-evaluation", views.AutoEvaluationView, basename="auto-evaluation")
router.register("valuation", ValuationView, basename="valuation") router.register("mechanic-auto-evaluation", views.MechanicAutoEvaluationView, basename="mechanic-auto-evaluation")
router.register("property-owner", PropertyOwnerView, basename="property-owner") router.register("vehicle", views.VehicleView, basename="vehicle")
router.register("customer", CustomerView, basename="customer") router.register("valuation", views.ValuationView, basename="valuation")
router.register("certificate", CertificateView, basename="certificate") router.register("property-owner", views.PropertyOwnerView, basename="property-owner")
router.register("customer", views.CustomerView, basename="customer")
router.register("certificate", views.CertificateView, basename="certificate")
router.register("bonus-type", views.BonusTypeView, basename="bonus-type")
router.register("bonus-employee", views.BonusEmployeeViewSet, basename="bonus-employee")
router.register("bonus-base", views.BaseBonusViewSet, basename="bonus-base")
urlpatterns = [ urlpatterns = [
path("", include(router.urls)), path("", include(router.urls)),
path("auto-evaluation/appraisers/", include(
path("didox/info/<int:tin>/", views.DidoxCompanyInfoAPIView.as_view()),
path("tech-passport/", views.TechPassportAPIView.as_view()),
# Quick Evaluation
path('quick-evaluation/', include(
[ [
path("<int:id>/list/", AutoEvaluationListAppraisersView.as_view(), name="auto-evaluation-list-appraisers"), path("admin/", views.AdminQuickEvalAPIView.as_view(), name="quick-evaluation"),
path("<int:id>/set/", AutoEvaluationSetAppraisersView.as_view(), name="auto-evaluation-set-appraisers"), path(
path("<int:id>/remove/", AutoEvaluationRemoveAppraisersView.as_view(), name="auto-evaluation-remove-appraisers"), 'archive/', include(
[
path("list/", views.QuickEvaluationArchivedListAPIView.as_view()),
path("<int:pk>/", views.QuickEvaluationArchiveAPIView.as_view()),
]
)
),
] ]
)), )),
path(
"didox/info/<int:tin>/",
DidoxCompanyInfoAPIView.as_view(),
name="didox-info"
),
path(
"tech-passport/",
TechPassportAPIView.as_view(),
name="tech-passport"
),
path("evaluation-request/<int:pk>/change-status/", EvaluationStatusChange.as_view(),
name="evaluation-change-status"),
path("archive/quick-evaluation/", ArchiveQuickEvaluationView.as_view(), name="quick-evaluation-archive"),
path("archive/evaluation-request/", ArchiveEvaluationrequestView.as_view(), name="evaluation-request-archive"),
path("archived-evaluvation/", GetArchivedEvaluationListAPIView.as_view(),
name="archived-evaluation"),
path("auto-evaluvation-change-status/<int:pk>", ArchivedEvaluation.as_view(),
name="archived-evaluation"),
# Auto Evaluation
path("auto-evaluation/", include(
[
path("admin/", views.AdminEvaluationsAPIView.as_view(), name="admin-evaluations"),
path('archive/', include(
[
path('<int:pk>/', views.AutoEvaluationArchiveAPIView.as_view()),
path('list/', views.AutoEvaluationArchivedListAPIView.as_view())
]
)),
path('appraisers/', include(
[
path("<int:id>/list/", views.AutoEvaluationListAppraisersView.as_view()),
path("<int:id>/set/", views.AutoEvaluationSetAppraisersView.as_view()),
path("<int:id>/remove/", views.AutoEvaluationRemoveAppraisersView.as_view()),
]
))
]
)),
# Mechanic Auto Evaluation
path("mechanic-auto-evaluation/", include(
[
path("admin/", views.AdminMechanicEvaluationsAPIView.as_view(), name="admin-mechanic-evaluations"),
path('archive/', include(
[
path('<int:pk>/', views.MechanicAutoEvaluationArchiveAPIView.as_view()),
path('list/', views.MechanicAutoEvaluationArchivedListAPIView.as_view())
]
)),
path('appraisers/', include(
[
path("<int:id>/list/", views.MechanicAutoEvaluationListAppraisersView.as_view()),
path("<int:id>/set/", views.MechanicAutoEvaluationSetAppraisersView.as_view()),
path("<int:id>/remove/", views.MechanicAutoEvaluationRemoveAppraisersView.as_view()),
]
))
]
)),
# Evaluation Request
path("evaluation-request/", include(
[
path("<int:pk>/change-status/", views.EvaluationStatusChange.as_view()),
path(
'archive/', include(
[
path('list/', views.RequestEvaluationArchivedListAPIView.as_view()),
path('<int:pk>/', views.RequestEvaluationArchiveAPIView.as_view()),
]
)
)
]
)),
path("calculate_avg_cost/", views.AvgCostAPIView.as_view()),
path("vehicle_document/", views.GeneratePDFView.as_view()),
] ]

View File

@@ -1,4 +1,5 @@
from .auto import * # noqa from .auto import * # noqa
from .mechanic_auto import * # noqa
from .customer import * # noqa from .customer import * # noqa
from .document import * # noqa from .document import * # noqa
from .documentcategory import * # noqa from .documentcategory import * # noqa
@@ -14,3 +15,5 @@ from .vehicle import * # noqa
from .didox import * # noqa from .didox import * # noqa
from .tech_passport import * # noqa from .tech_passport import * # noqa
from .certificate import * # noqa from .certificate import * # noqa
from .avg_cost import *
from .bonus import *

View File

@@ -3,6 +3,7 @@ from django.shortcuts import get_object_or_404
from django_core.mixins import BaseViewSetMixin from django_core.mixins import BaseViewSetMixin
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import extend_schema, OpenApiParameter from drf_spectacular.utils import extend_schema, OpenApiParameter
from rest_framework import generics
from rest_framework.filters import OrderingFilter, SearchFilter from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.generics import GenericAPIView, ListAPIView from rest_framework.generics import GenericAPIView, ListAPIView
from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.permissions import AllowAny, IsAuthenticated
@@ -10,16 +11,11 @@ from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from core.apps.accounts.permissions import IsAdminRole
from core.apps.accounts.serializers.user import UserSerializer from core.apps.accounts.serializers.user import UserSerializer
from core.apps.evaluation.filters.auto import AutoevaluationFilter from core.apps.evaluation.filters.auto import AutoevaluationFilter
from core.apps.evaluation.models import AutoEvaluationModel from core.apps.evaluation.models import AutoEvaluationModel
from core.apps.evaluation.serializers.auto import ( from core.apps.evaluation.serializers import auto as serializers, AutoEvaluationModelSerializer
CreateAutoevaluationSerializer,
ListAutoevaluationSerializer,
RetrieveAutoevaluationSerializer,
AutoEvaluationAppraisersSerializer,
UpdateAutoevaluationSerializer
)
@extend_schema(tags=["AutoEvaluation"]) @extend_schema(tags=["AutoEvaluation"])
@@ -28,8 +24,8 @@ class AutoEvaluationView(BaseViewSetMixin, ModelViewSet):
"valuation", "valuation",
"valuation__customer", "valuation__customer",
"vehicle", "vehicle",
).all() ).filter(is_archived=False)
serializer_class = ListAutoevaluationSerializer serializer_class = serializers.ListAutoevaluationSerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
@@ -67,18 +63,16 @@ class AutoEvaluationView(BaseViewSetMixin, ModelViewSet):
"created_at", "created_at",
"value_determined", "value_determined",
"rate_type", "rate_type",
"property_rights",
"form_ownership",
] ]
ordering = ["-created_at"] ordering = ["-created_at"]
action_permission_classes = {} action_permission_classes = {}
action_serializer_class = { action_serializer_class = {
"list": ListAutoevaluationSerializer, "list": serializers.ListAutoevaluationSerializer,
"retrieve": RetrieveAutoevaluationSerializer, "retrieve": serializers.RetrieveAutoevaluationSerializer,
"create": CreateAutoevaluationSerializer, "create": serializers.CreateAutoevaluationSerializer,
"update": UpdateAutoevaluationSerializer, "update": serializers.UpdateAutoevaluationSerializer,
"partial_update": UpdateAutoevaluationSerializer, "partial_update": serializers.UpdateAutoevaluationSerializer,
} }
def serializer_context(self): def serializer_context(self):
@@ -89,7 +83,7 @@ class AutoEvaluationView(BaseViewSetMixin, ModelViewSet):
class AutoEvaluationSetAppraisersView(GenericAPIView): class AutoEvaluationSetAppraisersView(GenericAPIView):
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
queryset = AutoEvaluationModel.objects.all() queryset = AutoEvaluationModel.objects.all()
serializer_class = AutoEvaluationAppraisersSerializer serializer_class = serializers.AutoEvaluationAppraisersSerializer
def post(self, request, id): def post(self, request, id):
try: try:
@@ -108,7 +102,7 @@ class AutoEvaluationSetAppraisersView(GenericAPIView):
class AutoEvaluationRemoveAppraisersView(GenericAPIView): class AutoEvaluationRemoveAppraisersView(GenericAPIView):
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
queryset = AutoEvaluationModel.objects.all() queryset = AutoEvaluationModel.objects.all()
serializer_class = AutoEvaluationAppraisersSerializer serializer_class = serializers.AutoEvaluationAppraisersSerializer
def post(self, request, id): def post(self, request, id):
try: try:
@@ -158,18 +152,41 @@ class AutoEvaluationListAppraisersView(GenericAPIView):
@extend_schema(tags=["AutoEvaluation"]) @extend_schema(tags=["AutoEvaluation"])
class GetArchivedEvaluationListAPIView(ListAPIView): class AutoEvaluationArchivedListAPIView(ListAPIView):
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
serializer_class = serializers.ListAutoevaluationSerializer
def get_queryset(self): def get_queryset(self):
return AutoEvaluationModel.objects.filter(is_archived=True) return AutoEvaluationModel.objects.filter(is_archived=True)
@extend_schema(tags=["AutoEvaluation"]) @extend_schema(tags=["AutoEvaluation"])
class ArchivedEvaluation(APIView): class AutoEvaluationArchiveAPIView(APIView):
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def post(self, request, pk): def post(self, request, pk):
auto_evaluation = get_object_or_404(AutoEvaluationModel, pk=pk) auto_evaluation = get_object_or_404(AutoEvaluationModel, pk=pk)
auto_evaluation.is_archived = request.data["is_archived"] auto_evaluation.is_archived = request.data["is_archived"]
auto_evaluation.save() auto_evaluation.save()
return Response({"success": True}, status=200) return Response(
{
"success": True,
"status": auto_evaluation.status,
"id": auto_evaluation.pk
},
status=200
)
@extend_schema(tags=["AutoEvaluation"])
class AdminEvaluationsAPIView(generics.GenericAPIView):
permission_classes = [IsAuthenticated, IsAdminRole]
queryset = AutoEvaluationModel.objects.all()
serializer_class = AutoEvaluationModel
def get(self, request):
auto_eval = AutoEvaluationModel.objects.filter(
created_by=self.request.user
).select_related('appraisers').distinct()
serializer = AutoEvaluationModelSerializer(auto_eval, many=True)
return Response(serializer.data)

View File

@@ -1,18 +1,37 @@
# rest framework
from rest_framework import generics from rest_framework import generics
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import permissions from rest_framework import permissions
# core apps
from core.apps.evaluation.serializers.auto.AvgCost import AvgCostSerializer from core.apps.evaluation.serializers.auto.AvgCost import AvgCostSerializer
from core.services.grpc.auto import get_auto_avg_cost from core.services.grpc.auto import get_auto_avg_cost
class AvgCostView(generics.GenericAPIView): class AvgCostAPIView(generics.GenericAPIView):
serializer_class = AvgCostSerializer serializer_class = AvgCostSerializer
permission_classes = [permissions.IsAuthenticated] permission_classes = [permissions.IsAuthenticated]
def post(self): def post(self, request):
serializer = self.get_serializer(data=self.request.data) serializer = self.get_serializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
avg_cost = get_auto_avg_cost(serializer.validated_data) print(serializer.validated_data)
brand = serializer.validated_data['brand']
condition = serializer.validated_data['condition']
model = serializer.validated_data['model']
complication = serializer.validated_data['complication']
manufacture_date = serializer.validated_data['manufacture_date']
distance_covered = serializer.validated_data['distance_covered']
color = serializer.validated_data['color']
avg_cost = get_auto_avg_cost(
brand=brand,
condition=condition,
model=model,
complication=complication,
manufacture_date=manufacture_date,
distance_covered=str(distance_covered),
color=color
)
return Response(avg_cost, status=200) return Response(avg_cost, status=200)
return Response(serializer.errors, status=400) return Response(serializer.errors, status=400)

View File

@@ -0,0 +1,60 @@
from django_core.mixins import BaseViewSetMixin
from drf_spectacular.utils import extend_schema
from rest_framework import viewsets
from rest_framework.permissions import IsAdminUser
from rest_framework.viewsets import ModelViewSet
# core
from core.apps.evaluation.models.bonus import BonusCategory, EmployeeBonus, BaseValueBonus
from core.apps.evaluation.serializers.bonus.Bonus import BonusCategorySerializer, \
BonusCategoryListSerializer, EmployeeBonusListSerializer, BonusEmployeeBonusSerializer, BaseBonusSerializer
@extend_schema(tags=["BaseBonus"])
class BaseBonusViewSet(BaseViewSetMixin, viewsets.ModelViewSet):
queryset = BaseValueBonus.objects.all()
serializer_class = BaseBonusSerializer
@extend_schema(tags=["Bonus-Category"])
class BonusTypeView(BaseViewSetMixin, ModelViewSet):
queryset = BonusCategory.objects.all()
serializer_class = BonusCategorySerializer
action_serializer_class = {
'create': BonusCategorySerializer,
'update': BonusCategorySerializer,
'partial_update': BonusCategorySerializer,
'list': BonusCategoryListSerializer,
'retrieve': BonusCategoryListSerializer,
}
action_permission_classes = {
'create': [IsAdminUser],
'update': [IsAdminUser],
'partial_update': [IsAdminUser],
'destroy': [IsAdminUser],
'list': [IsAdminUser],
}
class BonusEmployeeViewSet(BaseViewSetMixin, ModelViewSet):
queryset = EmployeeBonus.objects.all()
serializer_class = BonusEmployeeBonusSerializer
action_serializer_class = {
'create': BonusEmployeeBonusSerializer,
'update': BonusEmployeeBonusSerializer,
'partial_update': BonusEmployeeBonusSerializer,
'list': EmployeeBonusListSerializer,
'retrieve': EmployeeBonusListSerializer,
}
action_permission_classes = {
'create': [IsAdminUser],
'update': [IsAdminUser],
'partial_update': [IsAdminUser],
'destroy': [IsAdminUser],
'list': [IsAdminUser],
}

View File

@@ -1,23 +1,30 @@
# django core
from django_core.mixins import BaseViewSetMixin from django_core.mixins import BaseViewSetMixin
# swagger
from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema
from rest_framework.permissions import IsAuthenticated
# rest framework
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from core.apps.evaluation.models import CertificateModel from rest_framework.permissions import IsAuthenticated
from core.apps.evaluation.serializers.certificate import BaseCertificateSerializer
from rest_framework.filters import SearchFilter from rest_framework.filters import SearchFilter
from rest_framework.parsers import MultiPartParser, FormParser from rest_framework.parsers import MultiPartParser, FormParser
# local apps
from core.apps.evaluation.models import CertificateModel
from core.apps.evaluation.serializers.certificate import BaseCertificateSerializer, CreateCertificateSerializer
@extend_schema(tags=["Certificate"],request=BaseCertificateSerializer) @extend_schema(tags=["Certificate"],request=BaseCertificateSerializer)
class CertificateView(BaseViewSetMixin, ModelViewSet): class CertificateView(BaseViewSetMixin, ModelViewSet):
queryset = CertificateModel.objects.all() queryset = CertificateModel.objects.all()
serializer_class = BaseCertificateSerializer serializer_class = BaseCertificateSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
parser_classes = [MultiPartParser, FormParser] parser_classes = [MultiPartParser, FormParser]
filter_backends = [SearchFilter] filter_backends = [SearchFilter]
search_fields = ["title"] search_fields = ["title"]
pagination_class = None pagination_class = None
action_permission_classes = {} action_permission_classes = {}
action_serializer_class = {
"create": CreateCertificateSerializer
}

View File

@@ -3,44 +3,33 @@ from drf_spectacular.utils import extend_schema
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from rest_framework.viewsets import ReadOnlyModelViewSet from rest_framework.viewsets import ReadOnlyModelViewSet
# local apps
from core.apps.evaluation.models import CustomerModel, PropertyOwnerModel from core.apps.evaluation.models import CustomerModel, PropertyOwnerModel
from core.apps.evaluation.serializers.customer import ( from core.apps.evaluation.serializers import customer as serializers
CreateCustomerSerializer,
CreatePropertyOwnerSerializer,
ListCustomerSerializer,
ListPropertyOwnerSerializer,
RetrieveCustomerSerializer,
RetrievePropertyOwnerSerializer,
)
@extend_schema(tags=["Customer"]) @extend_schema(tags=["Customer"])
class CustomerView(BaseViewSetMixin, ReadOnlyModelViewSet): class CustomerView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = CustomerModel.objects.all() queryset = CustomerModel.objects.all()
serializer_class = ListCustomerSerializer serializer_class = serializers.ListCustomerSerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
action_permission_classes = {} action_permission_classes = {}
action_serializer_class = { action_serializer_class = {
"list": ListCustomerSerializer, "list": serializers.ListCustomerSerializer,
"retrieve": RetrieveCustomerSerializer, "retrieve": serializers.RetrieveCustomerSerializer,
"create": CreateCustomerSerializer, "create": serializers.CreateCustomerSerializer,
} }
@extend_schema(tags=["PropertyOwner"]) @extend_schema(tags=["PropertyOwner"])
class PropertyOwnerView(BaseViewSetMixin, ReadOnlyModelViewSet): class PropertyOwnerView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = PropertyOwnerModel.objects.all() queryset = PropertyOwnerModel.objects.all()
serializer_class = ListPropertyOwnerSerializer serializer_class = serializers.ListPropertyOwnerSerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
action_permission_classes = {} action_permission_classes = {}
action_serializer_class = { action_serializer_class = {
"list": ListPropertyOwnerSerializer, "list": serializers.ListPropertyOwnerSerializer,
"retrieve": RetrievePropertyOwnerSerializer, "retrieve": serializers.RetrievePropertyOwnerSerializer,
"create": CreatePropertyOwnerSerializer, "create": serializers.CreatePropertyOwnerSerializer,
} }
#test commit

View File

@@ -1,11 +1,13 @@
# rest framework
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import status from rest_framework import status
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import GenericAPIView from rest_framework.generics import GenericAPIView
# swagger
from drf_spectacular.utils import extend_schema, OpenApiParameter from drf_spectacular.utils import extend_schema, OpenApiParameter
# local apps
from core.services.didox import DidoxService from core.services.didox import DidoxService
@@ -38,7 +40,6 @@ class DidoxCompanyInfoAPIView(GenericAPIView):
{"detail": "TIN must be a valid integer"}, {"detail": "TIN must be a valid integer"},
status=status.HTTP_400_BAD_REQUEST status=status.HTTP_400_BAD_REQUEST
) )
data = DidoxService.get_company_info(tin) data = DidoxService.get_company_info(tin)
if not data: if not data:
@@ -47,14 +48,11 @@ class DidoxCompanyInfoAPIView(GenericAPIView):
status=status.HTTP_502_BAD_GATEWAY status=status.HTTP_502_BAD_GATEWAY
) )
# if both name and personalNum are null/empty -> 404
name = data.get("name") name = data.get("name")
personal_num = data.get("personalNum") personal_num = data.get("personalNum")
if not name and not personal_num: if not name and not personal_num:
return Response( return Response(
{"detail": "Company or person not found"}, {"detail": "Company or person not found"},
status=status.HTTP_404_NOT_FOUND status=status.HTTP_404_NOT_FOUND
) )
return Response(data, status=status.HTTP_200_OK) return Response(data, status=status.HTTP_200_OK)

View File

@@ -1,51 +1,51 @@
# django
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django_core.mixins import BaseViewSetMixin from django_core.mixins import BaseViewSetMixin
# swagger
from drf_spectacular.utils import extend_schema, OpenApiParameter from drf_spectacular.utils import extend_schema, OpenApiParameter
# rest framework
from rest_framework.exceptions import NotFound, PermissionDenied from rest_framework.exceptions import NotFound, PermissionDenied
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from rest_framework.viewsets import ReadOnlyModelViewSet, ModelViewSet from rest_framework.viewsets import ReadOnlyModelViewSet, ModelViewSet
from rest_framework.parsers import FormParser, MultiPartParser from rest_framework.parsers import FormParser, MultiPartParser
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
# filters
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
from core.apps.evaluation.models import DocumentModel, ValuationDocumentModel, AutoEvaluationModel from core.apps.evaluation.models import DocumentModel, ValuationDocumentModel, AutoEvaluationModel
from core.apps.evaluation.serializers.document import ( from core.apps.evaluation.serializers import document as serializers
CreateDocumentSerializer,
CreateValuationdocumentSerializer,
ListDocumentSerializer,
ListValuationdocumentSerializer,
RetrieveDocumentSerializer,
RetrieveValuationdocumentSerializer,
)
@extend_schema(tags=["ValuationDocument"]) @extend_schema(tags=["ValuationDocument"])
class ValuationDocumentView(BaseViewSetMixin, ReadOnlyModelViewSet): class ValuationDocumentView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = ValuationDocumentModel.objects.all() queryset = ValuationDocumentModel.objects.all()
serializer_class = ListValuationdocumentSerializer serializer_class = serializers.ListValuationdocumentSerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
action_permission_classes = {} action_permission_classes = {}
action_serializer_class = { action_serializer_class = {
"list": ListValuationdocumentSerializer, "list": serializers.ListValuationdocumentSerializer,
"retrieve": RetrieveValuationdocumentSerializer, "retrieve": serializers.RetrieveValuationdocumentSerializer,
"create": CreateValuationdocumentSerializer, "create": serializers.CreateValuationdocumentSerializer,
} }
@extend_schema(tags=["Document"]) @extend_schema(tags=["Document"])
class DocumentView(BaseViewSetMixin, ModelViewSet): class DocumentView(BaseViewSetMixin, ModelViewSet):
queryset = DocumentModel.objects.all() queryset = DocumentModel.objects.all()
serializer_class = ListDocumentSerializer serializer_class = serializers.ListDocumentSerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
parser_classes = [FormParser, MultiPartParser] parser_classes = [FormParser, MultiPartParser]
filter_backends = [DjangoFilterBackend] filter_backends = [DjangoFilterBackend]
action_permission_classes = {} action_permission_classes = {}
action_serializer_class = { action_serializer_class = {
"list": ListDocumentSerializer, "list": serializers.ListDocumentSerializer,
"retrieve": RetrieveDocumentSerializer, "retrieve": serializers.RetrieveDocumentSerializer,
"create": CreateDocumentSerializer, "create": serializers.CreateDocumentSerializer,
} }
@extend_schema( @extend_schema(
@@ -83,9 +83,9 @@ class DocumentView(BaseViewSetMixin, ModelViewSet):
documents = documents.filter(category_id=category_id) documents = documents.filter(category_id=category_id)
page = self.paginate_queryset(documents) page = self.paginate_queryset(documents)
if page is not None: if page is not None:
serializer = ListDocumentSerializer(page, many=True, context={"request": request}) serializer = serializers.ListDocumentSerializer(page, many=True, context={"request": request})
return self.get_paginated_response(serializer.data) return self.get_paginated_response(serializer.data)
serializer = ListDocumentSerializer(documents, many=True, context={"request": request}) serializer = serializers.ListDocumentSerializer(documents, many=True, context={"request": request})
return Response(serializer.data) return Response(serializer.data)
except AutoEvaluationModel.DoesNotExist: except AutoEvaluationModel.DoesNotExist:
raise NotFound("Auto evaluation not found") raise NotFound("Auto evaluation not found")

View File

@@ -1,8 +1,14 @@
# django core
from django_core.mixins import BaseViewSetMixin from django_core.mixins import BaseViewSetMixin
# swagger
from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema
# rest framework
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from rest_framework.viewsets import ReadOnlyModelViewSet from rest_framework.viewsets import ReadOnlyModelViewSet
# core apps
from core.apps.evaluation.models import DocumentcategoryModel from core.apps.evaluation.models import DocumentcategoryModel
from core.apps.evaluation.serializers.documentcategory import ( from core.apps.evaluation.serializers.documentcategory import (
ListDocumentcategorySerializer, ListDocumentcategorySerializer,

View File

@@ -1,24 +1,30 @@
# django
from django_core.mixins import BaseViewSetMixin from django_core.mixins import BaseViewSetMixin
# django filters
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
# swagger
from drf_spectacular.utils import OpenApiParameter, extend_schema from drf_spectacular.utils import OpenApiParameter, extend_schema
# rest framework
from rest_framework.filters import OrderingFilter from rest_framework.filters import OrderingFilter
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.viewsets import ReadOnlyModelViewSet from rest_framework.viewsets import ReadOnlyModelViewSet
# core apps
from core.apps.evaluation.filters.history import ( from core.apps.evaluation.filters.history import (
AutoevaluationhistoryFilter, AutoevaluationhistoryFilter,
MechanicAutoevaluationhistoryFilter,
QuickevaluationhistoryFilter, QuickevaluationhistoryFilter,
) )
from core.apps.evaluation.models import AutoevaluationhistoryModel, QuickevaluationhistoryModel from core.apps.evaluation.models import (
from core.apps.evaluation.serializers.history import ( AutoevaluationhistoryModel,
CreateAutoevaluationhistorySerializer, MechanicAutoevaluationhistoryModel,
CreateQuickevaluationhistorySerializer, QuickevaluationhistoryModel,
ListAutoevaluationhistorySerializer,
ListQuickevaluationhistorySerializer,
RetrieveAutoevaluationhistorySerializer,
RetrieveQuickevaluationhistorySerializer,
) )
from core.apps.evaluation.serializers import history as serializers
@extend_schema( @extend_schema(
@@ -31,13 +37,12 @@ from core.apps.evaluation.serializers.history import (
], ],
) )
class AutoEvaluationHistoryView(BaseViewSetMixin, ReadOnlyModelViewSet): class AutoEvaluationHistoryView(BaseViewSetMixin, ReadOnlyModelViewSet):
# select_related("auto_evaluation") faqat retrieve uchun — list uchun kerak emas
queryset = AutoevaluationhistoryModel.objects.only( queryset = AutoevaluationhistoryModel.objects.only(
"id", "auto_evaluation_id", "event_type", "id", "auto_evaluation_id", "event_type",
"actor_id", "actor_full_name", "actor_role", "actor_id", "actor_full_name", "actor_role",
"meta", "created_at", "meta", "created_at",
) )
serializer_class = ListAutoevaluationhistorySerializer serializer_class = serializers.ListAutoevaluationhistorySerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
pagination_class = None pagination_class = None
@@ -55,14 +60,63 @@ class AutoEvaluationHistoryView(BaseViewSetMixin, ReadOnlyModelViewSet):
action_permission_classes = {} action_permission_classes = {}
action_serializer_class = { action_serializer_class = {
"list": ListAutoevaluationhistorySerializer, "list": serializers.ListAutoevaluationhistorySerializer,
"retrieve": RetrieveAutoevaluationhistorySerializer, "retrieve": serializers.RetrieveAutoevaluationhistorySerializer,
"create": CreateAutoevaluationhistorySerializer, "create": serializers.CreateAutoevaluationhistorySerializer,
}
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
results = list(queryset)
serializer = self.get_serializer(results, many=True)
return Response({
"count": len(results),
"next": None,
"previous": None,
"results": serializer.data,
})
@extend_schema(
tags=["MechanicAutoEvaluationHistory"],
parameters=[
OpenApiParameter("mechanic_auto_evaluation", int, description="MechanicAutoEvaluation ID bo'yicha filter"),
OpenApiParameter("event_type", str, description="Event turi bo'yicha filter"),
OpenApiParameter("created_from", str, description="Boshlanish sanasi (ISO 8601)"),
OpenApiParameter("created_to", str, description="Tugash sanasi (ISO 8601)"),
],
)
class MechanicAutoEvaluationHistoryView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = MechanicAutoevaluationhistoryModel.objects.only(
"id", "mechanic_auto_evaluation_id", "event_type",
"actor_id", "actor_full_name", "actor_role",
"meta", "created_at",
)
serializer_class = serializers.ListMechanicAutoevaluationhistorySerializer
permission_classes = [AllowAny]
pagination_class = None
filter_backends = [DjangoFilterBackend, OrderingFilter]
filterset_class = MechanicAutoevaluationhistoryFilter
ordering_fields = [
"id",
"event_type",
"event_type_display",
"actor",
"meta",
"created_at",
]
ordering = ["created_at"]
action_permission_classes = {}
action_serializer_class = {
"list": serializers.ListMechanicAutoevaluationhistorySerializer,
"retrieve": serializers.RetrieveMechanicAutoevaluationhistorySerializer,
"create": serializers.CreateMechanicAutoevaluationhistorySerializer,
} }
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset()) queryset = self.filter_queryset(self.get_queryset())
# Queryset bir marta evaluate qilinadi — COUNT uchun alohida query yo'q
results = list(queryset) results = list(queryset)
serializer = self.get_serializer(results, many=True) serializer = self.get_serializer(results, many=True)
return Response({ return Response({
@@ -88,7 +142,7 @@ class QuickEvaluationHistoryView(BaseViewSetMixin, ReadOnlyModelViewSet):
"actor_id", "actor_full_name", "actor_role", "actor_id", "actor_full_name", "actor_role",
"meta", "created_at", "meta", "created_at",
) )
serializer_class = ListQuickevaluationhistorySerializer serializer_class = serializers.ListQuickevaluationhistorySerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
pagination_class = None pagination_class = None
@@ -99,9 +153,9 @@ class QuickEvaluationHistoryView(BaseViewSetMixin, ReadOnlyModelViewSet):
action_permission_classes = {} action_permission_classes = {}
action_serializer_class = { action_serializer_class = {
"list": ListQuickevaluationhistorySerializer, "list": serializers.ListQuickevaluationhistorySerializer,
"retrieve": RetrieveQuickevaluationhistorySerializer, "retrieve": serializers.RetrieveQuickevaluationhistorySerializer,
"create": CreateQuickevaluationhistorySerializer, "create": serializers.CreateQuickevaluationhistorySerializer,
} }
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):

View File

@@ -0,0 +1,198 @@
from django.db.models import Q
from django.shortcuts import get_object_or_404
from django_core.mixins import BaseViewSetMixin
from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import extend_schema, OpenApiParameter
from rest_framework import generics
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.generics import GenericAPIView, ListAPIView
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from core.apps.accounts.permissions import IsAdminRole
from core.apps.accounts.serializers.user import UserSerializer
from core.apps.evaluation.filters.mechanic_auto import MechanicAutoevaluationFilter
from core.apps.evaluation.models import MechanicAutoEvaluationModel
from core.apps.evaluation.serializers.auto.MechanicAutoEvaluation import (
ListMechanicAutoevaluationSerializer,
RetrieveMechanicAutoevaluationSerializer,
CreateMechanicAutoevaluationSerializer,
UpdateMechanicAutoevaluationSerializer,
MechanicAutoEvaluationAppraisersSerializer,
MechanicAutoEvaluationModelSerializer,
)
@extend_schema(tags=["MechanicAutoEvaluation"])
class MechanicAutoEvaluationView(BaseViewSetMixin, ModelViewSet):
queryset = MechanicAutoEvaluationModel.objects.select_related(
"valuation",
"valuation__customer",
"vehicle",
).filter(is_archived=False)
serializer_class = ListMechanicAutoevaluationSerializer
permission_classes = [AllowAny]
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_class = MechanicAutoevaluationFilter
search_fields = [
"registration_number",
"car_model",
"car_brand",
"car_number",
]
ordering_fields = [
"id",
"contract_date",
"object_inspection_date",
"object_owner_individual_person_passport_num",
"object_owner_type",
"object_owner_individual_person_f_name",
"object_owner_individual_person_l_name",
"object_owner_individual_person_p_name",
"object_owner_legal_entity",
"object_owner_legal_inn",
"tex_passport_serie_num",
"rating_goal",
"registration_number",
"object_type",
"object_type_display",
"car_brand",
"car_model",
"car_number",
"manufacture_year",
"car_color",
"status",
"status_display",
"created_at",
"value_determined",
"rate_type",
]
ordering = ["-created_at"]
action_permission_classes = {}
action_serializer_class = {
"list": ListMechanicAutoevaluationSerializer,
"retrieve": RetrieveMechanicAutoevaluationSerializer,
"create": CreateMechanicAutoevaluationSerializer,
"update": UpdateMechanicAutoevaluationSerializer,
"partial_update": UpdateMechanicAutoevaluationSerializer,
}
def serializer_context(self):
return self.serializer_class(context={'request': self.request})
@extend_schema(tags=["MechanicAutoEvaluation"])
class MechanicAutoEvaluationSetAppraisersView(GenericAPIView):
permission_classes = [IsAuthenticated]
queryset = MechanicAutoEvaluationModel.objects.all()
serializer_class = MechanicAutoEvaluationAppraisersSerializer
def post(self, request, id):
try:
evaluation = get_object_or_404(MechanicAutoEvaluationModel, id=id)
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
users = serializer.validated_data.get("users")
evaluation.appraisers.set(users)
evaluation.save()
return Response({"success": True, "data": "Foydalanuvchilar muvaffaqiyatli qo'shildi"})
except Exception as e:
return Response({"error": str(e)}, status=500)
@extend_schema(tags=["MechanicAutoEvaluation"])
class MechanicAutoEvaluationRemoveAppraisersView(GenericAPIView):
permission_classes = [IsAuthenticated]
queryset = MechanicAutoEvaluationModel.objects.all()
serializer_class = MechanicAutoEvaluationAppraisersSerializer
def post(self, request, id):
try:
evaluation = get_object_or_404(MechanicAutoEvaluationModel, id=id)
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
users = serializer.validated_data.get("users")
evaluation.appraisers.remove(*users)
evaluation.save()
return Response({"success": True, "data": "Foydalanuvchilar muvaffaqiyatli o'chirildi"})
except Exception as e:
return Response({"error": str(e)}, status=500)
@extend_schema(tags=["MechanicAutoEvaluation"])
class MechanicAutoEvaluationListAppraisersView(GenericAPIView):
permission_classes = [IsAuthenticated]
queryset = MechanicAutoEvaluationModel.objects.all()
serializer_class = UserSerializer
@extend_schema(
parameters=[
OpenApiParameter(
name="search",
type=str,
description="Search query",
required=False,
)
]
)
def get(self, request, id):
try:
search_query = request.query_params.get("search", "")
evaluation = get_object_or_404(MechanicAutoEvaluationModel, id=id)
query = evaluation.appraisers.all()
if search_query:
query = query.filter(
Q(phone__icontains=search_query) |
Q(first_name__icontains=search_query) |
Q(last_name__icontains=search_query)
)
page = self.paginate_queryset(query)
serializer = self.serializer_class(page, many=True)
return self.get_paginated_response(serializer.data)
except Exception as e:
return Response({"error": str(e)}, status=500)
@extend_schema(tags=["MechanicAutoEvaluation"])
class MechanicAutoEvaluationArchivedListAPIView(ListAPIView):
permission_classes = [IsAuthenticated]
serializer_class = ListMechanicAutoevaluationSerializer
def get_queryset(self):
return MechanicAutoEvaluationModel.objects.filter(is_archived=True)
@extend_schema(tags=["MechanicAutoEvaluation"])
class MechanicAutoEvaluationArchiveAPIView(APIView):
permission_classes = [IsAuthenticated]
def post(self, request, pk):
evaluation = get_object_or_404(MechanicAutoEvaluationModel, pk=pk)
evaluation.is_archived = request.data["is_archived"]
evaluation.save()
return Response(
{
"success": True,
"status": evaluation.status,
"id": evaluation.pk
},
status=200
)
@extend_schema(tags=["MechanicAutoEvaluation"])
class AdminMechanicEvaluationsAPIView(generics.GenericAPIView):
permission_classes = [IsAuthenticated, IsAdminRole]
queryset = MechanicAutoEvaluationModel.objects.all()
serializer_class = MechanicAutoEvaluationModelSerializer
def get(self, request):
evaluations = MechanicAutoEvaluationModel.objects.filter(
created_by=self.request.user
).distinct()
serializer = MechanicAutoEvaluationModelSerializer(evaluations, many=True)
return Response(serializer.data)

View File

@@ -1,25 +1,27 @@
# django core
from django_core.mixins import BaseViewSetMixin from django_core.mixins import BaseViewSetMixin
# swagger
from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema
# rest framework
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from rest_framework.viewsets import ReadOnlyModelViewSet from rest_framework.viewsets import ReadOnlyModelViewSet
# core apps
from core.apps.evaluation.models import MovablePropertyEvaluationModel from core.apps.evaluation.models import MovablePropertyEvaluationModel
from core.apps.evaluation.serializers.movable import ( from core.apps.evaluation.serializers import movable as serializers
CreateMovablepropertyevaluationSerializer,
ListMovablepropertyevaluationSerializer,
RetrieveMovablepropertyevaluationSerializer,
)
@extend_schema(tags=["MovablePropertyEvaluation"]) @extend_schema(tags=["MovablePropertyEvaluation"])
class MovablePropertyEvaluationView(BaseViewSetMixin, ReadOnlyModelViewSet): class MovablePropertyEvaluationView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = MovablePropertyEvaluationModel.objects.all() queryset = MovablePropertyEvaluationModel.objects.all()
serializer_class = ListMovablepropertyevaluationSerializer serializer_class = serializers.ListMovablepropertyevaluationSerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
action_permission_classes = {} action_permission_classes = {}
action_serializer_class = { action_serializer_class = {
"list": ListMovablepropertyevaluationSerializer, "list": serializers.ListMovablepropertyevaluationSerializer,
"retrieve": RetrieveMovablepropertyevaluationSerializer, "retrieve": serializers.RetrieveMovablepropertyevaluationSerializer,
"create": CreateMovablepropertyevaluationSerializer, "create": serializers.CreateMovablepropertyevaluationSerializer,
} }

View File

@@ -1,23 +1,26 @@
# django
from django.shortcuts import get_object_or_404
# django core
from django_core.mixins import BaseViewSetMixin from django_core.mixins import BaseViewSetMixin
# django filters
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import extend_schema, OpenApiResponse # swagger
from drf_spectacular.utils import extend_schema
# rest framework
from rest_framework import status, generics
from rest_framework.filters import OrderingFilter, SearchFilter from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.generics import ListAPIView
from rest_framework.parsers import FormParser, MultiPartParser from rest_framework.parsers import FormParser, MultiPartParser
from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.viewsets import ModelViewSet
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import status from rest_framework.views import APIView
from django.shortcuts import get_object_or_404 from rest_framework.viewsets import ModelViewSet
from core.apps.accounts.permissions import IsAdminRole
# core apps
from core.apps.evaluation.filters.quick import QuickevaluationFilter from core.apps.evaluation.filters.quick import QuickevaluationFilter
from core.apps.evaluation.models import QuickEvaluationModel from core.apps.evaluation.models import QuickEvaluationModel
from core.apps.evaluation.serializers.quick import ( from core.apps.evaluation.serializers import quick as serializers, QuickEvaluationModelSerializer
CreateQuickevaluationSerializer,
ListQuickevaluationSerializer,
RetrieveQuickevaluationSerializer,
ArchiveQuickevaluationSerializer,
)
@extend_schema(tags=["QuickEvaluation"]) @extend_schema(tags=["QuickEvaluation"])
@@ -26,7 +29,7 @@ class QuickEvaluationView(BaseViewSetMixin, ModelViewSet):
"created_by", "brand", "marka", "color", "fuel_type", "created_by", "brand", "marka", "color", "fuel_type",
"body_type", "state_car", "car_position", "body_type", "state_car", "car_position",
).filter(is_archive=False) ).filter(is_archive=False)
serializer_class = ListQuickevaluationSerializer serializer_class = serializers.ListQuickevaluationSerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
parser_classes = [MultiPartParser, FormParser] parser_classes = [MultiPartParser, FormParser]
@@ -51,80 +54,48 @@ class QuickEvaluationView(BaseViewSetMixin, ModelViewSet):
action_permission_classes = {} action_permission_classes = {}
action_serializer_class = { action_serializer_class = {
"list": ListQuickevaluationSerializer, "list": serializers.ListQuickevaluationSerializer,
"retrieve": RetrieveQuickevaluationSerializer, "retrieve": serializers.RetrieveQuickevaluationSerializer,
"create": CreateQuickevaluationSerializer, "create": serializers.CreateQuickevaluationSerializer,
} }
@extend_schema(tags=["QuickEvaluation"]) @extend_schema(tags=["QuickEvaluation"])
class ArchiveQuickEvaluationView(GenericAPIView): class QuickEvaluationArchiveAPIView(APIView):
permission_classes = [IsAuthenticated]
def get_serializer_class(self): def post(self, request, pk):
if self.request.method == "GET": instance = get_object_or_404(QuickEvaluationModel, pk=pk)
return ListQuickevaluationSerializer
return ArchiveQuickevaluationSerializer
@extend_schema(
tags=["QuickEvaluation"],
summary="Get archived quick evaluations list",
description="""
Returns only archived quick evaluations.
This endpoint works like quick-evaluation/,
but only records with is_archive=True are returned.
""",
responses={200: ListQuickevaluationSerializer(many=True)},
)
def get(self, request, *args, **kwargs):
queryset = QuickEvaluationModel.objects.filter(
is_archive=True
).order_by("-created_at")
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
@extend_schema(
tags=["QuickEvaluation"],
summary="Archive or unarchive quick evaluation",
description="""
Update archive status for quick evaluation.
- is_archive=true → archive
- is_archive=false → remove from archive
""",
request=ArchiveQuickevaluationSerializer,
responses={
200: OpenApiResponse(
description="Archive status updated successfully"
),
400: OpenApiResponse(
description="Validation error"
),
404: OpenApiResponse(
description="Quick evaluation not found"
),
},
)
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
validated_data = serializer.validated_data
obj = get_object_or_404(
QuickEvaluationModel,
id=validated_data["id"]
)
obj.is_archive = validated_data["is_archive"]
obj.save(update_fields=["is_archive"])
is_archived = request.data.get("is_archived")
if is_archived is None:
return Response( return Response(
{ {"error": "Поле 'is_archived' обязательно"},
"success": True, status=status.HTTP_400_BAD_REQUEST
"message": "Archive status updated successfully"
},
status=status.HTTP_200_OK
) )
instance.is_archive = is_archived
instance.save()
return Response({"success": True}, status=200)
@extend_schema(tags=["QuickEvaluation"])
class QuickEvaluationArchivedListAPIView(ListAPIView):
permission_classes = [IsAuthenticated]
serializer_class = serializers.ListQuickevaluationSerializer
def get_queryset(self):
return QuickEvaluationModel.objects.filter(is_archive=True)
@extend_schema(tags=["QuickEvaluation"])
class AdminQuickEvalAPIView(generics.GenericAPIView):
permission_classes = [IsAuthenticated, IsAdminRole]
queryset = QuickEvaluationModel.objects.all()
serializer_class = QuickEvaluationModelSerializer
def get(self, request):
quick_eval = QuickEvaluationModel.objects.filter(
created_by=self.request.user
).select_related('created_by').distinct()
serializer = QuickEvaluationModelSerializer(quick_eval, many=True)
return Response(serializer.data)

View File

@@ -1,25 +1,27 @@
# django core
from django_core.mixins import BaseViewSetMixin from django_core.mixins import BaseViewSetMixin
# swagger
from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema
# rest framework
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from rest_framework.viewsets import ReadOnlyModelViewSet from rest_framework.viewsets import ReadOnlyModelViewSet
# core
from core.apps.evaluation.models import RealEstateEvaluationModel from core.apps.evaluation.models import RealEstateEvaluationModel
from core.apps.evaluation.serializers.real_estate import ( from core.apps.evaluation.serializers import real_estate as serializers
CreateRealestateevaluationSerializer,
ListRealestateevaluationSerializer,
RetrieveRealestateevaluationSerializer,
)
@extend_schema(tags=["RealEstateEvaluation"]) @extend_schema(tags=["RealEstateEvaluation"])
class RealEstateEvaluationView(BaseViewSetMixin, ReadOnlyModelViewSet): class RealEstateEvaluationView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = RealEstateEvaluationModel.objects.all() queryset = RealEstateEvaluationModel.objects.all()
serializer_class = ListRealestateevaluationSerializer serializer_class = serializers.ListRealestateevaluationSerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
action_permission_classes = {} action_permission_classes = {}
action_serializer_class = { action_serializer_class = {
"list": ListRealestateevaluationSerializer, "list": serializers.ListRealestateevaluationSerializer,
"retrieve": RetrieveRealestateevaluationSerializer, "retrieve": serializers.RetrieveRealestateevaluationSerializer,
"create": CreateRealestateevaluationSerializer, "create": serializers.CreateRealestateevaluationSerializer,
} }

View File

@@ -1,20 +1,21 @@
# django core
from django_core.mixins import BaseViewSetMixin from django_core.mixins import BaseViewSetMixin
# django filters
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
# swagger
from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema
# rest framework
from rest_framework.filters import OrderingFilter, SearchFilter from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from rest_framework.viewsets import ReadOnlyModelViewSet from rest_framework.viewsets import ReadOnlyModelViewSet
# core apps
from core.apps.evaluation.filters.reference import ReferenceitemFilter from core.apps.evaluation.filters.reference import ReferenceitemFilter
from core.apps.evaluation.models import ReferenceitemModel from core.apps.evaluation.models import ReferenceitemModel
from core.apps.evaluation.serializers.reference import ( from core.apps.evaluation.serializers import reference as serializers
CreateReferenceitemSerializer,
ListReferenceitemSerializer,
RetrieveReferenceitemSerializer,
EvaluationPurposeSerializer,
DeterminedValueSerializer,
LabelValueSerializer,
)
@extend_schema(tags=["EvaluationPurpose"]) @extend_schema(tags=["EvaluationPurpose"])
@@ -22,10 +23,9 @@ class EvaluationPurposeView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = ReferenceitemModel.objects.filter( queryset = ReferenceitemModel.objects.filter(
type="evaluation_purpose", is_active=True type="evaluation_purpose", is_active=True
).order_by("order", "name") ).order_by("order", "name")
serializer_class = EvaluationPurposeSerializer serializer_class = serializers.EvaluationPurposeSerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
pagination_class = None pagination_class = None
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
search_fields = ["name"] search_fields = ["name"]
ordering_fields = ["name", "order"] ordering_fields = ["name", "order"]
@@ -37,10 +37,9 @@ class DeterminedValueView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = ReferenceitemModel.objects.filter( queryset = ReferenceitemModel.objects.filter(
type="determined_value", is_active=True type="determined_value", is_active=True
).order_by("order", "name") ).order_by("order", "name")
serializer_class = DeterminedValueSerializer serializer_class = serializers.DeterminedValueSerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
pagination_class = None pagination_class = None
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
search_fields = ["name"] search_fields = ["name"]
ordering_fields = ["name", "order"] ordering_fields = ["name", "order"]
@@ -52,10 +51,9 @@ class PropertyRightsView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = ReferenceitemModel.objects.filter( queryset = ReferenceitemModel.objects.filter(
type="property_rights", is_active=True type="property_rights", is_active=True
).order_by("order", "name") ).order_by("order", "name")
serializer_class = LabelValueSerializer serializer_class = serializers.LabelValueSerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
pagination_class = None pagination_class = None
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
search_fields = ["name"] search_fields = ["name"]
ordering_fields = ["name", "order"] ordering_fields = ["name", "order"]
@@ -67,10 +65,9 @@ class OwnershipFormView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = ReferenceitemModel.objects.filter( queryset = ReferenceitemModel.objects.filter(
type="ownership_form", is_active=True type="ownership_form", is_active=True
).order_by("order", "name") ).order_by("order", "name")
serializer_class = LabelValueSerializer serializer_class = serializers.LabelValueSerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
pagination_class = None pagination_class = None
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
search_fields = ["name"] search_fields = ["name"]
ordering_fields = ["name", "order"] ordering_fields = ["name", "order"]
@@ -80,18 +77,16 @@ class OwnershipFormView(BaseViewSetMixin, ReadOnlyModelViewSet):
@extend_schema(tags=["ReferenceItem"]) @extend_schema(tags=["ReferenceItem"])
class ReferenceitemView(BaseViewSetMixin, ReadOnlyModelViewSet): class ReferenceitemView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = ReferenceitemModel.objects.select_related("parent").filter(is_active=True) queryset = ReferenceitemModel.objects.select_related("parent").filter(is_active=True)
serializer_class = ListReferenceitemSerializer serializer_class = serializers.ListReferenceitemSerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_class = ReferenceitemFilter filterset_class = ReferenceitemFilter
search_fields = ["name"] search_fields = ["name"]
ordering_fields = ["name", "order", "type"] ordering_fields = ["name", "order", "type"]
ordering = ["order", "name"] ordering = ["order", "name"]
action_permission_classes = {} action_permission_classes = {}
action_serializer_class = { action_serializer_class = {
"list": ListReferenceitemSerializer, "list": serializers.ListReferenceitemSerializer,
"retrieve": RetrieveReferenceitemSerializer, "retrieve": serializers.RetrieveReferenceitemSerializer,
"create": CreateReferenceitemSerializer, "create": serializers.CreateReferenceitemSerializer,
} }

View File

@@ -1,25 +1,26 @@
# django core
from django_core.mixins import BaseViewSetMixin from django_core.mixins import BaseViewSetMixin
# swagger
from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema
# rest framework
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from rest_framework.viewsets import ReadOnlyModelViewSet from rest_framework.viewsets import ReadOnlyModelViewSet
# core apps
from core.apps.evaluation.models import EvaluationReportModel from core.apps.evaluation.models import EvaluationReportModel
from core.apps.evaluation.serializers.report import ( from core.apps.evaluation.serializers import report as serializers
CreateEvaluationreportSerializer,
ListEvaluationreportSerializer,
RetrieveEvaluationreportSerializer,
)
@extend_schema(tags=["EvaluationReport"]) @extend_schema(tags=["EvaluationReport"])
class EvaluationReportView(BaseViewSetMixin, ReadOnlyModelViewSet): class EvaluationReportView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = EvaluationReportModel.objects.all() queryset = EvaluationReportModel.objects.all()
serializer_class = ListEvaluationreportSerializer serializer_class = serializers.ListEvaluationreportSerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
action_permission_classes = {} action_permission_classes = {}
action_serializer_class = { action_serializer_class = {
"list": ListEvaluationreportSerializer, "list": serializers.ListEvaluationreportSerializer,
"retrieve": RetrieveEvaluationreportSerializer, "retrieve": serializers.RetrieveEvaluationreportSerializer,
"create": CreateEvaluationreportSerializer, "create": serializers.CreateEvaluationreportSerializer,
} }

View File

@@ -1,42 +1,33 @@
# django
from django.db.models.base import transaction
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
# django core
from django_core.mixins import BaseViewSetMixin from django_core.mixins import BaseViewSetMixin
from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import extend_schema
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.pagination import PageNumberPagination
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
# django filters
from django_filters.rest_framework import DjangoFilterBackend
# swagger
from drf_spectacular.utils import extend_schema
# rest framework
from rest_framework import status, filters, generics, permissions, views, viewsets
from rest_framework.response import Response
# core apps
from core.apps.accounts.choices import RoleChoice
from core.apps.evaluation.choices.request import RequestStatus
from core.apps.evaluation.filters.request import EvaluationrequestFilter from core.apps.evaluation.filters.request import EvaluationrequestFilter
from core.apps.evaluation.models import EvaluationrequestModel from core.apps.evaluation.models import EvaluationrequestModel
from core.apps.evaluation.serializers.request import ( from core.apps.evaluation.serializers import request as serializers
CreateEvaluationrequestSerializer,
ListEvaluationrequestSerializer,
RetrieveEvaluationrequestSerializer,
ArchiveEvaluationrequestSerializer,
)
from core.apps.evaluation.choices.request import RequestStatus
from rest_framework.generics import GenericAPIView
from drf_spectacular.utils import OpenApiResponse
# class RequestPagination(PageNumberPagination):
# page_size = 20
# page_size_query_param = "limit"
# max_page_size = 100
@extend_schema(tags=["EvaluationRequest"]) @extend_schema(tags=["EvaluationRequest"])
class EvaluationrequestView(BaseViewSetMixin, ModelViewSet): class EvaluationrequestView(BaseViewSetMixin, viewsets.ModelViewSet):
serializer_class = ListEvaluationrequestSerializer serializer_class = serializers.ListEvaluationrequestSerializer
permission_classes = [IsAuthenticated] permission_classes = [permissions.IsAuthenticated]
# pagination_class = RequestPagination filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_class = EvaluationrequestFilter filterset_class = EvaluationrequestFilter
search_fields = [ search_fields = [
"customer_inn_number", "customer_inn_number",
@@ -71,9 +62,9 @@ class EvaluationrequestView(BaseViewSetMixin, ModelViewSet):
action_permission_classes = {} action_permission_classes = {}
action_serializer_class = { action_serializer_class = {
"list": ListEvaluationrequestSerializer, "list": serializers.ListEvaluationrequestSerializer,
"retrieve": RetrieveEvaluationrequestSerializer, "retrieve": serializers.RetrieveEvaluationrequestSerializer,
"create": CreateEvaluationrequestSerializer, "create": serializers.CreateEvaluationrequestSerializer,
} }
def serializer_context(self): def serializer_context(self):
@@ -81,18 +72,15 @@ class EvaluationrequestView(BaseViewSetMixin, ModelViewSet):
def get_queryset(self): def get_queryset(self):
return EvaluationrequestModel.objects.filter( return EvaluationrequestModel.objects.filter(
user=self.request.user user=self.request.user, is_archive=False
).order_by("-created_at") ).order_by("-created_at")
@extend_schema(tags=["EvaluationRequest"]) @extend_schema(tags=["EvaluationRequest"])
class AdminEvaluationrequestView(BaseViewSetMixin, ModelViewSet): class AdminEvaluationrequestView(BaseViewSetMixin, viewsets.ModelViewSet):
serializer_class = ListEvaluationrequestSerializer serializer_class = serializers.ListEvaluationrequestSerializer
permission_classes = [IsAuthenticated] permission_classes = [permissions.IsAuthenticated]
# pagination_class = RequestPagination filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_class = EvaluationrequestFilter filterset_class = EvaluationrequestFilter
search_fields = [ search_fields = [
"customer_inn_number", "customer_inn_number",
@@ -127,29 +115,29 @@ class AdminEvaluationrequestView(BaseViewSetMixin, ModelViewSet):
action_permission_classes = {} action_permission_classes = {}
action_serializer_class = { action_serializer_class = {
"list": ListEvaluationrequestSerializer, "list": serializers.ListEvaluationrequestSerializer,
"retrieve": RetrieveEvaluationrequestSerializer, "retrieve": serializers.RetrieveEvaluationrequestSerializer,
"create": CreateEvaluationrequestSerializer, "create": serializers.CreateEvaluationrequestSerializer,
} }
def get_queryset(self): def get_queryset(self):
return EvaluationrequestModel.objects.select_related("value_determined", "rate_goal", "property_rights", "form_ownership", "user").order_by("-created_at") return EvaluationrequestModel.objects.\
select_related("value_determined","rate_goal","property_rights","form_ownership","user")\
.order_by("-created_at")
def serializer_context(self): def serializer_context(self):
return self.serializer_class(context={"request": self.request}) return self.serializer_class(context={"request": self.request})
@extend_schema(tags=["EvaluationRequest"]) @extend_schema(tags=["EvaluationRequest"])
class EvaluationStatusChange(APIView): class EvaluationStatusChange(views.APIView):
permission_classes = [IsAuthenticated] permission_classes = [permissions.IsAuthenticated]
def post(self, request, pk): def post(self, request, pk):
if request.user.role not in [RoleChoice.ADMIN, RoleChoice.SUPERUSER]: if request.user.role not in [RoleChoice.ADMIN, RoleChoice.SUPERUSER]:
return Response({'detail': 'Forbidden'}, status=status.HTTP_403_FORBIDDEN) return Response({'detail': 'Forbidden'}, status=status.HTTP_403_FORBIDDEN)
evaluation = get_object_or_404(EvaluationrequestModel, pk=pk) evaluation = get_object_or_404(EvaluationrequestModel, pk=pk)
status_value = request.data.get('status') status_value = request.data.get('status')
if not status_value: if not status_value:
return Response({'detail': 'Status is required'}, status=status.HTTP_400_BAD_REQUEST) return Response({'detail': 'Status is required'}, status=status.HTTP_400_BAD_REQUEST)
@@ -176,75 +164,30 @@ class EvaluationStatusChange(APIView):
'id': evaluation.pk 'id': evaluation.pk
}) })
@extend_schema(tags=["EvaluationRequest"]) @extend_schema(tags=["EvaluationRequest"])
class ArchiveEvaluationrequestView(GenericAPIView): class RequestEvaluationArchivedListAPIView(generics.ListAPIView):
permission_classes = [IsAuthenticated] permission_classes = [permissions.IsAuthenticated]
serializer_class = serializers.ListEvaluationrequestSerializer
def get_serializer_class(self): def get_queryset(self):
if self.request.method == "GET": return EvaluationrequestModel.objects.filter(is_archive=True)
return ListEvaluationrequestSerializer
return ArchiveEvaluationrequestSerializer
@extend_schema(
tags=["EvaluationRequest"],
summary="Get archived evaluation requests list",
description="""
Returns only archived evaluation requests.
This endpoint works like evaluation-request/, @extend_schema(tags=["EvaluationRequest"])
but only records with is_archive=True are returned. class RequestEvaluationArchiveAPIView(views.APIView):
""", permission_classes = [permissions.IsAuthenticated]
responses={200: ListEvaluationrequestSerializer(many=True)},
)
def get(self, request, *args, **kwargs):
queryset = EvaluationrequestModel.objects.filter(
is_archive=True
).order_by("-created_at")
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
@extend_schema(
tags=["EvaluationRequest"],
summary="Archive or unarchive evaluation request",
description="""
Update archive status for evaluation request.
- is_archive=true → archive
- is_archive=false → remove from archive
""",
request=ArchiveEvaluationrequestSerializer,
responses={
200: OpenApiResponse(
description="Archive status updated successfully"
),
400: OpenApiResponse(
description="Validation error"
),
404: OpenApiResponse(
description="Evaluation request not found"
),
},
)
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
validated_data = serializer.validated_data
obj = get_object_or_404(
EvaluationrequestModel,
id=validated_data["id"]
)
obj.is_archive = validated_data["is_archive"]
obj.save(update_fields=["is_archive"])
@transaction.atomic
def post(self, request, pk):
req_evaluation = get_object_or_404(EvaluationrequestModel, pk=pk)
req_evaluation.is_archive = request.data["is_archived"]
req_evaluation.save()
return Response( return Response(
{ {
"success": True, "success": True,
"message": "Archive status updated successfully" "status": req_evaluation.status,
"id": req_evaluation.pk
}, },
status=status.HTTP_200_OK status=status.HTTP_200_OK
) )

View File

@@ -1,29 +1,30 @@
# rest framework
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import status from rest_framework import status
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import BasePermission
from rest_framework.generics import GenericAPIView from rest_framework.generics import GenericAPIView
from drf_spectacular.utils import ( # swagger
extend_schema, from drf_spectacular.utils import extend_schema
OpenApiExample,
)
# core apps
from core.services.tech_passport import TechPassportService from core.services.tech_passport import TechPassportService
from ..serializers import TechPassportSerializer from core.apps.evaluation.serializers import TechPassportSerializer
from core.apps.accounts.choices import RoleChoice
class IsNotUserRole(BasePermission):
def has_permission(self, request, view):
if not request.user or not request.user.is_authenticated:
return False
return request.user.role != RoleChoice.USER
class TechPassportAPIView(GenericAPIView): class TechPassportAPIView(GenericAPIView):
authentication_classes = [] permission_classes = [IsNotUserRole]
permission_classes = [IsAuthenticated]
@extend_schema( @extend_schema(
tags=["Tech Passport"], tags=["Tech Passport"],
summary="Get vehicle information by technical passport",
description=(
"This endpoint retrieves vehicle information using "
"autonumber, technical passport number, and technical passport series "
"via Gross Insurance API integration."
),
request=TechPassportSerializer, request=TechPassportSerializer,
responses={ responses={
200: dict, 200: dict,
@@ -45,14 +46,12 @@ class TechPassportAPIView(GenericAPIView):
response_data = result["data"] response_data = result["data"]
status_code = result["status_code"] status_code = result["status_code"]
# success bolsa faqat data ichidagi data qaytariladi
if status_code == 200: if status_code == 200:
return Response( return Response(
response_data.get("data", {}), response_data.get("data", {}),
status=status.HTTP_200_OK status=status.HTTP_200_OK
) )
# error bolsa original response qaytariladi
return Response( return Response(
response_data, response_data,
status=status_code status=status_code

View File

@@ -1,25 +1,27 @@
# django core
from django_core.mixins import BaseViewSetMixin from django_core.mixins import BaseViewSetMixin
# swagger
from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema
# rest framework
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from rest_framework.viewsets import ReadOnlyModelViewSet from rest_framework.viewsets import ReadOnlyModelViewSet
# core apps
from core.apps.evaluation.models import ValuationModel from core.apps.evaluation.models import ValuationModel
from core.apps.evaluation.serializers.valuation import ( from core.apps.evaluation.serializers import valuation as serializers
CreateValuationSerializer,
ListValuationSerializer,
RetrieveValuationSerializer,
)
@extend_schema(tags=["Valuation"]) @extend_schema(tags=["Valuation"])
class ValuationView(BaseViewSetMixin, ReadOnlyModelViewSet): class ValuationView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = ValuationModel.objects.all() queryset = ValuationModel.objects.all()
serializer_class = ListValuationSerializer serializer_class = serializers.ListValuationSerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
action_permission_classes = {} action_permission_classes = {}
action_serializer_class = { action_serializer_class = {
"list": ListValuationSerializer, "list": serializers.ListValuationSerializer,
"retrieve": RetrieveValuationSerializer, "retrieve": serializers.RetrieveValuationSerializer,
"create": CreateValuationSerializer, "create": serializers.CreateValuationSerializer,
} }

View File

@@ -1,14 +1,19 @@
# django core
from django.http import HttpResponse
from django_core.mixins import BaseViewSetMixin from django_core.mixins import BaseViewSetMixin
# swagger
from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema
# rest framework
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import ReadOnlyModelViewSet from rest_framework.viewsets import ReadOnlyModelViewSet
# core apps
from core.apps.evaluation.models import VehicleModel from core.apps.evaluation.models import VehicleModel
from core.apps.evaluation.serializers.vehicle import ( from core.apps.evaluation.serializers import vehicle as serialziers, VehicleApplicationSerializer
CreateVehicleSerializer, from core.utils.generation_pdf import PDFService
ListVehicleSerializer,
RetrieveVehicleSerializer,
)
@extend_schema(tags=["Vehicle"]) @extend_schema(tags=["Vehicle"])
@@ -16,12 +21,28 @@ class VehicleView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = VehicleModel.objects.select_related( queryset = VehicleModel.objects.select_related(
"brand", "model", "color", "fuel_type", "body_type", "position", "brand", "model", "color", "fuel_type", "body_type", "position",
).all() ).all()
serializer_class = ListVehicleSerializer serializer_class = serialziers.ListVehicleSerializer
permission_classes = [AllowAny] permission_classes = [AllowAny]
action_permission_classes = {} action_permission_classes = {}
action_serializer_class = { action_serializer_class = {
"list": ListVehicleSerializer, "list": serialziers.ListVehicleSerializer,
"retrieve": RetrieveVehicleSerializer, "retrieve": serialziers.RetrieveVehicleSerializer,
"create": CreateVehicleSerializer, "create": serialziers.CreateVehicleSerializer,
} }
@extend_schema(tags=['GenerationDocument'], request=VehicleApplicationSerializer)
class GeneratePDFView(APIView):
permission_classes = [IsAuthenticated]
def post(self, request):
serializer = VehicleApplicationSerializer(data=request.data)
if not serializer.is_valid():
return Response(serializer.errors, status=400)
pdf_buffer = PDFService.generate_vehicle_pdf(serializer.validated_data)
response = HttpResponse(pdf_buffer, content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="ariza.pdf"'
return response

View File

View File

@@ -0,0 +1,4 @@
from .column import *
from .comment import *
from .task import *
from .label import *

View File

@@ -0,0 +1,7 @@
from django.contrib import admin
from core.apps.tasks.models import Column
@admin.register(Column)
class ColumnAdmin(admin.ModelAdmin):
list_display = ('name',)

View File

@@ -0,0 +1,7 @@
from django.contrib import admin
from core.apps.tasks.models import Comment
@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
list_display = ('created_by', 'type')

View File

@@ -0,0 +1,7 @@
from django.contrib import admin
from core.apps.tasks.models import Label
@admin.register(Label)
class LabelAdmin(admin.ModelAdmin):
list_display = ('name',)

View File

@@ -0,0 +1,7 @@
from django.contrib import admin
from core.apps.tasks.models import Task
@admin.register(Task)
class TaskAdmin(admin.ModelAdmin):
list_display = ('name', 'created_by', 'priority')

8
core/apps/tasks/apps.py Normal file
View File

@@ -0,0 +1,8 @@
from django.apps import AppConfig
class TasksConfig(AppConfig):
name = "core.apps.tasks"
def ready(self):
from core.apps.tasks import admin

View File

@@ -0,0 +1,6 @@
from django.db import models
class MessageChoice(models.TextChoices):
FILE = "file", "File"
TEXT = "text", "Text"

View File

@@ -0,0 +1,7 @@
from django.db import models
class PriorityChoice(models.TextChoices):
LOW = "low", "Low"
MEDIUM = "medium", "Medium"
HIGH = "high", "High"

View File

@@ -0,0 +1,77 @@
# Generated by Django 5.2.7 on 2026-04-29 13:18
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Column',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=255)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Label',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=255)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Task',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=255)),
('description', models.TextField(blank=True)),
('priority', models.CharField(choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High')], max_length=255)),
('from_date', models.DateField(blank=True, null=True)),
('to_date', models.DateField(blank=True, null=True)),
('assignees', models.ManyToManyField(related_name='assigned_tasks', to=settings.AUTH_USER_MODEL)),
('column', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tasks', to='tasks.column')),
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='created_tasks', to=settings.AUTH_USER_MODEL)),
('labels', models.ManyToManyField(related_name='tasks', to='tasks.label')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Comment',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('message', models.TextField()),
('file', models.FileField(blank=True, null=True, upload_to='comments/')),
('type', models.CharField(choices=[('file', 'File'), ('text', 'Text')], max_length=255)),
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='created_comments', to=settings.AUTH_USER_MODEL)),
('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='tasks.task')),
],
options={
'abstract': False,
},
),
]

View File

@@ -0,0 +1,21 @@
# Generated by Django 5.2.7 on 2026-04-29 13:20
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tasks', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AlterField(
model_name='comment',
name='created_by',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to=settings.AUTH_USER_MODEL),
),
]

View File

@@ -0,0 +1,29 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tasks', '0002_alter_comment_created_by'),
]
operations = [
migrations.RemoveField(
model_name='comment',
name='file',
),
migrations.CreateModel(
name='CommentFile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('file', models.FileField(upload_to='comments/')),
('comment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='files', to='tasks.comment')),
],
options={
'abstract': False,
},
),
]

View File

View File

@@ -0,0 +1,4 @@
from .column import *
from .comment import *
from .task import *
from .label import *

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