update
This commit is contained in:
commit
f98513f7d5
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,7 +6,6 @@ __pycache__
|
||||
db.sqlite3
|
||||
media
|
||||
./car_inventory/settings.py
|
||||
|
||||
# Backup files #
|
||||
*.bak
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -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')
|
||||
@ -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
3319703
car_specification_value.csv
Normal file
File diff suppressed because it is too large
Load Diff
31
clean_specs.py
Normal file
31
clean_specs.py
Normal 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}")
|
||||
@ -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
BIN
inventory/.DS_Store
vendored
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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):
|
||||
|
||||
@ -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
|
||||
117672
inventory/data/Final_Merged_Car_Generation_and_Serie_Data.csv
Normal file
117672
inventory/data/Final_Merged_Car_Generation_and_Serie_Data.csv
Normal file
File diff suppressed because it is too large
Load Diff
117672
inventory/data/Merged_CarSerie_Data.csv
Normal file
117672
inventory/data/Merged_CarSerie_Data.csv
Normal file
File diff suppressed because it is too large
Load Diff
15639
inventory/data/Merged_Car_Generation_and_Car_Serie.csv
Normal file
15639
inventory/data/Merged_Car_Generation_and_Car_Serie.csv
Normal file
File diff suppressed because it is too large
Load Diff
117672
inventory/data/Merged_Car_Generation_and_Serie_Data.csv
Normal file
117672
inventory/data/Merged_Car_Generation_and_Serie_Data.csv
Normal file
File diff suppressed because it is too large
Load Diff
15772
inventory/data/Updated_Merged_Car_Generation_and_Car_Serie.csv
Normal file
15772
inventory/data/Updated_Merged_Car_Generation_and_Car_Serie.csv
Normal file
File diff suppressed because it is too large
Load Diff
117672
inventory/data/Updated_Merged_Car_Generation_and_Serie_Data.csv
Normal file
117672
inventory/data/Updated_Merged_Car_Generation_and_Serie_Data.csv
Normal file
File diff suppressed because it is too large
Load Diff
412
inventory/data/car_make.csv
Normal file
412
inventory/data/car_make.csv
Normal 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
|
||||
|
5296
inventory/data/car_model.csv
Normal file
5296
inventory/data/car_model.csv
Normal file
File diff suppressed because it is too large
Load Diff
105
inventory/data/car_specification.csv
Normal file
105
inventory/data/car_specification.csv
Normal 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
|
||||
|
3319703
inventory/data/car_specification_value.csv
Normal file
3319703
inventory/data/car_specification_value.csv
Normal file
File diff suppressed because it is too large
Load Diff
4
inventory/data/car_specification_value1.csv
Normal file
4
inventory/data/car_specification_value1.csv
Normal 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
|
||||
|
86803
inventory/data/car_trim.csv
Normal file
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
84305
inventory/data/filters.csv
Normal file
File diff suppressed because it is too large
Load Diff
117672
inventory/data/final_merged_car_serie.csv
Normal file
117672
inventory/data/final_merged_car_serie.csv
Normal file
File diff suppressed because it is too large
Load Diff
502
inventory/data/generation.json
Normal file
502
inventory/data/generation.json
Normal 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
85843
inventory/data/mappings.csv
Normal file
File diff suppressed because it is too large
Load Diff
452
inventory/data/serie.json
Normal file
452
inventory/data/serie.json
Normal 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
|
||||
}
|
||||
]
|
||||
@ -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"
|
||||
|
||||
Binary file not shown.
56
inventory/management/commands/add_id_car_model.py
Normal file
56
inventory/management/commands/add_id_car_model.py
Normal 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}"))
|
||||
39
inventory/management/commands/serie_up.py
Normal file
39
inventory/management/commands/serie_up.py
Normal 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}"))
|
||||
@ -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}")
|
||||
|
||||
34
inventory/management/commands/update_car_make.py
Normal file
34
inventory/management/commands/update_car_make.py
Normal 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"))
|
||||
37
inventory/management/commands/update_car_model.py
Normal file
37
inventory/management/commands/update_car_model.py
Normal 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}"))
|
||||
@ -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}"))
|
||||
47
inventory/management/commands/update_car_trim.py
Normal file
47
inventory/management/commands/update_car_trim.py
Normal 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}"))
|
||||
53
inventory/migrations/0017_organization_representative.py
Normal file
53
inventory/migrations/0017_organization_representative.py
Normal 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),
|
||||
),
|
||||
]
|
||||
@ -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'),
|
||||
),
|
||||
]
|
||||
17
inventory/migrations/0019_remove_carfinance_total.py
Normal file
17
inventory/migrations/0019_remove_carfinance_total.py
Normal 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',
|
||||
),
|
||||
]
|
||||
@ -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'),
|
||||
),
|
||||
]
|
||||
@ -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),
|
||||
),
|
||||
]
|
||||
@ -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'),
|
||||
),
|
||||
]
|
||||
19
inventory/migrations/0023_cartrim_id_car_model.py
Normal file
19
inventory/migrations/0023_cartrim_id_car_model.py
Normal 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'),
|
||||
),
|
||||
]
|
||||
@ -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}"
|
||||
|
||||
|
||||
@ -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)
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
|
||||
@ -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'
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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
28
merge_db.py
Normal 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
126106
merged_car_data.json
Normal file
File diff suppressed because it is too large
Load Diff
1342
new_wmis.py
1342
new_wmis.py
File diff suppressed because it is too large
Load Diff
BIN
static/images/logos/.DS_Store
vendored
BIN
static/images/logos/.DS_Store
vendored
Binary file not shown.
@ -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', () => {
|
||||
|
||||
@ -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">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Previous">
|
||||
<span aria-hidden="true">«</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">»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Next">
|
||||
<span aria-hidden="true">»</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">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link" aria-hidden="true">«</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">»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link" aria-hidden="true">»</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% endblock %}
|
||||
@ -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"
|
||||
|
||||
@ -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");
|
||||
|
||||
|
||||
@ -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 %}
|
||||
|
||||
@ -467,7 +467,7 @@ async function updateFields(vinData) {
|
||||
if (vinData.model_id) {
|
||||
modelSelect.value = vinData.model_id;
|
||||
document.getElementById("model-check").innerHTML = '✓';
|
||||
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
|
||||
|
||||
@ -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">
|
||||
««
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous">
|
||||
«
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link">««</span>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link">«</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">
|
||||
»
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}" aria-label="Last">
|
||||
»»
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link">»</span>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link">»»</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">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Previous">
|
||||
<span aria-hidden="true">«</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">»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -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 %}
|
||||
10
templates/organizations/organization_confirm_delete.html
Normal file
10
templates/organizations/organization_confirm_delete.html
Normal file
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
21
templates/organizations/organization_detail.html
Normal file
21
templates/organizations/organization_detail.html
Normal 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 %}
|
||||
16
templates/organizations/organization_form.html
Normal file
16
templates/organizations/organization_form.html
Normal 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 %}
|
||||
43
templates/organizations/organization_list.html
Normal file
43
templates/organizations/organization_list.html
Normal 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 %}
|
||||
10
templates/representatives/representative_confirm_delete.html
Normal file
10
templates/representatives/representative_confirm_delete.html
Normal file
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
20
templates/representatives/representative_detail.html
Normal file
20
templates/representatives/representative_detail.html
Normal 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 %}
|
||||
15
templates/representatives/representative_form.html
Normal file
15
templates/representatives/representative_form.html
Normal 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 %}
|
||||
41
templates/representatives/representative_list.html
Normal file
41
templates/representatives/representative_list.html
Normal 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 %}
|
||||
@ -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">
|
||||
|
||||
@ -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>
|
||||
|
||||
4
templates/vendors/vendors_list.html
vendored
4
templates/vendors/vendors_list.html
vendored
@ -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>
|
||||
|
||||
4
templates/vendors/view_vendor.html
vendored
4
templates/vendors/view_vendor.html
vendored
@ -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">
|
||||
|
||||
108
testapi.py
108
testapi.py
@ -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
|
||||
#
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user