This commit is contained in:
gitea 2024-12-17 14:08:40 +00:00
commit f98513f7d5
84 changed files with 7653205 additions and 1536 deletions

BIN
.DS_Store vendored

Binary file not shown.

1
.gitignore vendored
View File

@ -6,7 +6,6 @@ __pycache__
db.sqlite3
media
./car_inventory/settings.py
# Backup files #
*.bak

View File

@ -9,7 +9,7 @@ https://docs.djangoproject.com/en/5.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.0/ref/settings/
"""
from decimal import Decimal
from pathlib import Path
import os
from django.utils.translation import gettext_lazy as _
@ -26,7 +26,7 @@ SECRET_KEY = 'django-insecure-gc9bh4*3=b6hihdnaom0edjsbxh$5t)aap@e8p&340r7)*)qb8
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['10.10.1.109',"10.10.1.120", 'localhost', '127.0.0.1', '192.168.1.135', '172.20.10.4']
ALLOWED_HOSTS = ['10.10.1.109', 'localhost', '127.0.0.1', '192.168.1.135', '172.20.10.4']
# Application definition
@ -110,9 +110,9 @@ WSGI_APPLICATION = 'car_inventory.wsgi.application'
DATABASES = {
"default": {
"ENGINE": "django_prometheus.db.backends.postgresql",
"NAME": "haikal",
"USER": "haikal",
"PASSWORD": "haikal",
"NAME": "haikal_app",
"USER": "f95166",
"PASSWORD": "Kfsh&rc9788",
"HOST": "localhost",
"PORT": 5432,
}
@ -232,9 +232,17 @@ CRISPY_TEMPLATE_PACK = "bootstrap5"
OPENAI_API_KEY = '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'
# ELM API KEYS
APP_ID = '367974ed'
APP_KEY = '046b0412c1b4d3f8c39ec6375d6f3030'
CLIENT_ID = '94142c27-2536-47e9-8e28-9ca7728b9442'
# vehicle-info
# c2729afb
# 6d397471920412d672af1b8a02ca52ea
# option-info
# 367974ed
# 046b0412c1b4d3f8c39ec6375d6f3030
ELM_APP_ID = 'c2729afb'
ELM_APP_KEY = '6d397471920412d672af1b8a02ca52ea'
ELM_CLIENT_ID = '94142c27-2536-47e9-8e28-9ca7728b9442'
LOGGING = {
'version': 1,
@ -255,3 +263,7 @@ LOGGING = {
},
},
}
# Global Settings
CURRENCY = _('SAR')
VAT_RATE = Decimal('0.15')

View File

@ -1,5 +1,5 @@
from django.contrib import admin
from django.urls import path, include
from django.urls import path, include, re_path
from django.conf.urls.static import static
from django.conf import settings
from django.conf.urls.i18n import i18n_patterns

3319703
car_specification_value.csv Normal file

File diff suppressed because it is too large Load Diff

31
clean_specs.py Normal file
View File

@ -0,0 +1,31 @@
import pandas as pd
# Specify the path to your CSV file
file_path = 'car_specification_value.csv'
# Read the file and fix inconsistencies
try:
with open(file_path, 'r') as file:
lines = file.readlines()
# Fix the problematic lines
fixed_lines = []
for line in lines:
# Remove single quotes and strip whitespace
columns = [col.strip().replace("'", "") for col in line.strip().split(',')]
if len(columns) >= 8: # Ensure at least 8 fields
fixed_line = ','.join(f'"{col}"' for col in columns[:8]) # Truncate to 8 fields and add double quotes
fixed_lines.append(fixed_line)
# Save the cleaned data to a new CSV file
cleaned_file_path = 'car_specification_value_cleaned.csv'
with open(cleaned_file_path, 'w') as file:
file.write('\n'.join(fixed_lines))
# Load the cleaned data into a DataFrame
cleaned_df = pd.read_csv(cleaned_file_path)
print("Cleaned data preview:")
print(cleaned_df.head())
print(f"Cleaned file saved as: {cleaned_file_path}")
except Exception as e:
print(f"An error occurred: {e}")

View File

@ -1533,7 +1533,7 @@ def decode_vin(vin):
# VR3USHNLWRJ521303
# KNARH81E8P5194005
# Example usage
vin_number = 'KNARH81E8P5194005'
vin_number = 'VYFED9HP0SJ519559'
decoded_vin = decode_vin(vin_number)
print(decoded_vin)

BIN
inventory/.DS_Store vendored

Binary file not shown.

View File

@ -19,6 +19,10 @@ admin.site.register(models.InteriorColors)
admin.site.register(models.Subscription)
admin.site.register(models.SubscriptionPlan)
admin.site.register(models.SubscriptionUser)
admin.site.register(models.CarLocation)
admin.site.register(models.CarReservation)
admin.site.register(models.Organization)
admin.site.register(models.Representative)
@admin.register(models.CarMake)
class CarMakeAdmin(admin.ModelAdmin):

View File

@ -4,3 +4,6 @@ from django.apps import AppConfig
class InventoryConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'inventory'
def ready(self):
import inventory.signals

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

412
inventory/data/car_make.csv Normal file
View File

@ -0,0 +1,412 @@
id_car_make,name
1,AC
2,Acura
3,Alfa Romeo
4,Alpine
5,AM General
6,Ariel
7,Aro
8,Asia
9,Aston Martin
10,Audi
11,Austin
12,Autobianchi
13,Baltijas Dzips
14,Beijing
15,Bentley
16,Bertone
17,Bitter
18,BMW
20,Brabus
21,Brilliance
22,Bristol
23,Bufori
24,Bugatti
25,Buick
26,BYD
27,Byvin
28,Cadillac
29,Callaway
30,Carbodies
31,Caterham
32,Changan
33,ChangFeng
34,Chery
35,Chevrolet
36,Chrysler
37,Citroen
38,Cizeta
39,Coggiola
40,Dacia
41,Dadi
42,Daewoo
44,Daihatsu
45,Daimler
46,Dallas
47,Datsun
48,De Tomaso
49,DeLorean
50,Derways
51,Dodge
52,DongFeng
53,Doninvest
54,Donkervoort
55,E-Car
56,Eagle
57,Eagle Cars
59,FAW
60,Ferrari
61,Fiat
62,Fisker
63,Ford
64,Foton
65,FSO
66,Fuqi
67,Geely
68,Geo
69,GMC
70,Gonow
71,Great Wall
72,Hafei
73,Haima
74,Hindustan
75,Holden
76,Honda
77,HuangHai
78,Hummer
79,Hyundai
80,Infiniti
81,Innocenti
82,Invicta
83,Iran Khodro
84,Isdera
85,Isuzu
86,IVECO
87,JAC
88,Jaguar
89,Jeep
90,Jensen
91,JMC
92,Kia
93,Koenigsegg
95,Lamborghini
96,Lancia
97,Land Rover
98,Landwind
99,Lexus
100,Liebao Motor
101,Lifan
102,Lincoln
103,Lotus
104,LTI
105,Luxgen
106,Mahindra
107,Marcos
108,Marlin
109,Marussia
110,Maruti
111,Maserati
112,Maybach
113,Mazda
114,McLaren
115,Mega
116,Mercedes-Benz
117,Mercury
118,Metrocab
119,MG
120,Microcar
121,Minelli
122,Mini
123,Mitsubishi
124,Mitsuoka
125,Morgan
126,Morris
127,Nissan
128,Noble
129,Oldsmobile
130,Opel
131,Osca
132,Pagani
133,Panoz
134,Perodua
135,Peugeot
136,Piaggio
137,Plymouth
138,Pontiac
139,Porsche
140,Premier
141,Proton
142,PUCH
143,Puma
144,Qoros
145,Qvale
146,Reliant
147,Renault
148,Samsung
149,Rolls-Royce
150,Ronart
151,Rover
152,Saab
153,Saleen
154,Santana
155,Saturn
156,Scion
157,SEAT
158,ShuangHuan
159,Skoda
160,Smart
161,Soueast
162,Spectre
163,Spyker
165,SsangYong
166,Subaru
167,Suzuki
168,Talbot
169,Tata
170,Tatra
171,Tazzari
172,Tesla
173,Tianma
174,Tianye
175,Tofas
176,Toyota
177,Trabant
178,Tramontana
179,Triumph
180,TVR
181,Vauxhall
182,Vector
183,Venturi
184,Volkswagen
185,Volvo
186,Vortex
187,Wartburg
188,Westfield
189,Wiesmann
190,Xin Kai
191,Zastava
192,Zotye
193,ZX
211,Yo-mobil
212,Autocam
215,VAZ (Lada)
216,GAZ
217,ZAZ
218,ZIL
219,IZH
221,Kanonir
222,LuAZ
223,Moskvich
224,SMZ
226,TagAZ
227,UAZ
280,Ultima
282,Hawtai
284,Renaissance Cars
286,Paykan
289,Gordon
290,Haval
291,Alpina
3589,DS
3590,PGO
3591,Zenvo
3619,Rezvani
3620,Rimac
3664,Adler
3665,DeSoto
3666,Packard
3667,Willys
3668,Combat
3676,Borgward
3689,Ravon
3690,ZiS
3705,AMC
3706,Zenos
3728,W Motors
3739,Hudson
3749,Austin Healey
3751,BAW
3752,Chana
3753,Changhe
3754,DFSK
3755,Efini
3756,Excalibur
3757,Groz
3760,Hurtan
3761,Jiangnan
3762,Jinbei
3764,Maxus
3765,Monte Carlo
3767,Nysa
3771,Shifeng
3772,SMA
3774,Wuling
3815,Genesis
3816,International
3821,Zibar
3822,Other car
4142,RAM
4144,Aurus
4145,Trumpchi
4146,Abarth
4147,Baic
4148,DFM
4149,Saipa
4154,Apal
4155,Bajaj
4156,Batmobile
4157,Bilenkin
4158,Cord
4159,Delage
4160,DKW
4162,DW Hower
4163,Flanker
4164,GAC
4165,GP
4166,Hanomag
4167,Hispano-Suiza
4168,Horch
4169,KTM AG
4170,Ligier
4171,Lucid
4172,Nash
4173,Rambler
4174,Renaissance
4175,Renault Samsung
4176,Rinspeed
4177,Shanghai Maple
4178,Simca
4179,Steyr
4180,Studebaker
4181,Think
4182,Wanderer
4183,Racing car
4184,Logem
4186,Dallara
4187,Heinkel
4188,Yulon
4189,Bio auto
4190,Deco Rides
4191,Sears
4192,Xpeng
4195,Roewe
4196,CheryExeed
4197,LiXiang
4198,Nio
4199,Hennessey
4200,Polestar
4201,Auburn
4202,Weltmeister
4203,Cupra
4204,Goggomobil
4205,HSV
4206,Matra
4207,Messerschmitt
4208,Pierce-Arrow
4209,VUHL
4210,Ciimo
4211,Everus
4212,Facel Vega
4213,Hongqi
4214,Hozon
4215,Aion
4216,Blaval
4217,Skywell
4307,Voyah
4308,Rivian
4309,Zeekr
4310,Leapmotor
4311,Tank
4312,Arcfox
4313,Auto Union
4314,Denza
4315,Evolute
4316,HiPhi
4317,Kaiyi
4318,Omoda
4319,DR
4320,Mobilize
4321,Oshan
4322,Aito
4323,Baojun
4324,Jetour
4325,Jetta
4326,Lynk & Co
4327,Seres
4361,ORA
4362,WEY
4363,Avatr
4364,Dongfeng Forthing
4365,Rising Auto
4366,IM Motors (Zhiji)
4367,VinFast
4368,Livan
4369,SWM
4947,EXEED
4948,Changan Kuayue (KYC)
4949,VGV
4950,Russo-Balt
4951,Aiways
4952,KYC
4953,Skyworth
4954,Yema
5037,Overland
5038,Belgee
5039,Dayun
5040,Enoreve
5041,Enovate
5042,Jaecoo
5043,JMEV
5044,Jonway
5045,KG Mobility
5046,Qiantu
5047,Qingling
5048,Radar
5049,Rox
5050,Sollers
5051,Thairung
5052,Venucia
5053,ATOM
5128,Hycan
5129,Qiyuan
5130,XiaoPaoChe (SSC)
5233,Fengon
5234,M-Hero
5235,Oting
5236,Rossa
5238,Huazi
5239,iCar
5240,Luxeed
5241,XEV
5242,Xiaomi
5305,Hanteng
5306,Kawei
5335,Ineos
5336,Karma
5337,Seres Aito
5338,Solaris
5339,Yipai
5340,Amberauto
5341,Polar Stone (Jishi)
5342,Xcite
5343,Coda
5405,Aixam
5406,Ambertruck
5407,Huaihai (Hoann)
5408,Maple
5409,Maxeed
5410,Volga
5481,Exlantix
5482,LEVC
5483,Rayton Fissore
5484,Stelato
5542,Franklin
5543,Jidu
5544,GMA
5545,Knewstar
5586,lingxi
5609,Hyperion
5610,Punk
5611,Radford
5612,Sandstorm
5613,Scout
1 id_car_make name
2 1 AC
3 2 Acura
4 3 Alfa Romeo
5 4 Alpine
6 5 AM General
7 6 Ariel
8 7 Aro
9 8 Asia
10 9 Aston Martin
11 10 Audi
12 11 Austin
13 12 Autobianchi
14 13 Baltijas Dzips
15 14 Beijing
16 15 Bentley
17 16 Bertone
18 17 Bitter
19 18 BMW
20 20 Brabus
21 21 Brilliance
22 22 Bristol
23 23 Bufori
24 24 Bugatti
25 25 Buick
26 26 BYD
27 27 Byvin
28 28 Cadillac
29 29 Callaway
30 30 Carbodies
31 31 Caterham
32 32 Changan
33 33 ChangFeng
34 34 Chery
35 35 Chevrolet
36 36 Chrysler
37 37 Citroen
38 38 Cizeta
39 39 Coggiola
40 40 Dacia
41 41 Dadi
42 42 Daewoo
43 44 Daihatsu
44 45 Daimler
45 46 Dallas
46 47 Datsun
47 48 De Tomaso
48 49 DeLorean
49 50 Derways
50 51 Dodge
51 52 DongFeng
52 53 Doninvest
53 54 Donkervoort
54 55 E-Car
55 56 Eagle
56 57 Eagle Cars
57 59 FAW
58 60 Ferrari
59 61 Fiat
60 62 Fisker
61 63 Ford
62 64 Foton
63 65 FSO
64 66 Fuqi
65 67 Geely
66 68 Geo
67 69 GMC
68 70 Gonow
69 71 Great Wall
70 72 Hafei
71 73 Haima
72 74 Hindustan
73 75 Holden
74 76 Honda
75 77 HuangHai
76 78 Hummer
77 79 Hyundai
78 80 Infiniti
79 81 Innocenti
80 82 Invicta
81 83 Iran Khodro
82 84 Isdera
83 85 Isuzu
84 86 IVECO
85 87 JAC
86 88 Jaguar
87 89 Jeep
88 90 Jensen
89 91 JMC
90 92 Kia
91 93 Koenigsegg
92 95 Lamborghini
93 96 Lancia
94 97 Land Rover
95 98 Landwind
96 99 Lexus
97 100 Liebao Motor
98 101 Lifan
99 102 Lincoln
100 103 Lotus
101 104 LTI
102 105 Luxgen
103 106 Mahindra
104 107 Marcos
105 108 Marlin
106 109 Marussia
107 110 Maruti
108 111 Maserati
109 112 Maybach
110 113 Mazda
111 114 McLaren
112 115 Mega
113 116 Mercedes-Benz
114 117 Mercury
115 118 Metrocab
116 119 MG
117 120 Microcar
118 121 Minelli
119 122 Mini
120 123 Mitsubishi
121 124 Mitsuoka
122 125 Morgan
123 126 Morris
124 127 Nissan
125 128 Noble
126 129 Oldsmobile
127 130 Opel
128 131 Osca
129 132 Pagani
130 133 Panoz
131 134 Perodua
132 135 Peugeot
133 136 Piaggio
134 137 Plymouth
135 138 Pontiac
136 139 Porsche
137 140 Premier
138 141 Proton
139 142 PUCH
140 143 Puma
141 144 Qoros
142 145 Qvale
143 146 Reliant
144 147 Renault
145 148 Samsung
146 149 Rolls-Royce
147 150 Ronart
148 151 Rover
149 152 Saab
150 153 Saleen
151 154 Santana
152 155 Saturn
153 156 Scion
154 157 SEAT
155 158 ShuangHuan
156 159 Skoda
157 160 Smart
158 161 Soueast
159 162 Spectre
160 163 Spyker
161 165 SsangYong
162 166 Subaru
163 167 Suzuki
164 168 Talbot
165 169 Tata
166 170 Tatra
167 171 Tazzari
168 172 Tesla
169 173 Tianma
170 174 Tianye
171 175 Tofas
172 176 Toyota
173 177 Trabant
174 178 Tramontana
175 179 Triumph
176 180 TVR
177 181 Vauxhall
178 182 Vector
179 183 Venturi
180 184 Volkswagen
181 185 Volvo
182 186 Vortex
183 187 Wartburg
184 188 Westfield
185 189 Wiesmann
186 190 Xin Kai
187 191 Zastava
188 192 Zotye
189 193 ZX
190 211 Yo-mobil
191 212 Autocam
192 215 VAZ (Lada)
193 216 GAZ
194 217 ZAZ
195 218 ZIL
196 219 IZH
197 221 Kanonir
198 222 LuAZ
199 223 Moskvich
200 224 SMZ
201 226 TagAZ
202 227 UAZ
203 280 Ultima
204 282 Hawtai
205 284 Renaissance Cars
206 286 Paykan
207 289 Gordon
208 290 Haval
209 291 Alpina
210 3589 DS
211 3590 PGO
212 3591 Zenvo
213 3619 Rezvani
214 3620 Rimac
215 3664 Adler
216 3665 DeSoto
217 3666 Packard
218 3667 Willys
219 3668 Combat
220 3676 Borgward
221 3689 Ravon
222 3690 ZiS
223 3705 AMC
224 3706 Zenos
225 3728 W Motors
226 3739 Hudson
227 3749 Austin Healey
228 3751 BAW
229 3752 Chana
230 3753 Changhe
231 3754 DFSK
232 3755 Efini
233 3756 Excalibur
234 3757 Groz
235 3760 Hurtan
236 3761 Jiangnan
237 3762 Jinbei
238 3764 Maxus
239 3765 Monte Carlo
240 3767 Nysa
241 3771 Shifeng
242 3772 SMA
243 3774 Wuling
244 3815 Genesis
245 3816 International
246 3821 Zibar
247 3822 Other car
248 4142 RAM
249 4144 Aurus
250 4145 Trumpchi
251 4146 Abarth
252 4147 Baic
253 4148 DFM
254 4149 Saipa
255 4154 Apal
256 4155 Bajaj
257 4156 Batmobile
258 4157 Bilenkin
259 4158 Cord
260 4159 Delage
261 4160 DKW
262 4162 DW Hower
263 4163 Flanker
264 4164 GAC
265 4165 GP
266 4166 Hanomag
267 4167 Hispano-Suiza
268 4168 Horch
269 4169 KTM AG
270 4170 Ligier
271 4171 Lucid
272 4172 Nash
273 4173 Rambler
274 4174 Renaissance
275 4175 Renault Samsung
276 4176 Rinspeed
277 4177 Shanghai Maple
278 4178 Simca
279 4179 Steyr
280 4180 Studebaker
281 4181 Think
282 4182 Wanderer
283 4183 Racing car
284 4184 Logem
285 4186 Dallara
286 4187 Heinkel
287 4188 Yulon
288 4189 Bio auto
289 4190 Deco Rides
290 4191 Sears
291 4192 Xpeng
292 4195 Roewe
293 4196 CheryExeed
294 4197 LiXiang
295 4198 Nio
296 4199 Hennessey
297 4200 Polestar
298 4201 Auburn
299 4202 Weltmeister
300 4203 Cupra
301 4204 Goggomobil
302 4205 HSV
303 4206 Matra
304 4207 Messerschmitt
305 4208 Pierce-Arrow
306 4209 VUHL
307 4210 Ciimo
308 4211 Everus
309 4212 Facel Vega
310 4213 Hongqi
311 4214 Hozon
312 4215 Aion
313 4216 Blaval
314 4217 Skywell
315 4307 Voyah
316 4308 Rivian
317 4309 Zeekr
318 4310 Leapmotor
319 4311 Tank
320 4312 Arcfox
321 4313 Auto Union
322 4314 Denza
323 4315 Evolute
324 4316 HiPhi
325 4317 Kaiyi
326 4318 Omoda
327 4319 DR
328 4320 Mobilize
329 4321 Oshan
330 4322 Aito
331 4323 Baojun
332 4324 Jetour
333 4325 Jetta
334 4326 Lynk & Co
335 4327 Seres
336 4361 ORA
337 4362 WEY
338 4363 Avatr
339 4364 Dongfeng Forthing
340 4365 Rising Auto
341 4366 IM Motors (Zhiji)
342 4367 VinFast
343 4368 Livan
344 4369 SWM
345 4947 EXEED
346 4948 Changan Kuayue (KYC)
347 4949 VGV
348 4950 Russo-Balt
349 4951 Aiways
350 4952 KYC
351 4953 Skyworth
352 4954 Yema
353 5037 Overland
354 5038 Belgee
355 5039 Dayun
356 5040 Enoreve
357 5041 Enovate
358 5042 Jaecoo
359 5043 JMEV
360 5044 Jonway
361 5045 KG Mobility
362 5046 Qiantu
363 5047 Qingling
364 5048 Radar
365 5049 Rox
366 5050 Sollers
367 5051 Thairung
368 5052 Venucia
369 5053 ATOM
370 5128 Hycan
371 5129 Qiyuan
372 5130 XiaoPaoChe (SSC)
373 5233 Fengon
374 5234 M-Hero
375 5235 Oting
376 5236 Rossa
377 5238 Huazi
378 5239 iCar
379 5240 Luxeed
380 5241 XEV
381 5242 Xiaomi
382 5305 Hanteng
383 5306 Kawei
384 5335 Ineos
385 5336 Karma
386 5337 Seres Aito
387 5338 Solaris
388 5339 Yipai
389 5340 Amberauto
390 5341 Polar Stone (Jishi)
391 5342 Xcite
392 5343 Coda
393 5405 Aixam
394 5406 Ambertruck
395 5407 Huaihai (Hoann)
396 5408 Maple
397 5409 Maxeed
398 5410 Volga
399 5481 Exlantix
400 5482 LEVC
401 5483 Rayton Fissore
402 5484 Stelato
403 5542 Franklin
404 5543 Jidu
405 5544 GMA
406 5545 Knewstar
407 5586 lingxi
408 5609 Hyperion
409 5610 Punk
410 5611 Radford
411 5612 Sandstorm
412 5613 Scout

5296
inventory/data/car_model.csv Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,105 @@
id_car_specification,name,id_parent
1,Bodywork,
2,Body type,1
3,Number of doors,1549
4,Number of seater,1
5,Length,1
6,Width,1
7,Height,1
8,Wheelbase,1
9,Front track,1
10,Rear track,1
11,Engine,
12,Engine type,11
13,Capacity,11
14,Engine power,11
15,Max power at RPM,11
16,Maximum torque,11
17,Injection type,11
18,Overhead camshaft,11
19,Cylinder layout,11
20,Number of cylinders,11
21,Compression ratio,11
22,Fuel,31
23,Gearbox and handling,
24,Gearbox type,23
26,Number of gear,23
27,Drive wheels,23
29,Front brakes,40
30,Rear brakes,40
31,Operating characteristics,
32,Max speed,31
33,Acceleration (0-100 km/h),31
34,Curb weight,1
35,Fuel tank capacity,31
36,Wheel size,1
37,Emission standards,31
38,Ground clearance,1
39,Valves per cylinder,11
40,Suspension and brakes,
41,Front suspension,40
42,Back suspension,40
44,Max trunk capacity,1
45,Min trunk capacity,1
46,Boost type,11
47,Cylinder bore,11
48,Stroke cycle,11
49,Bore/stroke ratio,23
50,City driving fuel consumption per 100 km,31
51,Highway driving fuel consumption per 100 km,31
52,Mixed driving fuel consumption per 100 km,31
53,Steering,
54,Steering type,53
55,Engine model,11
56,Electric motor power,11
57,Turning circle,23
58,Full weight,1
59,Disc size,31
60,Total power output,11
61,Engine placement,11
62,Cruising range,31
63,Full cycle charge,31
66,Car width with mirrors,1
1521,Cylinder bore and stroke cycle,11
1549,General information,
1550,Volume and weight,
1551,Security,
1552,Country,1549
1553,Car class,1549
1554,Clearance,1
1555,Front track width,1
1556,Back track width,1
1558,Max power (h.p.),11
1559,Max power (kW),11
1560,Model assembly,1549
1561,CO2 emissions,31
1562,Safety assessment,1551
1563,Rating name,1551
1564,Turnover of maximum torque,11
1565,Payload,1
1566,Presence of intercooler,11
1567,Trailer load (with brakes),1
1568,Front/rear axle load,1
1569,Loading height,1
1570,Cargo compartment (Length x Width x Height),1
1571,Cargo compartment volume,1
1631,Accumulator battery,
1632,Battery capacity,1631
1634,Electric power reserve,1631
1635,Charging time,1631
1636,Fuel consumption city/highway/mixed l',31
1641,Rudder location,53
1642,Dimensions,1
1644,Pitch Circle Diameter,1
1645,Engine code,11
1646,Disc sizes,1
1647,Battery type,1631
1648,Timing belt,11
1649,Flow calculation method,11
1650,Battery temperature,1631
1651,Fast charge time,1631
1652,Description of fast charging,1631
1653,Charging connector type,1631
1654,Consumption,1631
1655,Maximum charging power,1631
1656,Battery capacity (available),1631
1 id_car_specification name id_parent
2 1 Bodywork
3 2 Body type 1
4 3 Number of doors 1549
5 4 Number of seater 1
6 5 Length 1
7 6 Width 1
8 7 Height 1
9 8 Wheelbase 1
10 9 Front track 1
11 10 Rear track 1
12 11 Engine
13 12 Engine type 11
14 13 Capacity 11
15 14 Engine power 11
16 15 Max power at RPM 11
17 16 Maximum torque 11
18 17 Injection type 11
19 18 Overhead camshaft 11
20 19 Cylinder layout 11
21 20 Number of cylinders 11
22 21 Compression ratio 11
23 22 Fuel 31
24 23 Gearbox and handling
25 24 Gearbox type 23
26 26 Number of gear 23
27 27 Drive wheels 23
28 29 Front brakes 40
29 30 Rear brakes 40
30 31 Operating characteristics
31 32 Max speed 31
32 33 Acceleration (0-100 km/h) 31
33 34 Curb weight 1
34 35 Fuel tank capacity 31
35 36 Wheel size 1
36 37 Emission standards 31
37 38 Ground clearance 1
38 39 Valves per cylinder 11
39 40 Suspension and brakes
40 41 Front suspension 40
41 42 Back suspension 40
42 44 Max trunk capacity 1
43 45 Min trunk capacity 1
44 46 Boost type 11
45 47 Cylinder bore 11
46 48 Stroke cycle 11
47 49 Bore/stroke ratio 23
48 50 City driving fuel consumption per 100 km 31
49 51 Highway driving fuel consumption per 100 km 31
50 52 Mixed driving fuel consumption per 100 km 31
51 53 Steering
52 54 Steering type 53
53 55 Engine model 11
54 56 Electric motor power 11
55 57 Turning circle 23
56 58 Full weight 1
57 59 Disc size 31
58 60 Total power output 11
59 61 Engine placement 11
60 62 Cruising range 31
61 63 Full cycle charge 31
62 66 Car width with mirrors 1
63 1521 Cylinder bore and stroke cycle 11
64 1549 General information
65 1550 Volume and weight
66 1551 Security
67 1552 Country 1549
68 1553 Car class 1549
69 1554 Clearance 1
70 1555 Front track width 1
71 1556 Back track width 1
72 1558 Max power (h.p.) 11
73 1559 Max power (kW) 11
74 1560 Model assembly 1549
75 1561 CO2 emissions 31
76 1562 Safety assessment 1551
77 1563 Rating name 1551
78 1564 Turnover of maximum torque 11
79 1565 Payload 1
80 1566 Presence of intercooler 11
81 1567 Trailer load (with brakes) 1
82 1568 Front/rear axle load 1
83 1569 Loading height 1
84 1570 Cargo compartment (Length x Width x Height) 1
85 1571 Cargo compartment volume 1
86 1631 Accumulator battery
87 1632 Battery capacity 1631
88 1634 Electric power reserve 1631
89 1635 Charging time 1631
90 1636 Fuel consumption city/highway/mixed l' 31
91 1641 Rudder location 53
92 1642 Dimensions 1
93 1644 Pitch Circle Diameter 1
94 1645 Engine code 11
95 1646 Disc sizes 1
96 1647 Battery type 1631
97 1648 Timing belt 11
98 1649 Flow calculation method 11
99 1650 Battery temperature 1631
100 1651 Fast charge time 1631
101 1652 Description of fast charging 1631
102 1653 Charging connector type 1631
103 1654 Consumption 1631
104 1655 Maximum charging power 1631
105 1656 Battery capacity (available) 1631

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
id_car_specification_value,id_car_trim,id_car_specification,value,unit,date_create,date_update,id_car_type
7278893,12743,52,10,l,1400481796,1400481796,1
7278894,12743,34,1582,kg,1400481796,1400481796,1
7420800,18896,2,Cabriolet,NULL,1402348874,1402348874,1
1 id_car_specification_value id_car_trim id_car_specification value unit date_create date_update id_car_type
2 7278893 12743 52 10 l 1400481796 1400481796 1
3 7278894 12743 34 1582 kg 1400481796 1400481796 1
4 7420800 18896 2 Cabriolet NULL 1402348874 1402348874 1

86803
inventory/data/car_trim.csv Normal file

File diff suppressed because it is too large Load Diff

84305
inventory/data/filters.csv Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,502 @@
[
{
"id_car_generation": 1,
"id_car_model": 2123,
"name": "2 generation",
"year_begin": "2013",
"year_end": "2020",
"date_create": 1403453052,
"date_update": 1629665942,
"id_car_type": 1
},
{
"id_car_generation": 4,
"id_car_model": 7,
"name": "2 generation",
"year_begin": "2001",
"year_end": "2003",
"date_create": 1403637064,
"date_update": 1568054401,
"id_car_type": 1
},
{
"id_car_generation": 5,
"id_car_model": 7,
"name": "1 generation",
"year_begin": "1997",
"year_end": "2001",
"date_create": 1403637064,
"date_update": 1461884287,
"id_car_type": 1
},
{
"id_car_generation": 6,
"id_car_model": 9,
"name": "3 generation",
"year_begin": "1993",
"year_end": "2001",
"date_create": 1403637072,
"date_update": 1586337762,
"id_car_type": 1
},
{
"id_car_generation": 7,
"id_car_model": 9,
"name": "2 generation",
"year_begin": "1989",
"year_end": "1993",
"date_create": 1403637072,
"date_update": 1586337723,
"id_car_type": 1
},
{
"id_car_generation": 8,
"id_car_model": 9,
"name": "1 generation",
"year_begin": "1991",
"year_end": "2002",
"date_create": 1403637072,
"date_update": 1586390460,
"id_car_type": 1
},
{
"id_car_generation": 9,
"id_car_model": 10,
"name": "2 generation",
"year_begin": "1990",
"year_end": "1996",
"date_create": 1403637075,
"date_update": 1461884289,
"id_car_type": 1
},
{
"id_car_generation": 10,
"id_car_model": 10,
"name": "1 generation",
"year_begin": "1986",
"year_end": "1990",
"date_create": 1403637075,
"date_update": 1461884290,
"id_car_type": 1
},
{
"id_car_generation": 11,
"id_car_model": 11,
"name": "3 generation",
"year_begin": "2013",
"year_end": "2020",
"date_create": 1403637079,
"date_update": 1629662287,
"id_car_type": 1
},
{
"id_car_generation": 12,
"id_car_model": 11,
"name": "2 generation",
"year_begin": "2006",
"year_end": "2009",
"date_create": 1403637079,
"date_update": 1568054401,
"id_car_type": 1
},
{
"id_car_generation": 13,
"id_car_model": 11,
"name": "1 generation",
"year_begin": "2001",
"year_end": "2006",
"date_create": 1403637079,
"date_update": 1568054401,
"id_car_type": 1
},
{
"id_car_generation": 14,
"id_car_model": 13,
"name": "2 generation",
"year_begin": "2012",
"year_end": "2020",
"date_create": 1403637086,
"date_update": 1629662290,
"id_car_type": 1
},
{
"id_car_generation": 15,
"id_car_model": 13,
"name": "1 generation",
"year_begin": "2006",
"year_end": "2010",
"date_create": 1403637086,
"date_update": 1568054402,
"id_car_type": 1
},
{
"id_car_generation": 16,
"id_car_model": 14,
"name": "KB2",
"year_begin": "2008",
"year_end": "2010",
"date_create": 1403637090,
"date_update": 1577311082,
"id_car_type": 1
},
{
"id_car_generation": 17,
"id_car_model": 14,
"name": "KA9",
"year_begin": "1999",
"year_end": "2004",
"date_create": 1403637090,
"date_update": 1577311083,
"id_car_type": 1
},
{
"id_car_generation": 18,
"id_car_model": 18,
"name": "4 generation",
"year_begin": "2008",
"year_end": "2011",
"date_create": 1403637129,
"date_update": 1568054402,
"id_car_type": 1
},
{
"id_car_generation": 19,
"id_car_model": 18,
"name": "3 generation",
"year_begin": "2003",
"year_end": "2008",
"date_create": 1403637129,
"date_update": 1568054402,
"id_car_type": 1
},
{
"id_car_generation": 20,
"id_car_model": 18,
"name": "2 generation",
"year_begin": "1998",
"year_end": "2003",
"date_create": 1403637129,
"date_update": 1568054402,
"id_car_type": 1
},
{
"id_car_generation": 21,
"id_car_model": 18,
"name": "1 generation",
"year_begin": "1995",
"year_end": "1998",
"date_create": 1403637129,
"date_update": 1568054402,
"id_car_type": 1
},
{
"id_car_generation": 22,
"id_car_model": 19,
"name": "2 generation",
"year_begin": "2008",
"year_end": "2010",
"date_create": 1403637132,
"date_update": 1568054402,
"id_car_type": 1
},
{
"id_car_generation": 23,
"id_car_model": 19,
"name": "1 generation",
"year_begin": "2003",
"year_end": "2008",
"date_create": 1403637132,
"date_update": 1568054402,
"id_car_type": 1
},
{
"id_car_generation": 27,
"id_car_model": 28,
"name": "1 generation [restyling]",
"year_begin": "2003",
"year_end": "2007",
"date_create": 1403637189,
"date_update": 1461877031,
"id_car_type": 1
},
{
"id_car_generation": 28,
"id_car_model": 28,
"name": "936",
"year_begin": "1998",
"year_end": "2007",
"date_create": 1403637189,
"date_update": 1568054404,
"id_car_type": 1
},
{
"id_car_generation": 29,
"id_car_model": 29,
"name": "907",
"year_begin": "1990",
"year_end": "1994",
"date_create": 1403637192,
"date_update": 1568054404,
"id_car_type": 1
},
{
"id_car_generation": 30,
"id_car_model": 29,
"name": "905",
"year_begin": "1983",
"year_end": "1986",
"date_create": 1403637193,
"date_update": 1461877033,
"id_car_type": 1
},
{
"id_car_generation": 35,
"id_car_model": 40,
"name": "116",
"year_begin": "1977",
"year_end": "1981",
"date_create": 1403637463,
"date_update": 1568054405,
"id_car_type": 1
},
{
"id_car_generation": 36,
"id_car_model": 47,
"name": "939",
"year_begin": "2006",
"year_end": "2010",
"date_create": 1403637573,
"date_update": 1568054406,
"id_car_type": 1
},
{
"id_car_generation": 37,
"id_car_model": 47,
"name": "916",
"year_begin": "1995",
"year_end": "2006",
"date_create": 1403637573,
"date_update": 1568054406,
"id_car_type": 1
},
{
"id_car_generation": 38,
"id_car_model": 47,
"name": "115",
"year_begin": "1990",
"year_end": "1993",
"date_create": 1403637573,
"date_update": 1568054406,
"id_car_type": 1
},
{
"id_car_generation": 43,
"id_car_model": 63,
"name": "1 generation [restyling]",
"year_begin": "2008",
"year_end": "2012",
"date_create": 1403637672,
"date_update": 1568054407,
"id_car_type": 1
},
{
"id_car_generation": 46,
"id_car_model": 67,
"name": "1 generation [restyling]",
"year_begin": "2013",
"year_end": "2021",
"date_create": 1403637687,
"date_update": 1653251495,
"id_car_type": 1
},
{
"id_car_generation": 47,
"id_car_model": 67,
"name": "1 generation",
"year_begin": "2009",
"year_end": "2012",
"date_create": 1403637687,
"date_update": 1568054408,
"id_car_type": 1
},
{
"id_car_generation": 48,
"id_car_model": 69,
"name": "2 generation",
"year_begin": "2012",
"year_end": "2020",
"date_create": 1403637695,
"date_update": 1653251496,
"id_car_type": 1
},
{
"id_car_generation": 49,
"id_car_model": 69,
"name": "1 generation",
"year_begin": "2001",
"year_end": "2007",
"date_create": 1403637695,
"date_update": 1568054408,
"id_car_type": 1
},
{
"id_car_generation": 52,
"id_car_model": 74,
"name": "2 generation",
"year_begin": "2011",
"year_end": "2012",
"date_create": 1403637969,
"date_update": 1461877095,
"id_car_type": 1
},
{
"id_car_generation": 53,
"id_car_model": 74,
"name": "1 generation",
"year_begin": "2011",
"year_end": "2012",
"date_create": 1403637969,
"date_update": 1568054408,
"id_car_type": 1
},
{
"id_car_generation": 54,
"id_car_model": 78,
"name": "8C/B4",
"year_begin": "1991",
"year_end": "1996",
"date_create": 1403638037,
"date_update": 1568054409,
"id_car_type": 1
},
{
"id_car_generation": 55,
"id_car_model": 78,
"name": "8A/B3",
"year_begin": "1986",
"year_end": "1991",
"date_create": 1403638037,
"date_update": 1568054409,
"id_car_type": 1
},
{
"id_car_generation": 56,
"id_car_model": 78,
"name": "B2",
"year_begin": "1978",
"year_end": "1986",
"date_create": 1403638037,
"date_update": 1579480971,
"id_car_type": 1
},
{
"id_car_generation": 57,
"id_car_model": 78,
"name": "B1 [restyling]",
"year_begin": null,
"year_end": null,
"date_create": 1403638037,
"date_update": 1568054410,
"id_car_type": 1
},
{
"id_car_generation": 59,
"id_car_model": 82,
"name": "8V",
"year_begin": "2012",
"year_end": "2016",
"date_create": 1403638083,
"date_update": 1579480982,
"id_car_type": 1
},
{
"id_car_generation": 61,
"id_car_model": 82,
"name": "8P",
"year_begin": "2003",
"year_end": "2005",
"date_create": 1403638083,
"date_update": 1579480988,
"id_car_type": 1
},
{
"id_car_generation": 62,
"id_car_model": 84,
"name": "B8/8K [restyling]",
"year_begin": "2011",
"year_end": "2016",
"date_create": 1403638088,
"date_update": 1568054413,
"id_car_type": 1
},
{
"id_car_generation": 63,
"id_car_model": 84,
"name": "B8/8K",
"year_begin": "2007",
"year_end": "2011",
"date_create": 1403638088,
"date_update": 1568054413,
"id_car_type": 1
},
{
"id_car_generation": 64,
"id_car_model": 84,
"name": "B7",
"year_begin": "2004",
"year_end": "2008",
"date_create": 1403638088,
"date_update": 1579480998,
"id_car_type": 1
},
{
"id_car_generation": 65,
"id_car_model": 84,
"name": "B6",
"year_begin": "2000",
"year_end": "2005",
"date_create": 1403638088,
"date_update": 1579481000,
"id_car_type": 1
},
{
"id_car_generation": 66,
"id_car_model": 84,
"name": "B5 [restyling]",
"year_begin": "1997",
"year_end": "2001",
"date_create": 1403638089,
"date_update": 1568054414,
"id_car_type": 1
},
{
"id_car_generation": 68,
"id_car_model": 85,
"name": "8T [restyling]",
"year_begin": "2011",
"year_end": "2016",
"date_create": 1403638091,
"date_update": 1579481007,
"id_car_type": 1
},
{
"id_car_generation": 70,
"id_car_model": 86,
"name": "4F,C6",
"year_begin": "2006",
"year_end": "2008",
"date_create": 1403638093,
"date_update": 1560774955,
"id_car_type": 1
},
{
"id_car_generation": 71,
"id_car_model": 86,
"name": "4B/C5",
"year_begin": "2000",
"year_end": "2005",
"date_create": 1403638093,
"date_update": 1568054415,
"id_car_type": 1
}
]

85843
inventory/data/mappings.csv Normal file

File diff suppressed because it is too large Load Diff

452
inventory/data/serie.json Normal file
View File

@ -0,0 +1,452 @@
[
{
"id_car_serie": 457,
"id_car_model": 350,
"id_car_generation": 7840,
"name": "Crossover",
"date_create": 1399138514,
"date_update": 1586393837,
"id_car_type": 1
},
{
"id_car_serie": 458,
"id_car_model": 351,
"id_car_generation": 7841,
"name": "Sedan",
"date_create": 1399138516,
"date_update": 1568054502,
"id_car_type": 1
},
{
"id_car_serie": 461,
"id_car_model": 353,
"id_car_generation": 7842,
"name": "Hatchback",
"date_create": 1399138520,
"date_update": 1568054502,
"id_car_type": 1
},
{
"id_car_serie": 464,
"id_car_model": 356,
"id_car_generation": 7151,
"name": "Crossover",
"date_create": 1399139415,
"date_update": 1568054503,
"id_car_type": 1
},
{
"id_car_serie": 465,
"id_car_model": 357,
"id_car_generation": 7152,
"name": "SUV 5-doors",
"date_create": 1399139417,
"date_update": 1461877213,
"id_car_type": 1
},
{
"id_car_serie": 471,
"id_car_model": 361,
"id_car_generation": 7847,
"name": "Fastback",
"date_create": 1399139422,
"date_update": 1568054505,
"id_car_type": 1
},
{
"id_car_serie": 477,
"id_car_model": 365,
"id_car_generation": 7850,
"name": "Hatchback",
"date_create": 1399139427,
"date_update": 1568054506,
"id_car_type": 1
},
{
"id_car_serie": 478,
"id_car_model": 366,
"id_car_generation": 7851,
"name": "Hatchback",
"date_create": 1399139710,
"date_update": 1568054506,
"id_car_type": 1
},
{
"id_car_serie": 479,
"id_car_model": 367,
"id_car_generation": 7852,
"name": "Hatchback 5-doors",
"date_create": 1399139713,
"date_update": 1461884968,
"id_car_type": 1
},
{
"id_car_serie": 480,
"id_car_model": 368,
"id_car_generation": 7853,
"name": "Minivan",
"date_create": 1399139715,
"date_update": 1568054506,
"id_car_type": 1
},
{
"id_car_serie": 486,
"id_car_model": 372,
"id_car_generation": 7856,
"name": "Hatchback 5-doors",
"date_create": 1399139722,
"date_update": 1461884971,
"id_car_type": 1
},
{
"id_car_serie": 494,
"id_car_model": 376,
"id_car_generation": 7858,
"name": "Minivan",
"date_create": 1399140313,
"date_update": 1568054521,
"id_car_type": 1
},
{
"id_car_serie": 498,
"id_car_model": 378,
"id_car_generation": 7860,
"name": "Moroder Coupe",
"date_create": 1399140317,
"date_update": 1568054522,
"id_car_type": 1
},
{
"id_car_serie": 505,
"id_car_model": 382,
"id_car_generation": 7863,
"name": "Coupe",
"date_create": 1399140323,
"date_update": 1461885011,
"id_car_type": 1
},
{
"id_car_serie": 506,
"id_car_model": 383,
"id_car_generation": 7864,
"name": "Compactvan",
"date_create": 1399140325,
"date_update": 1555412782,
"id_car_type": 1
},
{
"id_car_serie": 508,
"id_car_model": 385,
"id_car_generation": 7865,
"name": "Compactvan Stepway",
"date_create": 1399140328,
"date_update": 1461885013,
"id_car_type": 1
},
{
"id_car_serie": 511,
"id_car_model": 387,
"id_car_generation": 7866,
"name": "Hatchback",
"date_create": 1399141253,
"date_update": 1586460721,
"id_car_type": 1
},
{
"id_car_serie": 513,
"id_car_model": 389,
"id_car_generation": 7867,
"name": "Sedan",
"date_create": 1399141257,
"date_update": 1568054523,
"id_car_type": 1
},
{
"id_car_serie": 515,
"id_car_model": 391,
"id_car_generation": 7868,
"name": "SUV",
"date_create": 1399141261,
"date_update": 1568054523,
"id_car_type": 1
},
{
"id_car_serie": 534,
"id_car_model": 404,
"id_car_generation": 7156,
"name": "Sedan",
"date_create": 1399142733,
"date_update": 1568054525,
"id_car_type": 1
},
{
"id_car_serie": 535,
"id_car_model": 405,
"id_car_generation": 7878,
"name": "Sedan",
"date_create": 1399142735,
"date_update": 1568054525,
"id_car_type": 1
},
{
"id_car_serie": 536,
"id_car_model": 406,
"id_car_generation": 7879,
"name": "Sedan",
"date_create": 1399143306,
"date_update": 1568054525,
"id_car_type": 1
},
{
"id_car_serie": 538,
"id_car_model": 408,
"id_car_generation": 7880,
"name": "SUV",
"date_create": 1399143310,
"date_update": 1568054526,
"id_car_type": 1
},
{
"id_car_serie": 542,
"id_car_model": 411,
"id_car_generation": 7881,
"name": "Sedan",
"date_create": 1399143315,
"date_update": 1568054529,
"id_car_type": 1
},
{
"id_car_serie": 545,
"id_car_model": 413,
"id_car_generation": 7883,
"name": "Minivan",
"date_create": 1399143318,
"date_update": 1568054530,
"id_car_type": 1
},
{
"id_car_serie": 546,
"id_car_model": 414,
"id_car_generation": 7884,
"name": "Minivan",
"date_create": 1399143320,
"date_update": 1568054530,
"id_car_type": 1
},
{
"id_car_serie": 547,
"id_car_model": 415,
"id_car_generation": 7885,
"name": "Hatchback",
"date_create": 1399143321,
"date_update": 1568054530,
"id_car_type": 1
},
{
"id_car_serie": 548,
"id_car_model": 416,
"id_car_generation": 7886,
"name": "Sedan",
"date_create": 1399145110,
"date_update": 1568054530,
"id_car_type": 1
},
{
"id_car_serie": 559,
"id_car_model": 423,
"id_car_generation": 7159,
"name": "Crossover",
"date_create": 1399145121,
"date_update": 1568054531,
"id_car_type": 1
},
{
"id_car_serie": 560,
"id_car_model": 424,
"id_car_generation": 7888,
"name": "Hatchback",
"date_create": 1399145123,
"date_update": 1568054531,
"id_car_type": 1
},
{
"id_car_serie": 561,
"id_car_model": 425,
"id_car_generation": 7889,
"name": "Hatchback 5-doors",
"date_create": 1399145124,
"date_update": 1461885078,
"id_car_type": 1
},
{
"id_car_serie": 582,
"id_car_model": 432,
"id_car_generation": 7892,
"name": "Hatchback",
"date_create": 1399146016,
"date_update": 1568054532,
"id_car_type": 1
},
{
"id_car_serie": 585,
"id_car_model": 434,
"id_car_generation": 7161,
"name": "Minivan",
"date_create": 1399146019,
"date_update": 1568054532,
"id_car_type": 1
},
{
"id_car_serie": 588,
"id_car_model": 436,
"id_car_generation": 7895,
"name": "Hatchback",
"date_create": 1399146907,
"date_update": 1568054533,
"id_car_type": 1
},
{
"id_car_serie": 589,
"id_car_model": 437,
"id_car_generation": 6922,
"name": "Hatchback",
"date_create": 1399146909,
"date_update": 1568054533,
"id_car_type": 1
},
{
"id_car_serie": 594,
"id_car_model": 441,
"id_car_generation": 7897,
"name": "Hatchback",
"date_create": 1399146914,
"date_update": 1568054534,
"id_car_type": 1
},
{
"id_car_serie": 598,
"id_car_model": 443,
"id_car_generation": 6947,
"name": "Compactvan",
"date_create": 1399146917,
"date_update": 1461885130,
"id_car_type": 1
},
{
"id_car_serie": 602,
"id_car_model": 446,
"id_car_generation": 7901,
"name": "Hatchback",
"date_create": 1399147804,
"date_update": 1568054534,
"id_car_type": 1
},
{
"id_car_serie": 603,
"id_car_model": 447,
"id_car_generation": 134600,
"name": "Hatchback",
"date_create": 1399147806,
"date_update": 1568054534,
"id_car_type": 1
},
{
"id_car_serie": 604,
"id_car_model": 448,
"id_car_generation": 7903,
"name": "SUV 3-doors",
"date_create": 1399147807,
"date_update": 1461885134,
"id_car_type": 1
},
{
"id_car_serie": 618,
"id_car_model": 461,
"id_car_generation": 7911,
"name": "Coupe",
"date_create": 1399149015,
"date_update": 1461885141,
"id_car_type": 1
},
{
"id_car_serie": 619,
"id_car_model": 462,
"id_car_generation": 9293,
"name": "SUV",
"date_create": 1399149016,
"date_update": 1568054535,
"id_car_type": 1
},
{
"id_car_serie": 620,
"id_car_model": 463,
"id_car_generation": 7912,
"name": "Coupe",
"date_create": 1399149017,
"date_update": 1461885141,
"id_car_type": 1
},
{
"id_car_serie": 621,
"id_car_model": 464,
"id_car_generation": 7913,
"name": "Pickup One-and-a-half cabin",
"date_create": 1399149018,
"date_update": 1461885183,
"id_car_type": 1
},
{
"id_car_serie": 647,
"id_car_model": 472,
"id_car_generation": 124181,
"name": "Sedan 2-doors",
"date_create": 1399149612,
"date_update": 1461885189,
"id_car_type": 1
},
{
"id_car_serie": 648,
"id_car_model": 473,
"id_car_generation": 7169,
"name": "Cabriolet",
"date_create": 1399149614,
"date_update": 1568054535,
"id_car_type": 1
},
{
"id_car_serie": 651,
"id_car_model": 475,
"id_car_generation": 7171,
"name": "Coupe",
"date_create": 1399149615,
"date_update": 1568054536,
"id_car_type": 1
},
{
"id_car_serie": 652,
"id_car_model": 476,
"id_car_generation": 7172,
"name": "Coupe",
"date_create": 1399150203,
"date_update": 1461877265,
"id_car_type": 1
},
{
"id_car_serie": 653,
"id_car_model": 477,
"id_car_generation": 7173,
"name": "Coupe",
"date_create": 1399150204,
"date_update": 1461877265,
"id_car_type": 1
},
{
"id_car_serie": 657,
"id_car_model": 481,
"id_car_generation": 7921,
"name": "SUV",
"date_create": 1399150208,
"date_update": 1568054536,
"id_car_type": 1
}
]

View File

@ -13,10 +13,17 @@ from .models import (
CarColors,
ExteriorColors,
InteriorColors,
SaleQuotation
SaleQuotation,
CarLocation,
Organization,
Representative,
SaleQuotationCar
)
from django.forms import ModelMultipleChoiceField
from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
from django.forms import formset_factory
@ -58,14 +65,22 @@ class CarForm(forms.ModelForm, AddClassMixin, ):
def __init__(self, *args, **kwargs):
dealer = kwargs.pop('dealer', None)
super().__init__(*args, **kwargs)
# if dealer:
# self.fields['branch'].queryset = Branch.objects.filter(dealer=dealer)
if 'id_car_make' in self.fields:
queryset = self.fields['id_car_make'].queryset
queryset = self.fields['id_car_make'].queryset.filter(is_sa_import=True)
self.fields['id_car_make'].choices = [
(obj.id_car_make, obj.get_local_name()) for obj in queryset
]
if 'id_car_model' in self.fields:
queryset = self.fields['id_car_model'].queryset
self.fields['id_car_model'].choices = [
(obj.id_car_model, obj.get_local_name()) for obj in queryset
]
if 'vendor' in self.fields:
queryset = self.fields['vendor'].queryset
self.fields['vendor'].choices = [
(obj.pk, obj.get_local_name()) for obj in queryset
]
class CarUpdateForm(forms.ModelForm, AddClassMixin):
@ -103,6 +118,15 @@ class CarFinanceForm(AddClassMixin, forms.ModelForm):
exclude = ['car', 'profit_margin', 'vat_amount', 'total', 'vat_rate']
class CarLocationForm(forms.ModelForm):
class Meta:
model = CarLocation
fields = ['showroom', 'description']
widgets = {
'description': forms.Textarea(attrs={'rows': 2, 'class': 'form-control'}),
}
# Custom Card Form
class CustomCardForm(forms.ModelForm):
custom_date = forms.DateTimeField(
@ -178,3 +202,35 @@ class QuotationForm(forms.ModelForm):
self.fields['cars'].queryset = Car.objects.filter(
finances__isnull=False
).distinct()
class OrganizationForm(forms.ModelForm):
class Meta:
model = Organization
fields = [
'name', 'arabic_name', 'crn', 'vrn',
'phone_number', 'address', 'logo',
]
def __init__(self, *args, **kwargs):
dealer = kwargs.pop('dealer', None)
super().__init__(*args, **kwargs)
class RepresentativeForm(forms.ModelForm):
class Meta:
model = Representative
fields = [
'name', 'arabic_name', 'id_number',
'phone_number', 'address', 'organization'
]
def __init__(self, *args, **kwargs):
dealer = kwargs.pop('dealer', None)
super().__init__(*args, **kwargs)
class CarSelectionTable(tables.Table):
select = tables.CheckBoxColumn(accessor="pk", orderable=False)
class Meta:
model = Car
fields = ['vin', 'year', 'id_car_make', 'id_car_model']
template_name = "django_tables2/bootstrap4.html"

View File

@ -0,0 +1,56 @@
import csv
import os
from django.core.management.base import BaseCommand
from django.db import connection
from inventory.models import CarTrim
class Command(BaseCommand):
help = 'Add id_car_model column to CarTrim model and populate it using a CSV file.'
def handle(self, *args, **options):
# Define the file path relative to the project directory
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
file_path = os.path.join(base_dir, 'data/mappings.csv')
# Step 1: Add the new column if it does not exist
with connection.cursor() as cursor:
try:
cursor.execute("ALTER TABLE inventory_cartrim ADD COLUMN id_car_model INTEGER")
self.stdout.write(self.style.SUCCESS("Column 'id_car_model' added successfully."))
except Exception as e:
self.stdout.write(self.style.WARNING(f"Column 'id_car_model' might already exist: {e}"))
# Step 2: Read and process the CSV file
try:
with open(file_path, mode='r', encoding='utf-8-sig') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
# Extract id_car_serie and id_car_model from the current row
id_car_serie = row.get('id_car_serie')
id_car_model = row.get('id_car_model')
if not id_car_serie or not id_car_model:
self.stdout.write(self.style.WARNING(f"Skipping row with missing data: {row}"))
continue
# Step 3: Update CarTrim rows based on the id_car_serie
updated_count = CarTrim.objects.filter(id_car_serie=id_car_serie).update(id_car_model=id_car_model)
# Output progress
if updated_count > 0:
self.stdout.write(self.style.SUCCESS(
f"Updated {updated_count} rows for id_car_serie={id_car_serie} with id_car_model={id_car_model}."
))
else:
self.stdout.write(self.style.WARNING(
f"No rows found for id_car_serie={id_car_serie}."
))
self.stdout.write(self.style.SUCCESS("All rows have been processed successfully!"))
except FileNotFoundError:
self.stdout.write(self.style.ERROR(f"File not found: {file_path}"))
except Exception as e:
self.stdout.write(self.style.ERROR(f"An error occurred: {e}"))

View File

@ -0,0 +1,39 @@
import os
import csv
from django.core.management.base import BaseCommand
from inventory.models import CarSerie, CarModel
class Command(BaseCommand):
help = "Update or add CarSerie entries from a merged CSV file"
def handle(self, *args, **kwargs):
# Path to the merged CSV file
base_dir = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(base_dir, "../../data/Updated_Merged_Car_Generation_and_Serie_Data.csv") # Adjust the path if needed
if not os.path.exists(file_path):
self.stdout.write(self.style.ERROR(f"File not found: {file_path}"))
return
with open(file_path, newline='', encoding='utf-8') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
try:
car_model = CarModel.objects.get(pk=row['id_car_model'])
except CarModel.DoesNotExist:
self.stdout.write(self.style.WARNING(f"CarModel with ID {row['id_car_model']} not found"))
continue
car_serie, created = CarSerie.objects.update_or_create(
id_car_serie=row['id_car_serie'],
defaults={
'id_car_model': car_model,
'name': row['name'],
'arabic_name': "-",
'year_begin': int(float(row['year_begin'])) if row['year_begin'] else None,
'year_end': int(float(row['year_end'])) if row['year_end'] else None,
},
)
action = "Created" if created else "Updated"
self.stdout.write(self.style.SUCCESS(f"{action} CarSerie with ID {car_serie.id_car_serie}"))

View File

@ -1,6 +1,6 @@
from openai import OpenAI
from django.core.management.base import BaseCommand
from inventory.models import CarSerie
from inventory.models import CarSerie, CarModel, CarMake, CarTrim, CarSpecification, CarSpecificationValue
from django.conf import settings
@ -9,10 +9,10 @@ class Command(BaseCommand):
def handle(self, *args, **kwargs):
client = OpenAI(api_key=settings.OPENAI_API_KEY)
car_serie = CarSerie.objects.filter(id_car_model__car__id_car_make__is_sa_import=True)[:500]
total = car_serie.count()
car_make = CarMake.objects.all()
total = car_make.count()
print(f'Translating {total} names...')
for index, car_serie in enumerate(car_serie, start=1):
for index, car_make in enumerate(car_make, start=1):
try:
completion = client.chat.completions.create(
model="gpt-4",
@ -23,14 +23,14 @@ class Command(BaseCommand):
},
{
"role": "user",
"content": car_serie.name
"content": car_make.name
}
],
temperature=0.3,
)
translation = completion.choices[0].message.content.strip()
car_serie.arabic_name = translation
car_serie.save()
print(f"[{index}/{total}] Translated '{car_serie.name}' to '{translation}'")
car_make.arabic_name = translation
car_make.save()
print(f"[{index}/{total}] Translated '{car_make.name}' to '{translation}'")
except Exception as e:
print(f"Error translating '{car_serie.name}': {e}")
print(f"Error translating '{car_make.name}': {e}")

View File

@ -0,0 +1,34 @@
import os
import csv
from django.core.management.base import BaseCommand
from inventory.models import CarMake
class Command(BaseCommand):
help = "Update or add CarMake entries from a CSV file"
def handle(self, *args, **kwargs):
# Path to the car_make CSV file
base_dir = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(base_dir, "../../data/car_make.csv")
if not os.path.exists(file_path):
self.stdout.write(self.style.ERROR(f"File not found: {file_path}"))
return
with open(file_path, newline='', encoding='utf-8') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
car_make, created = CarMake.objects.get_or_create(
id_car_make=row['id_car_make'],
defaults={
'name': row['name'],
'arabic_name': row.get('arabic_name', ''),
'logo': row.get('logo', None),
'is_sa_import': row.get('is_sa_import', False) in ['1', 'True', 'true'],
},
)
if created:
self.stdout.write(self.style.SUCCESS(f"Added CarMake with ID {car_make.id_car_make}"))
else:
self.stdout.write(self.style.WARNING(f"CarMake with ID {car_make.id_car_make} already exists"))

View File

@ -0,0 +1,37 @@
import os
import csv
from django.core.management.base import BaseCommand
from inventory.models import CarModel, CarMake
class Command(BaseCommand):
help = "Update or add CarModel entries from a CSV file"
def handle(self, *args, **kwargs):
# Path to the car_model CSV file
base_dir = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(base_dir, "../../data/car_model.csv") # Adjust path if needed
if not os.path.exists(file_path):
self.stdout.write(self.style.ERROR(f"File not found: {file_path}"))
return
with open(file_path, newline='', encoding='utf-8') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
try:
car_make = CarMake.objects.get(pk=row['id_car_make'])
except CarMake.DoesNotExist:
self.stdout.write(self.style.WARNING(f"CarMake with ID {row['id_car_make']} not found"))
continue
car_model, created = CarModel.objects.update_or_create(
id_car_model=row['id_car_model'],
defaults={
'id_car_make': car_make,
'name': row['name'],
'arabic_name': row.get('arabic_name', ''),
},
)
action = "Created" if created else "Updated"
self.stdout.write(self.style.SUCCESS(f"{action} CarModel with ID {car_model.id_car_model}"))

View File

@ -0,0 +1,44 @@
import os
import csv
from django.core.management.base import BaseCommand
from inventory.models import CarSpecificationValue, CarSpecification, CarTrim
class Command(BaseCommand):
help = "Update or add CarSpecificationValue entries from a CSV file"
def handle(self, *args, **kwargs):
# Path to the car_specification_value CSV file
base_dir = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(base_dir, "../../data/car_specification_value.csv") # Adjust this path if needed
if not os.path.exists(file_path):
self.stdout.write(self.style.ERROR(f"File not found: {file_path}"))
return
with open(file_path, newline='', encoding='utf-8') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
try:
car_trim = CarTrim.objects.get(pk=row['id_car_trim'])
except CarTrim.DoesNotExist:
self.stdout.write(self.style.WARNING(f"CarTrim with ID {row['id_car_trim']} not found"))
continue
try:
car_specification = CarSpecification.objects.get(pk=row['id_car_specification'])
except CarSpecification.DoesNotExist:
self.stdout.write(self.style.WARNING(f"CarSpecification with ID {row['id_car_specification']} not found"))
continue
car_specification_value, created = CarSpecificationValue.objects.update_or_create(
id_car_specification_value=row['id_car_specification_value'],
defaults={
'id_car_trim': car_trim,
'id_car_specification': car_specification,
'value': row['value'],
'unit': row.get('unit', ''),
},
)
action = "Created" if created else "Updated"
self.stdout.write(self.style.SUCCESS(f"{action} CarSpecificationValue with ID {car_specification_value.id_car_specification_value}"))

View File

@ -0,0 +1,47 @@
import os
import csv
from django.core.management.base import BaseCommand
from inventory.models import CarTrim, CarSerie, CarModel
class Command(BaseCommand):
help = "Update or add CarTrim entries from a CSV file"
def handle(self, *args, **kwargs):
# Path to the car_trim CSV file
base_dir = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(base_dir, "../../data/car_trim.csv") # Adjust path if needed
if not os.path.exists(file_path):
self.stdout.write(self.style.ERROR(f"File not found: {file_path}"))
return
with open(file_path, newline='', encoding='utf-8') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
try:
car_serie = CarSerie.objects.get(pk=row['id_car_serie'])
except CarSerie.DoesNotExist:
self.stdout.write(self.style.WARNING(f"CarSerie with ID {row['id_car_serie']} not found"))
continue
car_model = None
if row['id_car_model']:
try:
car_model = CarModel.objects.get(pk=row['id_car_model'])
except CarModel.DoesNotExist:
self.stdout.write(self.style.WARNING(f"CarModel with ID {row['id_car_model']} not found"))
car_trim, created = CarTrim.objects.update_or_create(
id_car_trim=row['id_car_trim'],
defaults={
'id_car_serie': car_serie,
'id_car_model': car_model,
'name': row['name'],
'arabic_name': row.get('arabic_name', ''),
'start_production_year': int(float(row['start_production_year'])) if row['start_production_year'] else None,
'end_production_year': int(float(row['end_production_year'])) if row['end_production_year'] else None,
},
)
action = "Created" if created else "Updated"
self.stdout.write(self.style.SUCCESS(f"{action} CarTrim with ID {car_trim.id_car_trim}"))

View File

@ -0,0 +1,53 @@
# Generated by Django 5.1.4 on 2024-12-12 12:44
import django.db.models.deletion
import inventory.mixins
import phonenumber_field.modelfields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0016_alter_carfinance_car_alter_customcard_car'),
]
operations = [
migrations.CreateModel(
name='Organization',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, verbose_name='Name')),
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
('crn', models.CharField(max_length=15, verbose_name='Commercial Registration Number')),
('vrn', models.CharField(max_length=15, verbose_name='VAT Registration Number')),
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')),
('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')),
('logo', models.ImageField(blank=True, null=True, upload_to='logos', verbose_name='Logo')),
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='organizations', to='inventory.dealer')),
],
options={
'verbose_name': 'Organization',
'verbose_name_plural': 'Organizations',
},
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
),
migrations.CreateModel(
name='Representative',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, verbose_name='Name')),
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
('id_number', models.CharField(max_length=10, verbose_name='ID Number')),
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')),
('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')),
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='representatives', to='inventory.dealer')),
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='representatives', to='inventory.organization')),
],
options={
'verbose_name': 'Representative',
'verbose_name_plural': 'Representatives',
},
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
),
]

View File

@ -0,0 +1,22 @@
# Generated by Django 5.1.4 on 2024-12-12 12:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0017_organization_representative'),
]
operations = [
migrations.RemoveField(
model_name='representative',
name='organization',
),
migrations.AddField(
model_name='representative',
name='organization',
field=models.ManyToManyField(related_name='representatives', to='inventory.organization'),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 5.1.4 on 2024-12-15 14:41
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0018_remove_representative_organization_and_more'),
]
operations = [
migrations.RemoveField(
model_name='carfinance',
name='total',
),
]

View File

@ -0,0 +1,45 @@
# Generated by Django 5.1.4 on 2024-12-16 01:33
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0019_remove_carfinance_total'),
]
operations = [
migrations.RemoveField(
model_name='carfinance',
name='administration_fee',
),
migrations.RemoveField(
model_name='carfinance',
name='custom_card_fee',
),
migrations.RemoveField(
model_name='carfinance',
name='registration_fee',
),
migrations.RemoveField(
model_name='carfinance',
name='transportation_fee',
),
migrations.AddField(
model_name='cartrim',
name='id_car_model',
field=models.ForeignKey(blank=True, db_column='id_car_model', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel'),
),
migrations.AlterField(
model_name='carfinance',
name='car',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='financial_details', to='inventory.car'),
),
migrations.AlterField(
model_name='carfinance',
name='vat_amount',
field=models.DecimalField(decimal_places=2, editable=False, max_digits=14, verbose_name='VAT Amount'),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 5.1.4 on 2024-12-16 10:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0020_remove_carfinance_administration_fee_and_more'),
]
operations = [
migrations.AddField(
model_name='carserie',
name='year_begin',
field=models.IntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='carserie',
name='year_end',
field=models.IntegerField(blank=True, null=True),
),
]

View File

@ -0,0 +1,49 @@
# Generated by Django 5.1.4 on 2024-12-16 12:54
import django.db.models.deletion
from decimal import Decimal
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0021_carserie_year_begin_carserie_year_end'),
]
operations = [
migrations.RemoveField(
model_name='cartrim',
name='id_car_model',
),
migrations.AddField(
model_name='carfinance',
name='administration_fee',
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Administration Fee'),
),
migrations.AddField(
model_name='carfinance',
name='custom_card_fee',
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Custom Card Fee'),
),
migrations.AddField(
model_name='carfinance',
name='registration_fee',
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Registration Fee'),
),
migrations.AddField(
model_name='carfinance',
name='transportation_fee',
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Transportation Fee'),
),
migrations.AlterField(
model_name='carfinance',
name='car',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='finances', to='inventory.car'),
),
migrations.AlterField(
model_name='carfinance',
name='vat_amount',
field=models.DecimalField(decimal_places=2, editable=False, max_digits=14, verbose_name='Vat Amount'),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 5.1.4 on 2024-12-16 19:55
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0022_remove_cartrim_id_car_model_and_more'),
]
operations = [
migrations.AddField(
model_name='cartrim',
name='id_car_model',
field=models.ForeignKey(blank=True, db_column='id_car_model', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel'),
),
]

View File

@ -1,4 +1,5 @@
from uuid import uuid4
from django.conf import settings
from django.db import models, transaction
from django.contrib.auth.models import User
from django.db.models.signals import pre_save, post_save
@ -15,11 +16,13 @@ from django_ledger.models import (
CustomerModel,
ItemModelQuerySet,
)
from django.db.models import Sum
from decimal import Decimal, InvalidOperation
from django.core.exceptions import ValidationError
from phonenumber_field.modelfields import PhoneNumberField
from django.contrib.contenttypes.models import ContentType
from django.utils.timezone import now
from .mixins import LocalizedNameMixin
@ -57,6 +60,8 @@ class CarSerie(models.Model, LocalizedNameMixin):
)
name = models.CharField(max_length=255)
arabic_name = models.CharField(max_length=255)
year_begin = models.IntegerField(blank=True, null=True)
year_end = models.IntegerField(blank=True, null=True)
def __str__(self):
return self.name
@ -74,6 +79,7 @@ class CarTrim(models.Model, LocalizedNameMixin):
arabic_name = models.CharField(max_length=255)
start_production_year = models.IntegerField(blank=True, null=True)
end_production_year = models.IntegerField(blank=True, null=True)
id_car_model = models.ForeignKey(CarModel, models.DO_NOTHING, db_column='id_car_model', blank=True, null=True)
def __str__(self):
return self.name
@ -211,138 +217,174 @@ class Car(models.Model):
active_reservations = self.reservations.filter(reserved_until__gt=now())
return active_reservations.exists()
@property
def finance(self):
"""Fetch the first related CarFinance object, or return None."""
return self.finances # Assuming a related_name of 'finances'
def _get_financial_value(self, attribute):
"""Reusable method to safely get financial values."""
finance = self.finance
return getattr(finance, attribute, Decimal('0.00')) if finance else Decimal('0.00')
def _calculate_vat(self, value):
"""Helper to calculate VAT dynamically for a given value."""
vat_rate = getattr(settings, 'VAT_RATE', Decimal('0.15')) # Default VAT rate
return (value * vat_rate).quantize(Decimal('0.01'))
@property
def cost_price(self):
return self._get_financial_value('cost_price')
@property
def selling_price(self):
finance = self.finances.first()
return finance.selling_price if finance else Decimal("0.00")
return self._get_financial_value('selling_price')
@property
def registration_fee(self):
return self._get_financial_value('registration_fee')
@property
def administration_fee(self):
return self._get_financial_value('administration_fee')
@property
def transportation_fee(self):
return self._get_financial_value('transportation_fee')
@property
def custom_card_fee(self):
return self._get_financial_value('custom_card_fee')
@property
def discount_amount(self):
finance = self.finances.first()
return finance.discount_amount if finance else Decimal("0.00")
return self._get_financial_value('discount_amount')
@property
def vat_amount(self):
finance = self.finances.first()
return finance.vat_amount if finance else Decimal("0.00")
"""Dynamically calculate VAT for the selling price after discount."""
price_after_discount = self.selling_price - self.discount_amount
return self._calculate_vat(price_after_discount)
@property
def administration_fee_vat(self):
return self._calculate_vat(self.administration_fee)
@property
def transportation_fee_vat(self):
return self._calculate_vat(self.transportation_fee)
@property
def custom_card_fee_vat(self):
return self._calculate_vat(self.custom_card_fee)
@property
def total_vat_amount(self):
"""Sum up the VAT for all applicable fields."""
return (
self.vat_amount +
self.administration_fee_vat +
self.transportation_fee_vat +
self.custom_card_fee_vat
)
@property
def total(self):
finance = self.finances.first()
return finance.total if finance else Decimal("0.00")
"""Calculate the total amount including VAT."""
price_after_discount = self.selling_price - self.discount_amount
subtotal = (
price_after_discount +
self.registration_fee +
self.administration_fee +
self.transportation_fee +
self.custom_card_fee
)
return subtotal + self.total_vat_amount
# class CarData(models.Model):
# vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN"))
# make = models.CharField(max_length=255, verbose_name=_("Make"))
# make_ar = models.CharField(max_length=255, verbose_name=_("Make Arabic"))
# model = models.CharField(max_length=255, verbose_name=_("Model"))
# model_ar = models.CharField(max_length=255, verbose_name=_("Model Arabic"))
# year = models.IntegerField(verbose_name=_("Year"))
# series = models.CharField(max_length=255,verbose_name=_("Series"))
# trim = models.CharField(max_length=255,verbose_name=_("Trim"))
# specs = models.JsonField
# status = models.CharField(
# max_length=10,
# choices=CarStatusChoices,
# default=CarStatusChoices.AVAILABLE,
# verbose_name=_("Status")
# )
# stock_type = models.CharField(
# max_length=10,
# choices=CarStockTypeChoices,
# default=CarStockTypeChoices.NEW,
# verbose_name=_("Stock Type")
# )
# remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks"))
# mileage = models.IntegerField(blank=True, null=True, verbose_name=_("Mileage"))
# receiving_date = models.DateTimeField(verbose_name=_("Receiving Date"))
class CarReservation(models.Model):
car = models.ForeignKey(
"Car", on_delete=models.CASCADE, related_name="reservations"
)
reserved_by = models.ForeignKey(User, on_delete=models.CASCADE)
reserved_at = models.DateTimeField(auto_now_add=True)
reserved_until = models.DateTimeField()
car = models.ForeignKey('Car', on_delete=models.CASCADE, related_name='reservations', verbose_name=_("Car"))
reserved_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reservations', verbose_name=_("Reserved By"))
reserved_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Reserved At"))
reserved_until = models.DateTimeField(verbose_name=_("Reserved Until"))
def is_active(self):
return self.reserved_until > now()
class Meta:
unique_together = ("car", "reserved_until")
ordering = ["-reserved_at"]
unique_together = ('car', 'reserved_until')
ordering = ['-reserved_at']
verbose_name = _("Car Reservation")
verbose_name_plural = _("Car Reservations")
# Car Finance Model
class CarFinance(models.Model):
car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name="finances")
cost_price = models.DecimalField(
max_digits=14, decimal_places=2, verbose_name=_("Cost Price")
)
selling_price = models.DecimalField(
max_digits=14, decimal_places=2, verbose_name=_("Selling Price")
)
profit_margin = models.DecimalField(
max_digits=14, decimal_places=2, verbose_name=_("Profit Margin"), editable=False
)
vat_amount = models.DecimalField(
max_digits=14, decimal_places=2, verbose_name=_("Vat Amount"), editable=False
)
discount_amount = models.DecimalField(
max_digits=14,
decimal_places=2,
verbose_name=_("Discount Amount"),
default=Decimal("0.00"),
)
registration_fee = models.DecimalField(
max_digits=14,
decimal_places=2,
verbose_name=_("Registration Fee"),
default=Decimal("0.00"),
)
administration_fee = models.DecimalField(
max_digits=14,
decimal_places=2,
verbose_name=_("Administration Fee"),
default=Decimal("0.00"),
)
transportation_fee = models.DecimalField(
max_digits=14,
decimal_places=2,
verbose_name=_("Transportation Fee"),
default=Decimal("0.00"),
)
custom_card_fee = models.DecimalField(
max_digits=14,
decimal_places=2,
verbose_name=_("Custom Card Fee"),
default=Decimal("0.00"),
)
vat_rate = models.DecimalField(
max_digits=14,
decimal_places=2,
default=Decimal("0.15"),
verbose_name=_("VAT Rate"),
)
total = models.DecimalField(
max_digits=14, decimal_places=2, default=Decimal("0.00"), null=True, blank=True
)
car = models.OneToOneField(Car, on_delete=models.CASCADE, related_name='finances')
cost_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Cost Price"))
selling_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Selling Price"))
profit_margin = models.DecimalField(max_digits=14,
decimal_places=2,
verbose_name=_("Profit Margin"),
editable=False)
vat_amount = models.DecimalField(max_digits=14,
decimal_places=2,
verbose_name=_("Vat Amount"),
editable=False)
discount_amount = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Discount Amount"),
default=Decimal('0.00'))
registration_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Registration Fee"),
default=Decimal('0.00'))
administration_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Administration Fee"),
default=Decimal('0.00'))
transportation_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Transportation Fee"),
default=Decimal('0.00'))
custom_card_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Custom Card Fee"),
default=Decimal('0.00'))
def __str__(self):
return f"{self.selling_price}"
return f"Car: {self.car}, Selling Price: {self.selling_price}"
def save(self, *args, **kwargs):
# vat_rate = settings.VAT_RATE
self.full_clean()
try:
services = (
self.administration_fee + self.transportation_fee + self.custom_card_fee
)
price_after_discount = self.selling_price - self.discount_amount
total_vat_amount = (price_after_discount + services) * self.vat_rate
self.vat_amount = self.selling_price * self.vat_rate
self.profit_margin = (
self.selling_price
- self.cost_price
- self.discount_amount
- self.registration_fee
)
self.total = (
price_after_discount
+ services
+ total_vat_amount
+ self.registration_fee
)
self.profit_margin = price_after_discount - self.cost_price
except InvalidOperation as e:
raise ValidationError(_("Invalid decimal operation: %s") % str(e))
super().save(*args, **kwargs)
class Meta:
verbose_name = _("Car Financial Details")
@property
def total_vat_amount(self):
services = (
self.administration_fee + self.transportation_fee + self.custom_card_fee
)
price_after_discount = self.selling_price - self.discount_amount
return (price_after_discount + services) * self.vat_rate
verbose_name_plural = _("Car Financial Details")
class ExteriorColors(models.Model, LocalizedNameMixin):
@ -392,12 +434,7 @@ class CarColors(models.Model):
# Custom Card Model
class CustomCard(models.Model):
car = models.ForeignKey(
Car,
on_delete=models.CASCADE,
related_name="custom_cards",
verbose_name=_("Car"),
)
car = models.OneToOneField(Car, on_delete=models.CASCADE, related_name='custom_cards', verbose_name=_("Car"))
custom_number = models.CharField(max_length=255, verbose_name=_("Custom Number"))
custom_date = models.DateField(verbose_name=_("Custom Date"))
@ -409,6 +446,55 @@ class CustomCard(models.Model):
return f"{self.car} - {self.custom_number}"
class CarLocation(models.Model):
car = models.OneToOneField(
Car,
on_delete=models.CASCADE,
related_name='location',
verbose_name=_("Car")
)
owner = models.ForeignKey(
'Dealer',
on_delete=models.CASCADE,
related_name='owned_cars',
verbose_name=_("Owner"),
help_text=_("Dealer who owns the car.")
)
showroom = models.ForeignKey(
'Dealer',
on_delete=models.CASCADE,
related_name='showroom_cars',
verbose_name=_("Showroom"),
help_text=_("Dealer where the car is displayed (can be the owner).")
)
description = models.TextField(
blank=True,
null=True,
verbose_name=_("Description"),
help_text=_("Optional description about the showroom placement.")
)
created_at = models.DateTimeField(
auto_now_add=True,
verbose_name=_("Created At")
)
updated_at = models.DateTimeField(
auto_now=True,
verbose_name=_("Last Updated")
)
class Meta:
verbose_name = _("Car Location")
verbose_name_plural = _("Car Locations")
def __str__(self):
return f"Car: {self.car}, Showroom: {self.showroom}, Owner: {self.owner}"
def is_owner_showroom(self):
"""
Returns True if the showroom is the same as the owner.
"""
return self.owner == self.showroom
# Car Registration Model
class CarRegistration(models.Model):
car = models.ForeignKey(
@ -585,6 +671,40 @@ class Customer(models.Model):
return f"{self.first_name} {self.middle_name} {self.last_name}"
class Organization(models.Model, LocalizedNameMixin):
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name='organizations')
name = models.CharField(max_length=255, verbose_name=_("Name"))
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
crn = models.CharField(max_length=15, verbose_name=_("Commercial Registration Number"))
vrn = models.CharField(max_length=15, verbose_name=_("VAT Registration Number"))
phone_number = PhoneNumberField(region='SA', verbose_name=_("Phone Number"))
address = models.CharField(max_length=200, blank=True, null=True, verbose_name=_("Address"))
logo = models.ImageField(upload_to="logos", blank=True, null=True, verbose_name=_("Logo"))
class Meta:
verbose_name = _("Organization")
verbose_name_plural = _("Organizations")
def __str__(self):
return self.name
class Representative(models.Model, LocalizedNameMixin):
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name='representatives')
name = models.CharField(max_length=255, verbose_name=_("Name"))
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
id_number = models.CharField(max_length=10, verbose_name=_("ID Number"))
phone_number = PhoneNumberField(region='SA', verbose_name=_("Phone Number"))
address = models.CharField(max_length=200, blank=True, null=True, verbose_name=_("Address"))
organization = models.ManyToManyField(Organization, related_name='representatives')
class Meta:
verbose_name = _("Representative")
verbose_name_plural = _("Representatives")
def __str__(self):
return self.name
class SaleQuotation(models.Model):
STATUS_CHOICES = [
("DRAFT", _("Draft")),
@ -613,6 +733,11 @@ class SaleQuotation(models.Model):
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At"))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At"))
@property
def total_quantity(self):
total_quantity = self.quotation_cars.aggregate(total=Sum('quantity'))['total']
return total_quantity or 0
def confirm(self):
"""Confirm the quotation and lock financial details."""
if self.status != "DRAFT":
@ -638,14 +763,20 @@ class SaleQuotationCar(models.Model):
related_name="quotation_cars",
verbose_name=_("Quotation"),
)
car = models.ForeignKey(Car, on_delete=models.CASCADE, verbose_name=_("Car"))
# dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="sale_cars", verbose_name=_("Dealer"))
car = models.ForeignKey(
Car,
on_delete=models.CASCADE,
verbose_name=_("Car")
)
quantity = models.PositiveIntegerField(default=1, verbose_name=_("Quantity"))
# price = models.DecimalField(decimal_places=2, max_digits=10, verbose_name=_("Price"), editable=False)
def get_financial_details(self):
"""Retrieve financial details dynamically from CarFinance."""
car_finance = self.car.finances.first()
@property
def financial_details(self):
"""
Retrieve financial details dynamically from CarFinance.
Returns a dictionary with all financial fields for better access.
"""
car_finance = self.car.finances
if not car_finance:
return None
@ -655,11 +786,20 @@ class SaleQuotationCar(models.Model):
"transportation_fee": car_finance.transportation_fee,
"custom_card_fee": car_finance.custom_card_fee,
"registration_fee": car_finance.registration_fee,
"vat_rate": car_finance.vat_rate,
"discount_amount": car_finance.discount_amount,
"total_amount": car_finance.total,
"vat_amount": car_finance.vat_amount,
# "total_amount": car_finance.total,
}
@property
def total_price(self):
"""
Calculate total price dynamically based on quantity and selling price.
"""
car_finance = self.car.finances
if not car_finance:
return Decimal("0.00")
return car_finance.selling_price * self.quantity
def __str__(self):
return f"{self.car} - Quotation #{self.quotation.id}"

View File

@ -4,6 +4,9 @@ Services module
import requests
import json
from django_ledger.models import EntityModel
from .utils import get_jwt_token
from pyvin import VIN
from django.conf import settings
@ -12,8 +15,9 @@ from .models import Car,CarMake,CarModel
def get_make(item):
data = CarMake.objects.filter(name__iexact=item).first()
data = CarMake.objects.filter(name__iexact=item).first()
return data
def get_model(item,make):
data = make.carmodel_set.filter(name__iexact=item).first()
if not data:
@ -37,7 +41,6 @@ def decodevin(vin):
return None
def decode_vin(vin):
v = VIN(vin)
data = {}
@ -51,20 +54,12 @@ def decode_vin(vin):
return data if all([x for x in data.values()]) else None
# vehicle-info
# c2729afb
# 6d397471920412d672af1b8a02ca52ea
# option-info
# 367974ed
# 046b0412c1b4d3f8c39ec6375d6f3030
def elm(vin):
headers = {
"Content-Type": "application/json",
"app-id": "c2729afb",
"app-key": "6d397471920412d672af1b8a02ca52ea",
"client-id": "94142c27-2536-47e9-8e28-9ca7728b9442",
"app-id": settings.ELM_APP_ID,
"app-key": settings.ELM_APP_KEY,
"client-id": settings.ELM_CLIENT_ID,
}
url = (
"https://vehicle-maintenance.api.elm.sa/api/v1/vehicles/vehicle-info?vin=" + vin
@ -84,24 +79,7 @@ def elm(vin):
return data if all([x for x in data.values()]) else None
def translate(content, *args, **kwargs):
client = OpenAI(api_key=settings.OPENAI_API_KEY)
completion = client.chat.completions.create(
model="gpt-4",
messages=[
{
"role": "system",
"content": "You are a translation assistant that translates English to Arabic.",
},
{"role": "user", "content": content},
],
temperature=0.3,
)
translation = completion.choices[0].message.content.strip()
return translation
def calculate_stock_value():
cars = Car.objects.all()
total_value = sum(car.selling_price for car in cars)
return total_value
def get_ledger_data(request):
entity = EntityModel.objects.filter(name="Marwan2").first()
data = entity.get_items_all()
print(data)

View File

@ -2,8 +2,16 @@ from random import randint
from django.db.models.signals import post_save, post_delete,pre_delete
from django.dispatch import receiver
from django_ledger.models import EntityModel, VendorModel, CustomerModel, UnitOfMeasureModel
from django_ledger.models import (EntityModel,
VendorModel,
CustomerModel,
UnitOfMeasureModel,
AccountModel,
ItemModelAbstract
)
from django.utils.translation import gettext_lazy as _
from . import models
@ -12,6 +20,26 @@ def remove_user_account(sender, instance, **kwargs):
user = instance.user
if user:
user.delete()
@receiver(post_save, sender=models.Car)
def create_car_location(sender, instance, created, **kwargs):
"""
Signal to create or update the car's location when a car instance is saved.
"""
try:
if created:
if instance.dealer is None:
raise ValueError(f"Cannot create CarLocation for car {instance.vin}: dealer is missing.")
models.CarLocation.objects.create(
car=instance,
owner=instance.dealer,
showroom=instance.dealer,
description=f"Initial location set for car {instance.vin}."
)
print("Car Location created")
except Exception as e:
print(f"Failed to create CarLocation for car {instance.vin}: {e}")
@receiver(post_save, sender=models.CarReservation)
def update_car_status_on_reservation(sender, instance, created, **kwargs):
@ -24,7 +52,7 @@ def update_car_status_on_reservation(sender, instance, created, **kwargs):
@receiver(post_delete, sender=models.CarReservation)
def update_car_status_on_reservation_delete(sender, instance, **kwargs):
car = instance.car
if not car.get_current_reservation():
if not car.is_reserved():
car.status = models.CarStatusChoices.AVAILABLE
car.save()
@ -36,10 +64,10 @@ def create_ledger_entity(sender, instance, created, **kwargs):
entity = EntityModel.objects.create(
name=instance.name,
admin=instance.user,
address_1=instance.address,
# address_1=instance.address,
accrual_method=False,
fy_start_month=1,
accrual_method=True,
depth=0,
# depth=0,
)
default_coa = entity.create_chart_of_accounts(assign_as_default=True,
@ -54,25 +82,56 @@ def create_ledger_entity(sender, instance, created, **kwargs):
# )
# entity.create_account(
# coa_model=coa,
# code=1100,
# role='asset_ca_recv',
# name=_('Accounts Receivable'),
# balance_type="debit",
# )
# entity.create_account(
# coa_model=coa,
# code=1200,
# role='asset_ca_inv',
# name=_('Inventory'),
# balance_type="debit",
# active=True)
#
# entity.create_account(
# coa_model=coa,
# code=2010,
# role='lia_cl_acc_payable',
# name=_('Accounts Payable'),
# balance_type="credit",
# active=True)
#
# entity.create_account(
# coa_model=coa,
# code=4010,
# role='in_operational',
# name=_('Sales Income'),
# balance_type="credit",
# active=True)
#
# entity.create_account(
# coa_model=coa,
# code=5010,
# role='cogs_regular',
# name=_('Cost of Goods Sold'),
# balance_type="debit",
# active=True)
if default_coa:
entity.populate_default_coa(activate_accounts=True, coa_model=default_coa)
uom_name = _("Unit")
unit_abbr = _("U")
entity.create_uom(uom_name, unit_abbr)
# uom_name = _("Unit")
# unit_abbr = _("U")
#
# entity.create_uom(uom_name, unit_abbr)
print(f"Ledger entity created for Dealer: {instance.name}")
# # Create Vendor
# Create Vendor
@receiver(post_save, sender=models.Vendor)
def create_ledger_vendor(sender, instance, created, **kwargs):
@ -119,55 +178,64 @@ def create_customer(sender, instance, created, **kwargs):
# Create Item
@receiver(post_save, sender=models.Car)
def create_item_model(sender, instance, created, **kwargs):
item_name = f"{instance.year} - {instance.id_car_make} - {instance.id_car_model} - {instance.id_car_trim}"
uom_name = _("Car")
unit_abbr = _("C")
uom, uom_created = UnitOfMeasureModel.objects.get_or_create(
name=uom_name,
unit_abbr=unit_abbr
)
if uom_created:
print(f"UOM created: {uom_name}")
else:
print(f"Using existing UOM: {uom_name}")
entity = EntityModel.objects.filter(name=instance.dealer.name).first()
inventory_account = AccountModel.objects.first()
cogs_account = AccountModel.objects.first()
earnings_account = AccountModel.objects.first()
entity.create_i
item = ItemModel.objects.create(
entity=entity,
uom=uom,
name=item_name,
item_role=ItemModelAbstract.ITEM_ROLE_INVENTORY,
item_type=ItemModelAbstract.ITEM_TYPE_MATERIAL,
item_id=instance.vin,
sold_as_unit=True,
inventory_received=1.00,
inventory_received_value=0.00,
inventory_account=inventory_account,
for_inventory=True,
is_product_or_service=True,
cogs_account=cogs_account,
earnings_account=earnings_account,
is_active=True,
additional_info={
"remarks": instance.remarks,
"status": instance.status,
"stock_type": instance.stock_type,
"mileage": instance.mileage,
},
)
print(f"ItemModel {'created' if created else 'updated'} for Car: {item.name}")
# @receiver(post_save, sender=models.Car)
# def create_item_model(sender, instance, created, **kwargs):
# item_name = f"{instance.year} - {instance.id_car_make} - {instance.id_car_model} - {instance.id_car_trim}"
# uom_name = _("Car")
# unit_abbr = _("C")
#
# uom, uom_created = UnitOfMeasureModel.objects.get_or_create(
# name=uom_name,
# unit_abbr=unit_abbr
# )
#
# if uom_created:
# print(f"UOM created: {uom_name}")
# else:
# print(f"Using existing UOM: {uom_name}")
#
# entity = EntityModel.objects.filter(name=instance.dealer.name).first()
#
# inventory_account = AccountModel.objects.first()
# cogs_account = AccountModel.objects.first()
# earnings_account = AccountModel.objects.first()
#
# entity.create_item_product(
# item_name=item_name,
# item_role=ItemModelAbstract.ITEM_ROLE_PRODUCT,
# item_type=ItemModelAbstract.ITEM_TYPE_MATERIAL,
# item_id=instance.vin,
# sold_as_unit=True,
# inventory_received=1.00,
# inventory_received_value=0.00,
# inventory_account=inventory_account,
# for_inventory=True,)
#
# item = ItemModel.objects.create(
# entity=entity,
# uom=uom,
# name=item_name,
# item_role=ItemModelAbstract.ITEM_ROLE_INVENTORY,
# item_type=ItemModelAbstract.ITEM_TYPE_MATERIAL,
# item_id=instance.vin,
# sold_as_unit=True,
# inventory_received=1.00,
# inventory_received_value=0.00,
# inventory_account=inventory_account,
# for_inventory=True,
# is_product_or_service=True,
# cogs_account=cogs_account,
# earnings_account=earnings_account,
# is_active=True,
# additional_info={
# "remarks": instance.remarks,
# "status": instance.status,
# "stock_type": instance.stock_type,
# "mileage": instance.mileage,
# },
# )
#
# print(f"ItemModel {'created' if created else 'updated'} for Car: {item.name}")
#
#
# # update price - CarFinance

View File

@ -2,8 +2,10 @@ from django.utils.html import format_html
from django.conf import settings
from . import models
import django_tables2 as tables
from django_tables2.utils import A
import django_tables2 as tables
from django import forms
from inventory.models import Car, SaleQuotation, SaleQuotationCar
class ImageColumn(tables.Column):
@ -16,3 +18,4 @@ class CustomerTable(tables.Table):
model = models.Customer
first_name = tables.Column()

View File

@ -55,17 +55,19 @@ urlpatterns = [
path('cars/<int:pk>/update/', views.CarUpdateView.as_view(), name='car_update'),
path('cars/<int:pk>/delete/', views.CarDeleteView.as_view(), name='car_delete'),
path('cars/<int:car_pk>/finance/create/', views.CarFinanceCreateView.as_view(), name='car_finance_create'),
path('cars/finance/update/<int:pk>/', views.CarFinanceUpdateView.as_view(), name='car_finance_update'),
path('cars/finance/<int:pk>/update/', views.CarFinanceUpdateView.as_view(), name='car_finance_update'),
path('cars/add/', views.CarCreateView.as_view(), name='car_add'),
path('ajax/', views.AjaxHandlerView.as_view(), name='ajax_handler'),
path('cars/<int:car_pk>/add-color/', views.CarColorCreate.as_view(), name='add_color'),
path('car/<int:car_pk>/location/add/', views.CarLocationCreateView.as_view(), name='add_car_location'),
path('car/<int:pk>/location/update/', views.CarLocationUpdateView.as_view(), name='transfer'),
# path('cars/<int:car_pk>/colors/<int:pk>/update/',views.CarColorUpdateView.as_view(),name='color_update'),
path('cars/reserve/<int:car_id>/', views.reserve_car_view, name='reserve_car'),
path('reservations/<int:reservation_id>/', views.manage_reservation, name='reservations'),
path('cars/<int:car_pk>/add-custom-card/', views.CustomCardCreateView.as_view(), name='add_custom_card'),
# Sales URLs
# Sales URLs quotation_create
path('sales/quotations/create/', views.QuotationCreateView.as_view(), name='quotation_create'),
path('sales/quotations/<int:pk>/', views.QuotationDetailView.as_view(), name='quotation_detail'),
path('sales/quotations/', views.QuotationListView.as_view(), name='quotation_list'),
@ -78,10 +80,27 @@ urlpatterns = [
path('user/<int:pk>/', views.UserDetailView.as_view(), name='user_detail'),
path('user/', views.UserListView.as_view(), name='user_list'),
path('user/<int:pk>/confirm/', views.UserDeleteview, name='user_delete'),
# Organization URLs
path('organizations/', views.OrganizationListView.as_view(), name='organization_list'),
path('organizations/<int:pk>/', views.OrganizationDetailView.as_view(), name='organization_detail'),
path('organizations/create/', views.OrganizationCreateView.as_view(), name='organization_create'),
path('organizations/<int:pk>/update/', views.OrganizationUpdateView.as_view(), name='organization_update'),
path('organizations/<int:pk>/delete/', views.OrganizationDeleteView.as_view(), name='organization_delete'),
# Representative URLs
path('representatives/', views.RepresentativeListView.as_view(), name='representative_list'),
path('representatives/<int:pk>/', views.RepresentativeDetailView.as_view(), name='representative_detail'),
path('representatives/create/', views.RepresentativeCreateView.as_view(), name='representative_create'),
path('representatives/<int:pk>/update/', views.RepresentativeUpdateView.as_view(), name='representative_update'),
path('representatives/<int:pk>/delete/', views.RepresentativeDeleteView.as_view(), name='representative_delete'),
]
handler404 = 'inventory.views.custom_page_not_found_view'
handler500 = 'inventory.views.custom_error_view'
handler403 = 'inventory.views.custom_permission_denied_view'
handler400 = 'inventory.views.custom_bad_request_view'
handler400 = 'inventory.views.custom_bad_request_view'

View File

@ -1,5 +1,7 @@
import logging
import json
from decimal import Decimal
from django.views.decorators.csrf import csrf_exempt
from vin import VIN
from django.contrib.auth.mixins import LoginRequiredMixin
@ -27,6 +29,7 @@ from django.db.models import Sum, F, Count
from inventory.mixins import AddDealerInstanceMixin
from .services import elm, decodevin,get_make,get_model,normalize_name
from .services import elm, decodevin, get_make, get_model, normalize_name, get_ledger_data
from . import models, forms
from django_tables2.export.views import ExportMixin
from django.contrib.auth.mixins import PermissionRequiredMixin
@ -71,6 +74,7 @@ def switch_language(request):
class HomeView(LoginRequiredMixin, TemplateView):
template_name = 'index.html'
def dispatch(self, request, *args, **kwargs):
if not any(hasattr(request.user, attr) for attr in ['dealer', 'subdealer']) or not request.user.is_authenticated:
messages.error(request, _('You are not associated with any dealer.'))
@ -175,21 +179,36 @@ class AjaxHandlerView(LoginRequiredMixin, View):
def get_models(self, request):
make_id = request.GET.get('make_id')
car_models = models.CarModel.objects.filter(id_car_make=make_id).values('id_car_model', 'name', 'arabic_name')
car_models = (models.CarModel.objects.filter(id_car_make=make_id)
.values('id_car_model', 'name', 'arabic_name')
.order_by('name'))
return JsonResponse(list(car_models), safe=False)
def get_series(self, request):
model_id = request.GET.get('model_id')
year = request.GET.get('year')
# Validate inputs
if not model_id or not year:
return JsonResponse({'error': 'Missing required parameters: model_id or year'}, status=400)
try:
year = int(year)
except ValueError:
return JsonResponse({'error': 'Invalid year format'}, status=400)
series = models.CarSerie.objects.filter(
id_car_model=model_id,
).values('id_car_serie', 'name', 'arabic_name')
id_car_model=model_id).values(
'id_car_serie', 'name', 'arabic_name'
)
return JsonResponse(list(series), safe=False)
def get_trims(self, request):
serie_id = request.GET.get('serie_id')
model_id = request.GET.get('model_id')
trims = models.CarTrim.objects.filter(
id_car_serie=serie_id
).values('id_car_trim', 'name', 'arabic_name')
id_car_serie=serie_id, id_car_model=model_id).values('id_car_trim', 'name', 'arabic_name')
return JsonResponse(list(trims), safe=False)
def get_specifications(self, request):
@ -401,6 +420,36 @@ class CarDeleteView(LoginRequiredMixin,SuccessMessageMixin, DeleteView):
template_name = 'inventory/car_confirm_delete.html'
success_url = reverse_lazy('inventory_stats')
def delete(self, request, *args, **kwargs):
messages.success(request, _('Car deleted successfully.'))
return super().delete(request, *args, **kwargs)
class CarLocationCreateView(CreateView):
model = models.CarLocation
form_class = forms.CarLocationForm
template_name = 'inventory/car_location_form.html'
def get_success_url(self):
return reverse_lazy('car_detail', kwargs={'pk': self.object.car.pk})
def form_valid(self, form):
form.instance.car = get_object_or_404(models.Car, pk=self.kwargs['car_pk'])
form.instance.owner = self.request.user.dealer
form.save()
messages.success(self.request, 'Car saved successfully.')
return super().form_valid(form)
class CarLocationUpdateView(UpdateView):
model = models.CarLocation
form_class = forms.CarLocationForm
template_name = 'inventory/car_location_form.html'
def get_success_url(self):
return reverse_lazy('car_detail', kwargs={'pk': self.object.car.pk})
class CustomCardCreateView(LoginRequiredMixin, CreateView):
model = models.CustomCard
form_class = forms.CustomCardForm
@ -616,7 +665,7 @@ class QuotationCreateView(LoginRequiredMixin,PermissionRequiredMixin, CreateView
quotation = form.save()
selected_cars = form.cleaned_data.get("cars")
for car in selected_cars:
car_finance = car.finances.first()
car_finance = car.finances
if car_finance:
models.SaleQuotationCar.objects.create(
quotation=quotation,
@ -649,6 +698,58 @@ class QuotationDetailView(LoginRequiredMixin,PermissionRequiredMixin, DetailView
context_object_name = "quotation"
permission_required = ('inventory.view_salequotation',)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
quotation = self.object
# Totals Calculation
context['vat_rate'] = settings.VAT_RATE
context['total_sales_before_vat'] = sum(item.car.selling_price * item.quantity for item in quotation.quotation_cars.all())
context['vat_amount'] = sum(item.car.vat_amount * item.quantity for item in quotation.quotation_cars.all())
context['total_sales_after_vat'] = context['total_sales_before_vat'] + context['vat_amount']
# Additional Costs
total_quantity = quotation.total_quantity
context['administration_fee'] = sum(item.car.administration_fee for item in quotation.quotation_cars.all())
context['transportation_fee'] = sum(item.car.transportation_fee for item in quotation.quotation_cars.all())
context['custom_card_fee'] = sum(item.car.custom_card_fee for item in quotation.quotation_cars.all())
context['registration_fee'] = sum(item.car.registration_fee for item in quotation.quotation_cars.all())
context['administration_fee_vat'] = sum(item.car.administration_fee_vat for item in quotation.quotation_cars.all())
context['transportation_fee_vat'] = sum(item.car.transportation_fee_vat for item in quotation.quotation_cars.all())
context['custom_card_fee_vat'] = sum(item.car.custom_card_fee_vat for item in quotation.quotation_cars.all())
context['administration_fee_total'] = sum(item.car.administration_fee + context['administration_fee_vat'] for item in quotation.quotation_cars.all())
context['transportation_fee_total'] = sum(item.car.transportation_fee + context['transportation_fee_vat'] for item in quotation.quotation_cars.all())
context['custom_card_fee_total'] = sum(item.car.custom_card_fee + context['custom_card_fee_vat'] for item in quotation.quotation_cars.all())
context['registration_fee_total'] = sum(item.car.registration_fee * total_quantity for item in quotation.quotation_cars.all())
return context
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
quotation = self.object
# Totals Calculation
context['vat_rate'] = settings.VAT_RATE
context['total_sales_before_vat'] = sum(item.car.selling_price * item.quantity for item in quotation.quotation_cars.all())
context['vat_amount'] = sum(item.car.vat_amount * item.quantity for item in quotation.quotation_cars.all())
context['total_sales_after_vat'] = context['total_sales_before_vat'] + context['vat_amount']
# Additional Costs
total_quantity = quotation.total_quantity
context['administration_fee'] = sum(item.car.administration_fee for item in quotation.quotation_cars.all())
context['transportation_fee'] = sum(item.car.transportation_fee for item in quotation.quotation_cars.all())
context['custom_card_fee'] = sum(item.car.custom_card_fee for item in quotation.quotation_cars.all())
context['registration_fee'] = sum(item.car.registration_fee for item in quotation.quotation_cars.all())
context['administration_fee_vat'] = sum(item.car.administration_fee_vat for item in quotation.quotation_cars.all())
context['transportation_fee_vat'] = sum(item.car.transportation_fee_vat for item in quotation.quotation_cars.all())
context['custom_card_fee_vat'] = sum(item.car.custom_card_fee_vat for item in quotation.quotation_cars.all())
context['administration_fee_total'] = sum(item.car.administration_fee + context['administration_fee_vat'] for item in quotation.quotation_cars.all())
context['transportation_fee_total'] = sum(item.car.transportation_fee + context['transportation_fee_vat'] for item in quotation.quotation_cars.all())
context['custom_card_fee_total'] = sum(item.car.custom_card_fee + context['custom_card_fee_vat'] for item in quotation.quotation_cars.all())
context['registration_fee_total'] = sum(item.car.registration_fee * total_quantity for item in quotation.quotation_cars.all())
return context
@login_required
def confirm_quotation(request, pk):
@ -774,4 +875,92 @@ def custom_permission_denied_view(request, exception=None):
return render(request, "errors/403.html", {})
def custom_bad_request_view(request, exception=None):
return render(request, "errors/400.html", {})
return render(request, "errors/400.html", {})
class OrganizationListView(LoginRequiredMixin, ListView):
model = models.Organization
template_name = 'organizations/organization_list.html'
context_object_name = 'organizations'
class OrganizationDetailView(DetailView):
model = models.Organization
template_name = 'organizations/organization_detail.html'
context_object_name = 'organization'
class OrganizationCreateView(LoginRequiredMixin, CreateView):
model = models.Organization
form_class = forms.OrganizationForm
template_name = 'organizations/organization_form.html'
success_url = reverse_lazy('organization_list')
def form_valid(self, form):
if form.is_valid():
form.instance.dealer = self.request.user.dealer
form.save()
success_message = "Organization created successfully."
return super().form_valid(form)
else:
return form.errors
class OrganizationUpdateView(LoginRequiredMixin, UpdateView):
model = models.Organization
form_class = forms.OrganizationForm
template_name = 'organizations/organization_form.html'
success_url = reverse_lazy('organization_list')
success_message = "Organization updated successfully."
class OrganizationDeleteView(LoginRequiredMixin, DeleteView):
model = models.Organization
template_name = 'organizations/organization_confirm_delete.html'
success_url = reverse_lazy('organization_list')
success_message = "Organization deleted successfully."
class RepresentativeListView(LoginRequiredMixin, ListView):
model = models.Representative
template_name = 'representatives/representative_list.html'
context_object_name = 'representatives'
class RepresentativeDetailView(DetailView):
model = models.Representative
template_name = 'representatives/representative_detail.html'
context_object_name = 'representative'
class RepresentativeCreateView(LoginRequiredMixin, CreateView):
model = models.Representative
form_class = forms.RepresentativeForm
template_name = 'representatives/representative_form.html'
success_url = reverse_lazy('representative_list')
def form_valid(self, form):
if form.is_valid():
form.instance.dealer = self.request.user.dealer
form.save()
success_message = "Representative created successfully."
return super().form_valid(form)
else:
return form.errors
class RepresentativeUpdateView(LoginRequiredMixin, UpdateView):
model = models.Representative
form_class = forms.RepresentativeForm
template_name = 'representatives/representative_form.html'
success_url = reverse_lazy('representative_list')
success_message = "Representative updated successfully."
class RepresentativeDeleteView(LoginRequiredMixin, DeleteView):
model = models.Representative
template_name = 'representatives/representative_confirm_delete.html'
success_url = reverse_lazy('representative_list')
success_message = "Representative deleted successfully."

File diff suppressed because it is too large Load Diff

28
merge_db.py Normal file
View File

@ -0,0 +1,28 @@
import pymysql
from sqlalchemy import create_engine
import pandas as pd
# Database connection
engine = create_engine("mysql+pymysql://root:Kfsh&rc9788@localhost/car2db_01112024")
try:
# Load car_generation table
car_generation_query = "SELECT * FROM car_generation;"
car_generation_df = pd.read_sql(car_generation_query, engine)
# Load car_serie table
car_serie_query = "SELECT * FROM car_serie;"
car_serie_df = pd.read_sql(car_serie_query, engine)
# Merge tables on the appropriate column (e.g., generation_id)
merged_df = pd.merge(car_generation_df, car_serie_df, on="id_car_generation", how="inner")
# Select only the desired columns
final_df = merged_df[["id_car_serie", "id_car_model_y", "name_x", "name_y", "year_begin", "year_end"]]
# Save the filtered data to a JSON file
final_df.to_json("merged_car_data.json", orient="records", indent=4)
print("Filtered merged data saved to 'merged_car_data.json'.")
except Exception as e:
print("Error:", e)

126106
merged_car_data.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -36,6 +36,7 @@ small, .small {
}
.btn {
text-transform: uppercase;
border-radius: 5px;
}
</style>
@ -47,11 +48,7 @@ small, .small {
{% block content %}
<!-- Main content goes here -->
{% endblock %}
<!-- JavaScript Files -->
<script type="text/javascript" src="{% static 'js/main.js' %}"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {

View File

@ -1,113 +1,130 @@
{% extends "base.html" %}
{% load i18n %}
{% load render_table from django_tables2 %}
{% block title %}{% trans "customers" %}{% endblock title %}
{% block customers %}<a class="nav-link active">{% trans "customers"|capfirst %}</a>{% endblock %}
{% block title %}{% trans "Customers" %}{% endblock title %}
{% block customers %}
<a class="nav-link active fw-bold">
{% trans "Customers"|capfirst %}
<span class="visually-hidden">(current)</span>
</a>
{% endblock %}
{% block content %}
<div class="d-flex flex-column min-vh-100">
<div class="d-flex flex-column flex-sm-grow-1 ms-sm-14 p-4">
<main class="d-grid gap-4 p-1">
<div class="row g-4">
<div class="col-lg-6 col-xl-12">
<div class="container-fluid p-2">
<form method="get">
<div class="input-group input-group-sm">
<button id="inputGroup-sizing-sm"
class="btn btn-sm btn-secondary rounded-start" type="submit">
{% trans 'search'|capfirst %}
</button>
<input type="text"
name="q"
class="form-control form-control-sm rounded-end"
value="{{ request.GET.q }}"
aria-describedby="inputGroup-sizing-sm"/>
<!-- Clear Button -->
{% if request.GET.q %}
<a href="{% url request.resolver_match.view_name %}"
class="btn btn-sm btn-outline-danger ms-1 rounded">
<i class="bi bi-x-lg"></i>
</a>
{% endif %}
<main class="d-grid gap-4 p-1">
<!-- Search Bar -->
<div class="row g-4">
<div class="col-12">
<div class="container-fluid p-2">
<form method="get">
<div class="input-group input-group-sm">
<button class="btn btn-sm btn-secondary rounded-start" type="submit">
{% trans "search" %}
</button>
<input type="text"
name="q"
class="form-control form-control-sm rounded-end"
value="{{ request.GET.q }}"
placeholder="{% trans 'Search customers...' %}" />
{% if request.GET.q %}
<a href="{% url request.resolver_match.view_name %}"
class="btn btn-sm btn-outline-danger ms-1 rounded">
<i class="bi bi-x-lg"></i>
</a>
{% endif %}
</div>
</form>
</div>
</div>
</div>
</form>
</div>
<table class="table table-hover table-responsive-sm">
<thead>
<tr>
<th>{% trans 'first name'|capfirst %}</th>
<th>{% trans 'middle name'|capfirst %}</th>
<th>{% trans 'last name'|capfirst %}</th>
<th>{% trans 'national ID'|capfirst %}</th>
<th>{% trans 'actions'|capfirst %}</th>
</tr>
</thead>
<tbody>
{% for customer in customers %}
<tr>
<td>{{ customer.first_name }}</td>
<td>{{ customer.middle_name }}</td>
<td>{{ customer.last_name }}</td>
<td>{{ customer.national_id }}</td>
<td>
<a class="btn btn-sm btn-success"
href="{% url 'customer_detail' customer.id %}">
{% trans 'view'|capfirst %}
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- Optional: Pagination -->
{% if is_paginated %}
<nav aria-label="Page navigation">
<ul class="pagination pagination-sm justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item py-0">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active"><a class="page-link" href="?page={{ num }}">{{ num }}</a></li>
{% else %}
<li class="page-item"><a class="page-link" href="?page={{ num }}">{{ num }}</a></li>
{% endif %}
{% endfor %} {% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>
</div>
</main>
<!-- Customer Table -->
<div class="row g-4">
<div class="col-12">
<div class="card">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">{% trans "Customers List" %}</h5>
</div>
<div class="card-body p-0">
<table class="table table-hover table-sm mb-0">
<thead class="table-light">
<tr>
<th>{% trans "First Name" %}</th>
<th>{% trans "Middle Name" %}</th>
<th>{% trans "Last Name" %}</th>
<th>{% trans "National ID" %}</th>
<th class="text-center">{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for customer in customers %}
<tr>
<td>{{ customer.first_name }}</td>
<td>{{ customer.middle_name }}</td>
<td>{{ customer.last_name }}</td>
<td>{{ customer.national_id }}</td>
<td class="text-center">
<a href="{% url 'customer_detail' customer.id %}"
class="btn btn-sm btn-success">
{% trans "view" %}
</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="5" class="text-center text-muted">
{% trans "No customers found." %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Pagination -->
{% if is_paginated %}
<div class="card-footer bg-light">
<nav aria-label="Page navigation">
<ul class="pagination pagination-sm justify-content-center mb-0">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="{% trans 'Previous' %}">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link" aria-hidden="true">&laquo;</span>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active">
<span class="page-link">{{ num }}</span>
</li>
{% else %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="{% trans 'Next' %}">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link" aria-hidden="true">&raquo;</span>
</li>
{% endif %}
</ul>
</nav>
</div>
{% endif %}
</div>
</div>
</div>
</main>
</div>
</div>
{% endblock %}
{% endblock %}

View File

@ -82,6 +82,58 @@
</li>
{% endif %}
{% if perms.inventory.add_vendor %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle"
href="#" id="customerDropdown"
role="button" data-bs-toggle="dropdown"
aria-expanded="false">
{% trans 'organizations' %}
</a>
<ul class="dropdown-menu" aria-labelledby="customerDropdown">
<li>
<a href="{% url 'organization_create' %}"
class="dropdown-item fw-lighter">
<small>
{% trans "add organization" %}
</small>
</a>
</li>
<li>
<a href="{% url 'organization_list' %}"
class="dropdown-item fw-lighter">
<small>
{% trans "organizations" %}
</small>
</a>
</li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle"
href="#" id="customerDropdown"
role="button" data-bs-toggle="dropdown"
aria-expanded="false">
{% trans 'sales' %}
</a>
<ul class="dropdown-menu" aria-labelledby="customerDropdown">
<li>
<a href="{% url 'quotation_create' %}"
class="dropdown-item fw-lighter">
<small>
{% trans "create quotation" %}
</small>
</a>
</li>
<li>
<a href="{% url 'quotation_list' %}"
class="dropdown-item fw-lighter">
<small>
{% trans "quotations" %}
</small>
</a>
</li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle"
href="#" id="vendorsDropdown"

View File

@ -1,49 +1,81 @@
<!-- templates/cars/car_detail.html -->
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% load custom_filters %}
{% block title %}{{ _("Car Details") }}{% endblock %}
{% block content %}
<style>
.color-circle{
width: 32px;
height: 32px;
border-radius: 50px;
border-style: solid;
border-color: #000;
}
</style>
<div class="container mt-2 p-1">
<!-- Custom Card Modal -->
<div class="modal fade" id="customCardModal" tabindex="-1" aria-labelledby="customCardModalLabel" aria-hidden="true">
<!-- Custom Card Modal -->
<div class="modal fade" id="customCardModal" tabindex="-1" aria-labelledby="customCardModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header bg-primary">
<h5 class="modal-title text-light" id="customCardModalLabel">{% trans 'Custom Card' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<!-- Content will be loaded here via AJAX -->
</div>
<div class="modal-content">
<div class="modal-header bg-primary">
<h5 class="modal-title text-light" id="customCardModalLabel">{% trans 'Custom Card' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<!-- Content will be loaded here via AJAX -->
</div>
</div>
</div>
</div>
</div>
<!-- Reservation Modal -->
<div class="modal fade" id="reserveModal" tabindex="-1" aria-labelledby="reserveModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header bg-primary">
<h5 class="modal-title text-light" id="reserveModalLabel">{% trans 'Car Reservation' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{% trans 'Are you sure you want to reserve this car?' %}
</div>
<div class="modal-footer">
<button type="button"
class="btn btn-sm btn-danger"
data-bs-dismiss="modal">
{% trans 'No' %}
</button>
<form method="POST" action="{% url 'reserve_car' car.id %}" class="d-inline">
{% csrf_token %}
<button type="submit" class="btn btn-success btn-sm">{% trans "Yes" %}</button>
</form>
</div>
</div>
</div>
</div>
<!-- Specification Modal -->
<div class="modal fade"
id="specificationsModal"
tabindex="-1"
aria-labelledby="specificationsModalLabel"
aria-hidden="true">
<div class="modal fade" id="specificationsModal" tabindex="-1" aria-labelledby="specificationsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content glossy-modal">
<div class="modal-header bg-primary text-light">
<h5 class="modal-title"
id="specificationsModalLabel">
{% trans 'specifications'|upper %}
<h5 class="modal-title" id="specificationsModalLabel">
{% trans 'specifications'|upper %}
</h5>
<button type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close">
</button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="specificationsContent"></div>
<div class="d-grid gap-2">
<button type="button"
class="btn btn-sm btn-danger"
data-bs-dismiss="modal">
{% trans 'Close' %}
</button>
</div>
</div>
</div>
</div>
@ -55,7 +87,10 @@
<main class="d-grid gap-4">
<div class="row g-4">
<div class="col-lg-6 col-xl-6">
<p class="fs-5">{% trans 'Car Details' %}</p>
<div class="card rounded shadow">
<p class="card-header bg-primary text-white rounded-top fw-bold">{% trans 'Car Details' %}</p>
<div class="card-body">
<table class="table table-sm table-responsive align-middle">
<tr>
<th>{% trans "VIN" %}</th>
@ -67,8 +102,7 @@
</tr>
<tr>
<th>{% trans "make" %}</th>
<td>{{ car.id_car_make.get_local_name }}</td>
<td>{{ car.id_car_make.get_local_name }}</td>
</tr>
<tr>
<th>{% trans "model" %}</th>
@ -116,127 +150,165 @@
<tr>
<th>{% trans 'specifications' %}</th>
<td>
<button type="button"
class="btn btn-primary btn-sm"
id="specification-btn"
data-bs-toggle="modal"
<button type="button"
class="btn btn-primary btn-sm"
id="specification-btn"
data-bs-toggle="modal"
data-bs-target="#specificationsModal">
{% trans 'view' %}
{% trans 'view' %}
</button>
</td>
</tr>
{% if car.custom_cards.exists %}
{% for custom_card in car.custom_cards.all %}
<tr>
<th>{% trans "Custom Number" %}</th>
<td>{{ custom_card.custom_number }}</td>
</tr>
<tr>
<th>{% trans "Custom Date" %}</th>
<td>{{ custom_card.custom_date|date }}</td>
</tr>
{% endfor %}
{% else %}
<tr>
<th>{% trans "Custom Card" %}</th>
<td>
<button type="button"
class="btn btn-sm btn-success"
data-bs-toggle="modal"
data-bs-target="#customCardModal">
{% trans 'Add' %}
</button>
</td>
</tr>
{% endif %}
{% if car.custom_cards %}
<tr>
<th>{% trans "Custom Number" %}</th>
<td>{{ car.custom_cards.custom_number }}</td>
</tr>
<tr>
<th>{% trans "Custom Date" %}</th>
<td>{{ car.custom_cards.custom_date|date }}</td>
</tr>
{% else %}
<tr>
<th>{% trans "Custom Card" %}</th>
<td>
<button type="button"
class="btn btn-sm btn-success"
data-bs-toggle="modal"
data-bs-target="#customCardModal">
{% trans 'Add' %}
</button>
</td>
</tr>
{% endif %}
<tr>
<th>{% trans 'Showroom Location' %}</th>
<td>
{% if car.location %}
{% if car.location.is_owner_showroom %}
{% trans 'Our Showroom' %}
{% else %}
{{ car.location.showroom.get_local_name }}
{% endif %}
<a href="{% url 'transfer' car.location.pk %}"
class="btn btn-danger btn-sm">
{% trans "transfer" %}
</a>
{% else %} {% trans "No location available." %}
<a href="{% url 'add_car_location' car.pk %}"
class="btn btn-success btn-sm ms-2">
{% trans "Add" %}
</a>
</td>
{% endif %}
</tr>
</table>
<div>
<a href="{% url 'car_update' car.pk %}" class="btn btn-warning btn-sm">{% trans "Edit" %}</a>
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-xl-6">
<p class="fs-5">{% trans 'Financial Details' %}</p>
<div class="card rounded shadow">
<p class="card-header bg-primary text-white rounded-top fw-bold">{% trans 'Financial Details' %}</p>
{% if car.finances.exists %} {% for finance in car.finances.all %}
<table class="table table-sm table-responsive mb-3 align-middle">
<div class="card-body">
{% if car.finances %}
<table class="table table-sm table-responsive align-middle">
{% if perms.inventory.view_carfinance %}
<tr>
<th>{% trans "Cost Price" %}</th>
<td>{{ finance.cost_price }}</td>
<td>{{ car.finances.cost_price }}</td>
</tr>
{% endif %}
<tr>
<th>{% trans "Selling Price" %}</th>
<td>{{ finance.selling_price }}</td>
<td>{{ car.finances.selling_price }}</td>
</tr>
<tr>
<tr>
<td><small class="ms-5">{% trans "Administration Fee" %}</small></td>
<td><small>{{ finance.administration_fee }}</small></td>
<td><small>{{ car.finances.administration_fee }}</small></td>
</tr>
<tr>
<tr>
<td><small class="ms-5">{% trans "Registration Fee" %}</small></td>
<td><small>{{ finance.registration_fee }}</small></td>
<td><small>{{ car.finances.registration_fee }}</small></td>
</tr>
<tr>
<td><small class="ms-5">{% trans "Transportation Fee" %}</small></td>
<td><small>{{ finance.transportation_fee }}</small></td>
<tr>
<td><small class="ms-5">{% trans "Transportation Fee" %}</small></td>
<td><small>{{ car.finances.transportation_fee }}</small></td>
</tr>
<tr>
<td><small class="ms-5">{% trans "Custom Card Fee" %}</small></td>
<td><small>{{ finance.custom_card_fee }}</small></td>
<tr>
<td><small class="ms-5">{% trans "Custom Card Fee" %}</small></td>
<td><small>{{ car.finances.custom_card_fee }}</small></td>
</tr>
<tr>
<tr>
<th>{% trans "Discount Amount" %}</th>
<td>{{ finance.discount_amount }} - </td>
<td>{{ car.finances.discount_amount }} -</td>
</tr>
<tr>
<th>{% trans "VAT Amount" %}</th>
<td>{{ finance.vat_amount }}</td>
<td>{{ car.finances.vat_amount }}</td>
</tr>
<tr>
<th>{% trans "Total" %}</th>
<td>{{ car.total }}</td>
<td>{{ car.finances.total }}</td>
</tr>
<tr>
<tr>
<td colspan="2">
{% if perms.inventory.change_carfinance %}
<a href="{% url 'car_finance_update' finance.pk %}"
class="btn btn-warning btn-sm mb-3">
{% if perms.inventory.change_carfinance %}
<a href="{% url 'car_finance_update' car.finances.pk %}"
class="btn btn-warning btn-sm mb-3">
{% trans "Edit Finance Details" %}
</a>
</a>
{% endif %}
{% endfor %}
{% else %}
<p>{% trans "No finance details available." %}</p>
<a href="{% url 'car_finance_create' car.pk %}"
class="btn btn-success btn-sm mb-3">
{% else %}
<p>{% trans "No finance details available." %}</p>
<a href="{% url 'car_finance_create' car.pk %}"
class="btn btn-success btn-sm mb-3">
{% trans "Add Finance Details" %}
</a>
</a>
</td>
</tr>
{% endif %}
</table>
<p class="fs-5 mt-2">{% trans 'Colors Details' %}</p>
</tr>
{% endif %}
</table>
</div>
</div>
<div class="card rounded shadow mt-3">
<p class="card-header bg-primary text-white rounded-top fw-bold">{% trans 'Colors Details' %}</p>
<div class="card-body">
<table class="table table-sm table-responsive align-middle">
<tbody class=" align-middle">
<tbody class="align-middle">
{% if car.colors.exists %}
{% for color in car.colors.all %}
<tr>
<th>{% trans 'Exterior' %}</th>
<th>{% trans 'Exterior' %}</th>
<td>
<span>{{ color.exterior.get_local_name }}</span>
</td>
<td class="align-middle">
<div class="text-end" style="width: 32px; height: 32px; background-color: rgb({{ color.exterior.rgb }}); border-radius: 50px; border-style: solid; border-color: #000;"></div>
<div class="text-end color-circle"
style="background-color: rgb({{ color.exterior.rgb }});">
</div>
</td>
</tr>
<tr>
<th>{% trans 'Interior' %}</th>
<tr>
<th>{% trans 'Interior' %}</th>
<td>
<span>{{ color.interior.get_local_name }}</span>
</td>
<td class="align-middle">
<div class="text-end" style="width: 32px; height: 32px; background-color: rgb({{ color.interior.rgb }}); border-radius: 50px; border-style: solid; border-color: #000;"></div>
<div class="text-end color-circle"
style="background-color: rgb({{ color.interior.rgb }});">
</div>
</td>
</tr>
{% endfor %}
{% endfor %}
{% else %}
<tr>
<td colspan="2">
@ -245,51 +317,58 @@
</tr>
<tr>
<td colspan="2">
<a href="{% url 'add_color' car.pk %}"
class="btn btn-success btn-sm">
{% trans "Get Colors" %}
<a href="{% url 'add_color' car.pk %}" class="btn btn-success btn-sm">
{% trans "Get Colors" %}
</a>
</td>
</tr>
{% endif %}
{% endif %}
</tbody>
</table>
<p class="fs-5 mt-2">{% trans 'Reservations Details' %}</p>
{% if car.is_reserved %}
</div>
</div>
<div class="card rounded shadow mt-3">
<p class="card-header bg-primary text-white rounded-top fw-bold">{% trans 'Reservations Details' %}</p>
<div class="card-body">
{% if car.is_reserved %}
<table class="table table-sm table-responsive align-middle">
<thead>
<thead>
<tr>
<th>{% trans "Reserved By" %}</th>
<th>{% trans "Expires At" %}</th>
<th>{% trans 'Actions' %}</th>
<th>{% trans "Reserved By" %}</th>
<th>{% trans "Expires At" %}</th>
<th>{% trans 'Actions' %}</th>
</tr>
</thead>
<tbody>
</thead>
<tbody>
{% for reservation in car.reservations.all %}
<tr>
<td>{{ reservation.reserved_by.username }}</td>
<td>{{ reservation.reserved_until }}</td>
<td>
{% if reservation.is_active %}
<form method="post" action="{% url 'reservations' reservation.id %}">
{% csrf_token %}
<button type="submit"
name="action"
value="renew"
class="btn btn-sm btn-success">
<small>{% trans "Renew" %}</small>
</button>
<button type="submit"
name="action"
value="cancel"
class="btn btn-sm btn-secondary">
<small>{% trans "Cancel" %}</small>
</button>
</form>
{% else %}
<span class="badge bg-danger" style="width: 120px;">{% trans "Expired" %}</span>
{% endif %}
</td>
<td>{{ reservation.reserved_by.username }}</td>
<td>{{ reservation.reserved_until }}</td>
<td>
{% if reservation.is_active %}
<form method="post" action="{% url 'reservations' reservation.id %}">
{% csrf_token %}
<button type="submit"
name="action"
value="renew"
class="btn btn-sm btn-success">
<small>{% trans "Renew" %}</small>
</button>
<button type="submit"
name="action"
value="cancel"
class="btn btn-sm btn-secondary">
<small>{% trans "Cancel" %}</small>
</button>
</form>
{% else %}
<span class="badge bg-danger"
style="width: 120px;">
{% trans "Expired" %}
</span>
{% endif %}
</td>
</tr>
{% endfor %}
{% else %}
@ -297,9 +376,7 @@
<td>
<form method="POST" action="{% url 'reserve_car' car.id %}" class="d-inline">
{% csrf_token %}
{% if perms.inventory.car_change %}
<button type="submit" class="btn btn-success btn-sm">{% trans "Reserve" %}</button>
{% endif %}
</form>
{% endif %}
</td>
@ -307,19 +384,19 @@
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row g-4">
<div class="">
<!-- Actions -->
{% if perms.inventory.car_change %}
<a href="#" class="btn btn-danger btn-sm">{% trans "transfer" %}</a>
<a href="{% url 'car_update' car.pk %}" class="btn btn-warning btn-sm">{% trans "Edit" %}</a>
{% endif %}
<a href="#" class="btn btn-danger btn-sm">{% trans "transfer" %}</a>
<a href="{% url 'car_update' car.pk %}" class="btn btn-warning btn-sm">{% trans "Edit" %}</a>
<a href="{% url 'inventory_stats' %}" class="btn btn-secondary btn-sm">{% trans "Back to List" %}</a>
</div>
</div>
</main>
</div>
</div>
@ -345,33 +422,28 @@
const csrfToken = getCookie("csrftoken");
const ajaxUrl = "{% url 'ajax_handler' %}";
const modal = document.getElementById('customCardModal');
const modalBody = modal.querySelector('.modal-body');
const modal = document.getElementById("customCardModal");
const modalBody = modal.querySelector(".modal-body");
// When the modal is triggered, load the form
modal.addEventListener('show.bs.modal', function () {
const url = "{% url 'add_custom_card' car.pk %}";
// When the modal is triggered, load the form
modal.addEventListener("show.bs.modal", function () {
const url = "{% url 'add_custom_card' car.pk %}";
fetch(url)
.then(response => response.text())
.then(html => {
modalBody.innerHTML = html;
})
.catch(error => {
modalBody.innerHTML = '<p class="text-danger">Error loading form. Please try again later.</p>';
console.error('Error loading form:', error);
});
fetch(url)
.then((response) => response.text())
.then((html) => {
modalBody.innerHTML = html;
})
.catch((error) => {
modalBody.innerHTML = '<p class="text-danger">Error loading form. Please try again later.</p>';
console.error("Error loading form:", error);
});
});
modal.addEventListener("hidden.bs.modal", function () {
modalBody.innerHTML = "";
});
modal.addEventListener('hidden.bs.modal', function () {
modalBody.innerHTML = '';
});
const showSpecificationButton = document.getElementById("specification-btn");
const specificationsContent = document.getElementById("specificationsContent");

View File

@ -1,47 +1,19 @@
{% extends 'base.html' %}
{% load crispy_forms_filters %}
{% load i18n %}
{% load custom_filters %}
{% block title %}
{% trans 'Edit Car' %}
{% endblock %}
{% block content %}
<div class="container p-4">
<h4 class="mb-4">
{% trans 'Edit Car' %}
</h4>
<!-- Display Validation Errors -->
{% if form.errors %}
<div class="alert alert-danger">
<ul class="mb-0">
{% for field in form %}
{% for error in field.errors %}
<li><strong>{{ field.label }}:</strong> {{ error }}</li>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
<form method="post" class="needs-validation" novalidate>
{% csrf_token %}
{{ form|crispy }}
{% extends 'base.html' %} {% load crispy_forms_filters %} {% load i18n %} {% load custom_filters %} {% block title %} {% trans 'Edit Car' %} {% endblock %} {% block content %}
<div class="container">
<div class="card rounded shadow mt-3">
<p class="card-header bg-primary text-white rounded-top fw-bold">{% trans 'Edit Car' %}</p>
<div class="card-body">
<form method="post" class="needs-validation" novalidate>
{% csrf_token %} {{ form|crispy }}
<!-- Save and Back Buttons -->
<div class="d-flex justify-content-center mt-4 ms-2">
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-sm btn-danger">{% trans "Back" %}</a>
<button type="submit" class="btn btn-success ms-2">
{% trans 'Save' %}
</button>
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-sm btn-danger">{% trans "Back" %}</a>
<button type="submit" class="btn btn-sm btn-success ms-2">
{% trans 'Save' %}
</button>
</div>
</form>
</form>
</div>
</div>
</div>
{% endblock %}
{% endblock %}

View File

@ -467,7 +467,7 @@ async function updateFields(vinData) {
if (vinData.model_id) {
modelSelect.value = vinData.model_id;
document.getElementById("model-check").innerHTML = '&#10003;';
await loadSeries(vinData.model_id);
await loadSeries(vinData.model_id, vinData.year);
}
if (vinData.year) {
yearSelect.value = vinData.year;
@ -483,7 +483,7 @@ async function startScanner() {
if (result) {
vinInput.value = result.text;
closeModal();
decodeVin();
await decodeVin();
}
}).catch(console.error);
}
@ -540,11 +540,11 @@ async function loadModels(makeId) {
async function loadSeries(modelId){
async function loadSeries(modelId, year){
resetDropdown(serieSelect, '{% trans "Select" %}');
resetDropdown(trimSelect, '{% trans "Select" %}');
specificationsContent.innerHTML = '';
const response = await fetch(`${ajaxUrl}?action=get_series&model_id=${modelId}`, {
const response = await fetch(`${ajaxUrl}?action=get_series&model_id=${modelId}&year=${year}`, {
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRFToken': csrfToken

View File

@ -1,128 +1,151 @@
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% block title %}
{% trans 'inventory'|capfirst %}
{% endblock %}
{% block inventory %}<a class="nav-link active fw-bold">{% trans "inventory" %}<span class="visually-hidden">(current)</span></a>{% endblock %}
{% block inventory %}
<a class="nav-link active fw-bold">{% trans "inventory" %}<span class="visually-hidden">(current)</span></a>
{% endblock %}
{% block content %}
<style>
<style>
.color-div {
width: 22px;
height: 22px;
border-radius: 50%;
border: 1px solid #ccc;
margin: auto;
text-align: center;
vertical-align: middle;
line-height: 22px;
}
</style>
<div class="d-flex flex-column min-vh-100">
<div class="d-flex flex-column flex-sm-grow-1 ms-sm-14 p-1">
<main class="d-grid gap-4 p-1">
<div class="row g-4">
<div class="col-lg-6 col-xl-12">
</style>
<div class="container mt-3">
<div class="d-flex flex-column min-vh-100 flex-sm-grow-1 ms-sm-14 p-1">
<main class="d-grid gap-4 p-1">
<div class="row g-4">
<div class="col-lg-6 col-xl-12">
<div class="card rounded shadow">
<p class="card-header bg-primary text-white rounded-top fw-bold">
{{ cars.id_car_make.get_local_name }} -
{{ cars.id_car_model.get_local_name }}
</p>
<div class="card-body">
<table class="table table-responsive table-hover table-sm align-middle">
<thead>
<tr>
<th>{% trans "VIN" %}</th>
<th>{% trans "Year" %}</th>
<th>{% trans 'Exterior Color' %}</th>
<th>{% trans 'Interior Color' %}</th>
<th>{% trans "Showroom Location" %}</th>
<th>{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for car in cars %}
<tr class="{% if car.is_reserved %}table-danger{% endif %}">
<td>{{ car.vin }}</td>
<td>{{ car.year }}</td>
{% if car.colors.exists %}
<td>
<div class="d-flex flex-column align-items-center">
<span class="color-div"
style="background-color: rgb({{ car.colors.first.exterior.rgb }});"
title="{{ car.colors.first.exterior.get_local_name }}">
</span>
</div>
</td>
<td>
<div class="d-flex flex-column align-items-center">
<span class="color-div"
style="background-color: rgb({{ car.colors.first.interior.rgb }});"
title="{{ car.colors.first.interior.get_local_name }}">
</span>
</div>
</td>
{% else %}
<td><span class="color-div">{% trans 'No Color' %}</span></td>
<td><span class="color-div">{% trans 'No Color' %}</span></td>
{% endif %}
{% if car.location.is_owner_showroom %}
<td> {% trans 'Our Showroom' %}</td>
{% else %}
<td>{{ car.location.showroom.get_local_name }}</td>
{% endif %}
<td>
<a href="{% url 'car_detail' car.pk %}" class="btn btn-sm btn-success">
<small>{% trans "view" %}</small>
</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="6">{% trans "No cars available." %}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="container-fluid p-2">
<form method="get">
<div class="input-group input-group-sm">
<button id="inputGroup-sizing-sm"
class="btn btn-sm btn-secondary rounded-start" type="submit">
{% trans 'search'|capfirst %}
</button>
<input type="text"
name="q"
class="form-control form-control-sm rounded-end"
value="{{ request.GET.q }}"
aria-describedby="inputGroup-sizing-sm"/>
<!-- Clear Button -->
{% if request.GET.q %}
<a href="{{request.META.HTTP_REFERER}}"
class="btn btn-sm btn-outline-danger ms-1 rounded">
<i class="bi bi-x-lg"></i>
</a>
{% endif %}
<!-- Pagination -->
{% if is_paginated %}
<nav aria-label="Page navigation">
<ul class="pagination pagination-sm justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page=1" aria-label="First">
&laquo;&laquo;
</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous">
&laquo;
</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">&laquo;&laquo;</span>
</li>
<li class="page-item disabled">
<span class="page-link">&laquo;</span>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active">
<span class="page-link">{{ num }}</span>
</li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="Next">
&raquo;
</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}" aria-label="Last">
&raquo;&raquo;
</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">&raquo;</span>
</li>
<li class="page-item disabled">
<span class="page-link">&raquo;&raquo;</span>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>
</div>
</form>
</div>
<table class="table table-responsive table-sm align-middle">
<thead>
<tr>
<th></th>
<th>{% trans "VIN" %}</th>
<th>{% trans "Year" %}</th>
<th>{% trans "Make" %}</th>
<th>{% trans "Model" %}</th>
<th>{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for car in cars %}
<tr class="{% if car.is_reserved %}table-danger{% endif %}">
{% if car.colors.all %}
{% for color in car.colors.all %}
<td><div class="color-div" style="background-color: rgb({{ color.rgb }});"></div></td>
{% endfor %}
{% else %}
<td><div class="color-div"><i class="bi bi-x-lg fs-6"></i></div></td>
{% endif %}
<td>{{ car.vin }}</td>
<td>{{ car.year }}</td>
<td>{{ car.id_car_make.get_local_name }}</td>
<td>{{ car.id_car_model.get_local_name }}</td>
<td>
<a href="{% url 'car_detail' car.pk %}" class="btn btn-sm btn-success"><small>{% trans "view" %}</small></a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="7">{% trans "No cars available." %}</td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- Optional: Pagination -->
{% if is_paginated %}
<nav aria-label="Page navigation">
<ul class="pagination pagination-sm justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item py-0">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active"><a class="page-link" href="?page={{ num }}">{{ num }}</a></li>
{% else %}
<li class="page-item"><a class="page-link" href="?page={{ num }}">{{ num }}</a></li>
{% endif %}
{% endfor %} {% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>
</div>
</main>
</div>
</div>
</main>
</div>
</div>
{% endblock %}

View File

@ -1,10 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>$Title$</title>
</head>
<body>
$END$
</body>
</html>
{% extends 'base.html' %}
{% load i18n %}
{% load crispy_forms_filters %}
{% block title %}{% trans "Manage Car Location" %}{% endblock %}
{% block content %}
{% if carlocation.exists %}
<p>Transfer</p>
{% else %}
<p>Add</p>
{% endif %}
<div class="container mt-3">
<h3 class="mb-3">{% trans "Manage Car Location" %}</h3>
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<div class="form-group">
<button type="submit" class="btn btn-primary">{% trans "Save" %}</button>
</div>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,21 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Organization Details" %}{% endblock title %}
{% block content %}
<div class="container my-4">
<h2>{{ organization.get_local_name }}</h2>
<ul class="list-group mb-4">
<li class="list-group-item"><strong>{% trans "CRN" %}:</strong> {{ organization.crn }}</li>
<li class="list-group-item"><strong>{% trans "VRN" %}:</strong> {{ organization.vrn }}</li>
<li class="list-group-item"><strong>{% trans "Phone" %}:</strong> {{ organization.phone_number }}</li>
<li class="list-group-item"><strong>{% trans "Address" %}:</strong> {{ organization.address }}</li>
</ul>
<div class="d-flex">
<a href="{% url 'organization_update' organization.id %}" class="btn btn-sm btn-warning me-2">{% trans "Edit" %}</a>
<form method="post" action="{% url 'organization_delete' organization.id %}">
{% csrf_token %}
<button type="submit" class="btn btn-sm btn-danger">{% trans "Delete" %}</button>
</form>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,16 @@
{% extends 'base.html' %}
{% load i18n %}
{% load crispy_forms_filters %}
{% block title %}{% trans "Add Organization" %}{% endblock title %}
{% block content %}
<div class="container my-4">
<h2>{% trans "Add Organization" %}</h2>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-sm btn-primary">{% trans "Save" %}</button>
<a href="{% url 'organization_list' %}" class="btn btn-sm btn-secondary">{% trans "Cancel" %}</a>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,43 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Organizations" %}{% endblock title %}
{% block content %}
<div class="container my-4">
<h2>{% trans "Organizations" %}</h2>
<div class="d-flex justify-content-between align-items-center mb-3">
<form method="get" class="d-flex">
<input type="text" name="q" class="form-control form-control-sm" placeholder="{% trans 'Search' %}" value="{{ request.GET.q }}">
<button type="submit" class="btn btn-sm btn-secondary ms-2">{% trans "Search" %}</button>
</form>
<a href="{% url 'organization_create' %}" class="btn btn-sm btn-primary">{% trans "Add Organization" %}</a>
</div>
<table class="table table-hover">
<thead>
<tr>
<th>{% trans "Name" %}</th>
<th>{% trans "CRN" %}</th>
<th>{% trans "VRN" %}</th>
<th>{% trans "Phone" %}</th>
<th>{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for org in organizations %}
<tr>
<td>{{ org.get_local_name }}</td>
<td>{{ org.crn }}</td>
<td>{{ org.vrn }}</td>
<td>{{ org.phone_number }}</td>
<td>
<a href="{% url 'organization_detail' org.id %}" class="btn btn-sm btn-success">{% trans "view" %}</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="5" class="text-center">{% trans "No organizations found." %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,20 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Representative Details" %}{% endblock title %}
{% block content %}
<div class="container my-4">
<h2>{{ representative.get_local_name }}</h2>
<ul class="list-group mb-4">
<li class="list-group-item"><strong>{% trans "ID Number" %}:</strong> {{ representative.id_number }}</li>
<li class="list-group-item"><strong>{% trans "Phone" %}:</strong> {{ representative.phone_number }}</li>
<li class="list-group-item"><strong>{% trans "Address" %}:</strong> {{ representative.address }}</li>
</ul>
<div class="d-flex">
<a href="{% url 'representative_update' representative.id %}" class="btn btn-sm btn-warning me-2">{% trans "Edit" %}</a>
<form method="post" action="{% url 'representative_delete' representative.id %}">
{% csrf_token %}
<button type="submit" class="btn btn-sm btn-danger">{% trans "Delete" %}</button>
</form>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,15 @@
{% extends 'base.html' %}
{% load i18n %}
{% load crispy_forms_filters %}
{% block title %}{% trans "Add Representative" %}{% endblock title %}
{% block content %}
<div class="container my-4">
<h2>{% trans "Add Representative" %}</h2>
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-sm btn-primary">{% trans "Save" %}</button>
<a href="{% url 'representative_list' %}" class="btn btn-sm btn-secondary">{% trans "Cancel" %}</a>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,41 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Representatives" %}{% endblock title %}
{% block content %}
<div class="container my-4">
<h2>{% trans "Representatives" %}</h2>
<div class="d-flex justify-content-between align-items-center mb-3">
<form method="get" class="d-flex">
<input type="text" name="q" class="form-control form-control-sm" placeholder="{% trans 'Search' %}" value="{{ request.GET.q }}">
<button type="submit" class="btn btn-sm btn-secondary ms-2">{% trans "Search" %}</button>
</form>
<a href="{% url 'representative_create' %}" class="btn btn-sm btn-primary">{% trans "Add Representative" %}</a>
</div>
<table class="table table-hover">
<thead>
<tr>
<th>{% trans "Name" %}</th>
<th>{% trans "ID Number" %}</th>
<th>{% trans "Phone" %}</th>
<th>{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for rep in representatives %}
<tr>
<td>{{ rep.get_local_name }}</td>
<td>{{ rep.id_number }}</td>
<td>{{ rep.phone_number }}</td>
<td>
<a href="{% url 'representative_detail' rep.id %}" class="btn btn-sm btn-success">{% trans "view" %}</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="4" class="text-center">{% trans "No representatives found." %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View File

@ -1,4 +1,5 @@
{% extends "base.html" %}
{% load custom_filters %}
{% load i18n %}
{% block content %}
<div class="container mt-4">
@ -11,7 +12,7 @@
<div class="col-md-6">
<h5>{% trans "Customer Details" %}</h5>
<p>
<strong>{% trans "Name" %}:</strong>
<strong>{% trans "Name" %}:</strong>
{{ quotation.customer.get_full_name }}</p>
<p><strong>{% trans "Address" %}:</strong> {{ quotation.customer.address }}</p>
<p><strong>{% trans "VAT No" %}:</strong> {{ quotation.customer.vat_number }}</p>
@ -26,50 +27,77 @@
<h5 class="mt-4">{% trans "Car Details" %}</h5>
<table class="table table-bordered">
<thead>
<tr>
<th>VIN</th>
<th>Model</th>
<th>Selling Price</th>
<th>VAT</th>
<th>Total</th>
</tr>
</thead>
<tbody>
{% for item in quotation.quotation_cars.all %}
<tr>
<td>{{ item.car.vin }}</td>
<td>{{ item.car.id_car_model.get_local_name }}</td>
<td>{{ item.car.selling_price }}</td>
<td>{{ item.car.vat_amount }}</td>
<td>{{ item.car.total }}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<th colspan="2">Totals</th>
<th>{{ item.car.finances.total_vat_amount }}</th>
<th>{{ item.car.finances.total_before_vat }}</th>
<th>{{ item.car.finances.total }}</th>
</tr>
</tfoot>
</table>
<thead>
<tr>
<th>{% trans "VIN" %}</th>
<th>{% trans "Model" %}</th>
<th>{% trans "Year" %}</th>
<th>{% trans "Quantity" %}</th>
<th>{% trans "Price" %}</th>
<th>{% trans "VAT" %}</th>
<th>{% trans "Total" %}</th>
</tr>
</thead>
<tbody>
{% for item in quotation.quotation_cars.all %}
<tr>
<td>{{ item.car.vin }}</td>
<td>{{ item.car.id_car_model.get_local_name }}</td>
<td>{{ item.car.year }}</td>
<td>{{ item.quantity }}</td>
<td>{{ item.car.selling_price }}</td>
<td>{{ item.car.vat_amount }}</td>
<td>{{ item.car.total }}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<th colspan="3">{% trans "Totals" %}</th>
<th>{{ quotation.total_quantity }}</th>
<th>{{ total_sales_before_vat }}</th>
<th>{{ vat_amount }}</th>
<th>{{ total_sales_after_vat }}</th>
</tr>
</tfoot>
</table>
<h5 class="mt-4">{% trans "Summary" %}</h5>
<h5 class="mt-4">{% trans "Additional Costs" %}</h5>
<table class="table table-bordered">
<tr>
<th>{% trans "Total Sales Before VAT" %}</th>
<td>{{ quotations.total_sales_before_vat }}</td>
</tr>
<tr>
<th>{% trans "VAT Amount" %}</th>
<td>{{ quotations.vat_amount }}</td>
</tr>
<tr>
<th>{% trans "Total Sales After VAT" %}</th>
<td>{{ quotations.total_sales_after_vat }}</td>
</tr>
<thead>
<tr>
<th>{% trans "Additions" %}</th>
<th>{% trans "Cost" %}</th>
<th>{% trans "VAT" %}</th>
<th>{% trans "Total Cost with VAT" %}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{% trans "Administration Fee" %}</td>
<td>{{ administration_fee }}</td>
<td>{{ administration_fee_vat }}</td>
<td>{{ administration_fee_total }}</td>
</tr>
<tr>
<td>{% trans "Transportation Fee" %}</td>
<td>{{ transportation_fee }}</td>
<td>{{ transportation_fee_vat }}</td>
<td>{{ transportation_fee_total }}</td>
</tr>
<tr>
<td>{% trans "Custom Card Fee" %}</td>
<td>{{ custom_card_fee }}</td>
<td>{{ custom_card_fee_vat }}</td>
<td>{{ custom_card_fee_total }}</td>
</tr>
<tr>
<td>{% trans "Registration Fee (No VAT)" %}</td>
<td>{{ registration_fee }}</td>
<td>{% trans "N/A" %}</td>
<td>{{ registration_fee_total }}</td>
</tr>
</tbody>
</table>
</div>
<div class="card-footer text-end">

View File

@ -21,6 +21,7 @@
<tbody>
{% for quotation in quotations %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ quotation.customer.get_full_name }}</td>
<td>{{ quotation.quotation_cars.count }}</td>
@ -28,7 +29,7 @@
<td>{{ quotation.created_at|date:"d/m/Y H:i" }}</td>
<td>
<a href="{% url 'quotation_detail' quotation.id %}" class="btn btn-sm btn-info">
{% trans "View" %}
{% trans "view" %}
</a>
</td>

View File

@ -52,8 +52,8 @@
</td>
<td>{{ vendor.address }}</td>
<td>
<a href="{% url 'vendor_detail' vendor.id %}" class="btn btn-warning btn-sm">
{{ _("View")|capfirst }}
<a href="{% url 'vendor_detail' vendor.id %}" class="btn btn-success btn-sm">
{{ _("view")|capfirst }}
</a>
</td>
</tr>

View File

@ -39,9 +39,7 @@
<!-- Vendor Details -->
<div class="container mt-4">
<div class="card shadow rounded">
<div class="card-header bg-primary text-white">
<h4 class="mb-0">{% trans "Vendor Details" %}</h4>
</div>
<p class="card-header bg-primary text-white mb-0 fs-5">{% trans "Vendor Details" %}</p>
<div class="card-body">
<ul class="list-group list-group-flush">
<li class="list-group-item">

View File

@ -1,5 +1,5 @@
from vin import VIN
# from vin import VIN
import requests
import json
@ -7,45 +7,45 @@ import json
#
# print(details)
# from vininfo import Vin
#
#
# def vin_info_py(vin_no):
#
# # vin_no = 'VR3FDAHD1S3023105'
# vin_dic = {}
# vin_info = Vin(vin_no)
# vin_dic['country'] = vin_info.country
# vin_dic['manufacturer'] = vin_info.manufacturer
# vin_dic['region'] = vin_info.region
# vin_dic['produce_year'] = vin_info.years
# vin_dic['model_year'] = vin_info.years
#
# details = vin_info.details
# if details:
# vin_dic['details'] = True
# vin_dic['body'] = str(details.body)
# # vin_dic['engine'] = f"{details.engine}"
# vin_dic['model'] = f"{details.model}"
# vin_dic['plant'] = f"{details.plant}"
# vin_dic['serial'] = f"{details.serial}"
# vin_dic['transmission'] = f"{details.transmission}"
#
# for key, value in vin_dic.items():
# print(f'\t{key}:\t{value}')
#
# print("Decoded By ****vin_info****")
# print(vin_info.vds)
#
# else:
# print(vin_info.country)
# print(vin_info.manufacturer)
# print(vin_info.region)
# print(vin_info.years)
# print(vin_info.wmi)
# print(vin_info.vds)
# print(vin_info.vis)
# print('vin_info but no details')
from vininfo import Vin
# def vin_info_py():
vin_no = 'VYFED9HP0SJ519559'
vin_dic = {}
vin_info = Vin(vin_no)
vin_dic['country'] = vin_info.country
vin_dic['manufacturer'] = vin_info.manufacturer
vin_dic['region'] = vin_info.region
vin_dic['produce_year'] = vin_info.years
vin_dic['model_year'] = vin_info.years
details = vin_info.details
if details:
vin_dic['details'] = True
vin_dic['body'] = str(details.body)
# vin_dic['engine'] = f"{details.engine}"
vin_dic['model'] = f"{details.model}"
vin_dic['plant'] = f"{details.plant}"
vin_dic['serial'] = f"{details.serial}"
vin_dic['transmission'] = f"{details.transmission}"
for key, value in vin_dic.items():
print(f'\t{key}:\t{value}')
print("Decoded By ****vin_info****")
print(vin_info.vds)
else:
print(vin_info.country)
print(vin_info.manufacturer)
print(vin_info.region)
print(vin_info.years)
print(vin_info.wmi)
print(vin_info.vds)
print(vin_info.vis)
print('vin_info but no details')
#
#
# from pyvin import VIN
@ -76,20 +76,20 @@ import json
vin_no = '5LMCJ2D93NUL03460'
details = {
# 'Description': VIN(vin_no).description,
'make': VIN(vin_no).make,
'year': VIN(vin_no).model_year,
'model': VIN(vin_no).model,
'trim': VIN(vin_no).trim,
'Series': VIN(vin_no).series,
'body_class': VIN(vin_no).body_class,
'Type': VIN(vin_no).vehicle_type,
'Electrification level': VIN(vin_no).electrification_level,
}
print(details)
# vin_no = '5LMCJ2D93NUL03460'
#
# details = {
# # 'Description': VIN(vin_no).description,
# 'make': VIN(vin_no).make,
# 'year': VIN(vin_no).model_year,
# 'model': VIN(vin_no).model,
# 'trim': VIN(vin_no).trim,
# 'Series': VIN(vin_no).series,
# 'body_class': VIN(vin_no).body_class,
# 'Type': VIN(vin_no).vehicle_type,
# 'Electrification level': VIN(vin_no).electrification_level,
# }
# print(details)
# from vpic import Client
#