add qotation

# Conflicts:
#	car_inventory/settings.py
#	inventory/models.py
#	inventory/services.py
#	inventory/urls.py
#	inventory/views.py
This commit is contained in:
Marwan Alwali 2024-12-10 13:52:44 +03:00
parent 6cf77729a6
commit 88e81fadf1
61 changed files with 1700 additions and 1376 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -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! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True 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 # Application definition
@ -110,9 +110,9 @@ WSGI_APPLICATION = 'car_inventory.wsgi.application'
DATABASES = { DATABASES = {
"default": { "default": {
"ENGINE": "django_prometheus.db.backends.postgresql", "ENGINE": "django_prometheus.db.backends.postgresql",
"NAME": "haikal", "NAME": "haikal_app",
"USER": "haikal", "USER": "f95166",
"PASSWORD": "haikal", "PASSWORD": "Kfsh&rc9788",
"HOST": "localhost", "HOST": "localhost",
"PORT": 5432, "PORT": 5432,
} }

69
carapi.py Normal file
View File

@ -0,0 +1,69 @@
import requests
import csv
# Replace with your actual API token and secret key
API_TOKEN = 'f5204a00-6f31-4de2-96d8-ed998e0d230c'
SECRET_KEY = 'ae430502a5c66e818d9722919c8b5584'
BASE_URL = 'https://carapi.app/api/v1'
def fetch_and_save_car_makes_models(api_url, headers, output_csv):
TRANSLATION = []
page = 1
pages_per_batch = 1
while True:
responses = []
# Fetch 100 pages in one batch
for i in range(pages_per_batch):
current_page = page + i
response = requests.get(f"{api_url}&page={current_page}", headers=headers)
if response.status_code == 200 and page < 100:
responses.append(response.json())
else:
break
# Process the batch of responses
for data in responses:
if 'data' not in data:
continue
for item in data['data']:
make_name = item['make_model']['make']['name']
model_name = item['make_model']['name']
# Create dictionary for each make and model combination
translation_entry = {
'make': f"{make_name} ",
'model': f"{model_name} " # Replace with actual Arabic translation if available
}
TRANSLATION.append(translation_entry)
# Increment the page number for the next batch
page += pages_per_batch
# Check if there are more pages to fetch
if not responses or ('next' in responses[-1]['collection'] and not responses[-1]['collection']['next']):
break
# Save the TRANSLATION data to a CSV file
with open(output_csv, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ['make', 'model']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for entry in TRANSLATION:
writer.writerow(entry)
print(f"Data saved to {output_csv}")
# Example usage:
api_url = "https://carapi.app/api/trims?sort=make_model_id&direction=asc&verbose=yes"
headers = {
'Authorization': 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJjYXJhcGkuYXBwIiwic3ViIjoiYjU1OGYzMDMtODI0Ni00NjgzLTkwYTQtZmYwMGQxYWNmNGU3IiwiYXVkIjoiYjU1OGYzMDMtODI0Ni00NjgzLTkwYTQtZmYwMGQxYWNmNGU3IiwiZXhwIjoxNzIzNzMxODMyLCJpYXQiOjE3MjMxMjcwMzIsImp0aSI6IjNkMGJhMzA4LWUzZTAtNGJhZC1iZmMxLTBiMDA3YzNmMmE2NSIsInVzZXIiOnsic3Vic2NyaWJlZCI6dHJ1ZSwic3Vic2NyaXB0aW9uIjoic3RhcnRlciIsInJhdGVfbGltaXRfdHlwZSI6ImhhcmQiLCJhZGRvbnMiOnsiYW50aXF1ZV92ZWhpY2xlcyI6ZmFsc2UsImRhdGFfZmVlZCI6ZmFsc2V9fX0.t__L53yN0OndnOP3_YxaAbrwgQXSYwVUgEqE1IwH8Nk', # Replace with actual token
'Content-Type': 'application/json'
}
output_csv = 'car_makes_models.csv'
fetch_and_save_car_makes_models(api_url, headers, output_csv)

View File

@ -6,23 +6,24 @@ from typing import List
def vin_years(vin_char: str) -> List[int]: def vin_years(vin_char: str) -> List[int]:
letters = 'ABCDEFGHJKLMNPRSTVWXY123456789' letters = 'ABCDEFGHJKLMNPRSTVWXY123456789'
year_char = vin_char.upper() year_letter = vin_char
if year_char not in letters:
raise ValueError("Invalid VIN character. Must be A-Z (excluding I, O, Q) or 1-9.")
year = 1979 year = 1979
current_year = datetime.now().year year_current = datetime.now().year
result = [] result = []
for letter in cycle(letters): for letter in cycle(letters):
year += 1 year += 1
if letter == year_char:
if letter == year_letter:
result.append(year) result.append(year)
if year >= current_year:
if year == year_current:
break break
result.sort(reverse=True) result.sort(reverse=True)
return result return result
@ -150,7 +151,6 @@ wmi_manufacturer_mapping = {
"J8T": "GMC", "J8T": "GMC",
"J8Z": "Chevrolet", "J8Z": "Chevrolet",
"KF3": "Merkavim", "KF3": "Merkavim",
"KF6": "Automotive Industries,",
"KL": "Daewoo", "KL": "Daewoo",
"KLA": "Daewoo", "KLA": "Daewoo",
"KLP": "CT&T United", "KLP": "CT&T United",
@ -190,9 +190,9 @@ wmi_manufacturer_mapping = {
"KPT": "SsangYong", "KPT": "SsangYong",
"LA6": "King Long", "LA6": "King Long",
"LC0": "BYD", "LC0": "BYD",
"LBE": "Beijing-Hyundai Shouwang", "LBE": "Hyundai Shouwang",
"LBM": "Zongshen Piaggio", "LBM": "Zongshen Piaggio",
"LBV": "BMW Brilliance", "LBV": "BMW",
"LB1": "Fujian Benz", "LB1": "Fujian Benz",
"LB3": "Geely", "LB3": "Geely",
"LCR": "Gonow", "LCR": "Gonow",
@ -222,17 +222,15 @@ wmi_manufacturer_mapping = {
"LGL": "Guilin Daewoo", "LGL": "Guilin Daewoo",
"LGW": "Great Wall", "LGW": "Great Wall",
"LGX": "BYD", "LGX": "BYD",
"LGZ": "Guangzhou Denway Bus",
"LHA": "Shuanghuan", "LHA": "Shuanghuan",
"LHB": "BAIC", "LHB": "BAIC",
"LHG": "Guangzhou Honda", "LHG": "Guangzhou Honda",
"LH1": "FAW", "LH1": "FAW",
"LJC": "Jincheng", "LJC": "Jincheng",
"LJD": "Human Horizons - HiPhi", "LJD": "Yueda Kia",
"LJN": "Zhengzhou Nissan", "LJN": "Zhengzhou Nissan",
"LJS": "Yaxing Coach", "LJS": "Yaxing Coach",
"LJU": "Lotus Geely", "LJU": "Lotus Geely",
"LJV": "Chengdu Wangpai",
"LJX": "JMC Ford", "LJX": "JMC Ford",
"LJ1": "Nio", "LJ1": "Nio",
"LJ8": "Zotye", "LJ8": "Zotye",
@ -260,7 +258,7 @@ wmi_manufacturer_mapping = {
"LPE": "BYD", "LPE": "BYD",
"LPS": "Polestar", "LPS": "Polestar",
"LRB": "SAIC Buick", "LRB": "SAIC Buick",
"LRD": "Foton Daimler", "LRD": "Foton",
"LRE": "SAIC Cadillac", "LRE": "SAIC Cadillac",
"LRW": "Tesla", "LRW": "Tesla",
"LSC": "Changan", "LSC": "Changan",
@ -270,15 +268,14 @@ wmi_manufacturer_mapping = {
"LSJ": "SAIC MG", "LSJ": "SAIC MG",
"LSK": "SAIC Maxus", "LSK": "SAIC Maxus",
"LSV": "SAIC Volkswagen", "LSV": "SAIC Volkswagen",
"LSY": "Brilliance Jinbei GM", "LSY": "Brilliance",
"LS4": "Changan", "LS4": "Changan",
"LS5": "Changan", "LS5": "Changan",
"LS6": "Changan", "LS6": "Changan",
"LS7": "JMC", "LS7": "JMC",
"LTA": "ZX Auto", "LTA": "ZX Auto",
"LTN": "Soueast", "LTN": "Soueast",
"LTP": "National Electric Vehicle Sweden AB", "LTV": "FAW Toyota",
"LTV": "FAW Toyota (Tianjin)",
"LUC": "Honda", "LUC": "Honda",
"LUD": "Dongfeng Nissan", "LUD": "Dongfeng Nissan",
"LUX": "Dongfeng Yulon", "LUX": "Dongfeng Yulon",
@ -322,18 +319,15 @@ wmi_manufacturer_mapping = {
"L4F": "Suzhou Eagle", "L4F": "Suzhou Eagle",
"L5C": "KangDi", "L5C": "KangDi",
"L5K": "Yongkang", "L5K": "Yongkang",
"L6T": "Geely, Lynk & Co, Zeekr", "L6T": "Lynk & Co",
"L82": "Baotian", "L82": "Baotian",
"L85": "Yongkang Huabao", "L85": "Yongkang Huabao",
"L9N": "Taotao", "L9N": "Taotao",
"MAB": "Mahindra & Mahindra",
"MAC": "Mahindra & Mahindra",
"MAH": "Fiat", "MAH": "Fiat",
"MAJ": "Ford", "MAJ": "Ford",
"MAK": "Honda", "MAK": "Honda",
"MAL": "Hyundai", "MAL": "Hyundai",
"MAT": "Tatar", "MAT": "Tatar",
"MA1": "Mahindra & Mahindra",
"MA3": "Suzuki", "MA3": "Suzuki",
"MA6": "GM", "MA6": "GM",
"MA7": "Hindustan", "MA7": "Hindustan",
@ -348,10 +342,7 @@ wmi_manufacturer_mapping = {
"MBX": "Piaggio", "MBX": "Piaggio",
"MBY": "Asia Motors", "MBY": "Asia Motors",
"MB1": "Ashok Leyland", "MB1": "Ashok Leyland",
"MB7": "Reva Electric Company",
"MCA": "FCA Pvt. Ltd",
"MCB": "GM", "MCB": "GM",
"MCD": "Mahindra Two Wheelers",
"MCG": "Atul", "MCG": "Atul",
"MC1": "Force", "MC1": "Force",
"MC2": "Eicher", "MC2": "Eicher",
@ -361,7 +352,7 @@ wmi_manufacturer_mapping = {
"MD6": "TVS", "MD6": "TVS",
"MD9": "Shuttles", "MD9": "Shuttles",
"MEC": "Daimler", "MEC": "Daimler",
"MEE": "Renault Private Limited", "MEE": "Renault",
"MEG": "Harley-Davidson", "MEG": "Harley-Davidson",
"MER": "Benelli", "MER": "Benelli",
"MET": "Piaggio", "MET": "Piaggio",
@ -502,7 +493,7 @@ wmi_manufacturer_mapping = {
"SAJ": "Jaguar", "SAJ": "Jaguar",
"SAL": "Land Rover", "SAL": "Land Rover",
"SAM": "Morris", "SAM": "Morris",
"SAR": "Rover MG Rover", "SAR": "Rover MG",
"SAT": "Triumph", "SAT": "Triumph",
"SAX": "Austin-Rover", "SAX": "Austin-Rover",
"SAZ": "Freight Rover", "SAZ": "Freight Rover",
@ -513,7 +504,6 @@ wmi_manufacturer_mapping = {
"SBL": "Leyland", "SBL": "Leyland",
"SBM": "McLaren", "SBM": "McLaren",
"SBS": "Scammell", "SBS": "Scammell",
"SBV": "Kenworth Peterbilt",
"SB1": "Toyota", "SB1": "Toyota",
"SCA": "Rolls Royce", "SCA": "Rolls Royce",
"SCB": "Bentley", "SCB": "Bentley",
@ -568,13 +558,13 @@ wmi_manufacturer_mapping = {
"TN9": "Karosa", "TN9": "Karosa",
"TRA": "Ikarus Bus", "TRA": "Ikarus Bus",
"TRC": "Csepel", "TRC": "Csepel",
"TRU": "Audi Hungary", "TRU": "Audi",
"TSB": "Ikarus Bus", "TSB": "Ikarus Bus",
"TSE": "Ikarus,", "TSE": "Ikarus,",
"TSF": "Alfabusz", "TSF": "Alfabusz",
"TSM": "Suzuki Fiat", "TSM": "Suzuki Fiat",
"TWG": "Ceatano Bus", "TWG": "Ceatano Bus",
"TW1": "Toyota Caetano", "TW1": "Toyota",
"TW2": "Ford", "TW2": "Ford",
"TW6": "Citroën", "TW6": "Citroën",
"TW7": "Mini", "TW7": "Mini",
@ -670,7 +660,6 @@ wmi_manufacturer_mapping = {
"WBU": "Bürstner", "WBU": "Bürstner",
"WBX": "BMW", "WBX": "BMW",
"WBY": "BMW", "WBY": "BMW",
"WB0": "Böckmann Fahrzeugwerke GmbH",
"WB1": "BMW", "WB1": "BMW",
"WB5": "BMW", "WB5": "BMW",
"WCD": "Freightliner", "WCD": "Freightliner",
@ -691,14 +680,11 @@ wmi_manufacturer_mapping = {
"WD6": "Freightliner", "WD6": "Freightliner",
"WD8": "Dodge", "WD8": "Dodge",
"WEB": "Evobus GmbH", "WEB": "Evobus GmbH",
"WEL": "e.GO Mobile AG",
"WFC": "Fendt", "WFC": "Fendt",
"WFD": "Fliegl Trailer", "WFD": "Fliegl Trailer",
"WF0": "Ford", "WF0": "Ford",
"WF1": "Merkur", "WF1": "Merkur",
"WHB": "Hobby", "WHB": "Hobby",
"WHD": "Humbaur GmbH",
"WHW": "Hako GmbH",
"WHY": "Hymer", "WHY": "Hymer",
"WJM": "Iveco", "WJM": "Iveco",
"WJR": "Irmscher", "WJR": "Irmscher",
@ -760,12 +746,10 @@ wmi_manufacturer_mapping = {
"XMC": "Mitsubishi", "XMC": "Mitsubishi",
"XMD": "Mitsubishi", "XMD": "Mitsubishi",
"XMG": "VDL Bus", "XMG": "VDL Bus",
"XMR": "Nooteboom Trailers", "XMR": "Nooteboom",
"XM4": "RAVO Holding", "XM4": "RAVO Holding",
"XNB": "Mitsubishi", "XNB": "Mitsubishi",
"XNC": "Mitsubishi", "XNC": "Mitsubishi",
"XNL": "VDL Bus & Coach",
"XPN": "Knapen Trailers",
"XP7": "Tesla", "XP7": "Tesla",
"XTA": "Lada", "XTA": "Lada",
"XTB": "Moskvitch", "XTB": "Moskvitch",
@ -803,7 +787,6 @@ wmi_manufacturer_mapping = {
"X9X": "Great Wall", "X9X": "Great Wall",
"YAF": "Faymonville", "YAF": "Faymonville",
"YAR": "Toyota", "YAR": "Toyota",
"YA9": "Lambrecht Constructie NV",
"YBW": "Volkswagen", "YBW": "Volkswagen",
"YB1": "Volvo", "YB1": "Volvo",
"YB2": "Volvo", "YB2": "Volvo",
@ -812,7 +795,7 @@ wmi_manufacturer_mapping = {
"YH1": "Solifer", "YH1": "Solifer",
"YH2": "BRP", "YH2": "BRP",
"YH4": "Fisker", "YH4": "Fisker",
"YK1": "Saab-Valmet", "YK1": "Saab",
"YSC": "Cadillac", "YSC": "Cadillac",
"YSM": "Polestars", "YSM": "Polestars",
"YSP": "Volta AB", "YSP": "Volta AB",
@ -827,7 +810,6 @@ wmi_manufacturer_mapping = {
"YV3": "Volvos", "YV3": "Volvos",
"YV4": "Volvo", "YV4": "Volvo",
"YYC": "Think Nordic", "YYC": "Think Nordic",
"Y3J": "Belkommunmash",
"Y3M": "MAZ", "Y3M": "MAZ",
"Y4F": "Ford", "Y4F": "Ford",
"Y4K": "Geely", "Y4K": "Geely",
@ -841,19 +823,19 @@ wmi_manufacturer_mapping = {
"Y8A": "LAZ", "Y8A": "LAZ",
"Y9H": "LAZ", "Y9H": "LAZ",
"ZAA": "Autobianchi", "ZAA": "Autobianchi",
"ZAC": "Jeep Dodge Hornet", "ZAC": "Jeep Dodge",
"ZAM": "Maserati", "ZAM": "Maserati",
"ZAP": "Piaggio", "ZAP": "Piaggio",
"ZAR": "Alfa Romeo", "ZAR": "Alfa Romeo",
"ZAS": "Alfa Romeo", "ZAS": "Alfa Romeo",
"ZBB": "Bertone", "ZBB": "Bertone",
"ZBN": "Benelli", "ZBN": "Benelli",
"ZBW": "Rayton-Fissore Magnum", "ZBW": "Rayton-Fissore",
"ZCB": "Bartoletti", "ZCB": "Bartoletti",
"ZCF": "Iveco", "ZCF": "Iveco",
"ZCG": "Cagiva", "ZCG": "Cagiva",
"ZCM": "Menarinibus", "ZCM": "Menarinibus",
"ZC2": "Chrysler Maserati", "ZC2": "Chrysler",
"ZDC": "Honda", "ZDC": "Honda",
"ZDF": "Ferrari", "ZDF": "Ferrari",
"ZDM": "Ducati", "ZDM": "Ducati",
@ -895,8 +877,6 @@ wmi_manufacturer_mapping = {
"Z8T": "PCMA", "Z8T": "PCMA",
"Z9M": "Mercedes-Benz", "Z9M": "Mercedes-Benz",
"Z94": "Hyundai", "Z94": "Hyundai",
"1AC": "American Corporation",
"1AF": "American LaFrance",
"1B3": "Dodge", "1B3": "Dodge",
"1B4": "Dodge", "1B4": "Dodge",
"1B7": "Dodge", "1B7": "Dodge",
@ -946,7 +926,7 @@ wmi_manufacturer_mapping = {
"1GY": "Cadillac", "1GY": "Cadillac",
"1HD": "Harley-Davidson", "1HD": "Harley-Davidson",
"1HG": "Honda", "1HG": "Honda",
"1HS": "International & Caterpillar", "1HS": "Caterpillar",
"1JC": "Jeep", "1JC": "Jeep",
"1JT": "Jeep", "1JT": "Jeep",
"1JU": "Marmon", "1JU": "Marmon",
@ -969,10 +949,7 @@ wmi_manufacturer_mapping = {
"1P3": "Plymouth", "1P3": "Plymouth",
"1P4": "Plymouth", "1P4": "Plymouth",
"1PY": "John Deere", "1PY": "John Deere",
"1T7": "Thomas Built Buses",
"1T8": "Thomas Built Buses",
"1TC": "Coachmen", "1TC": "Coachmen",
"1TU": "Transportation Manufacturing Corporation",
"1UJ": "Jayco", "1UJ": "Jayco",
"1UT": "Jeep", "1UT": "Jeep",
"1VH": "Orion Bus", "1VH": "Orion Bus",
@ -993,8 +970,6 @@ wmi_manufacturer_mapping = {
"10T": "Oshkosh", "10T": "Oshkosh",
"12A": "Avanti", "12A": "Avanti",
"137": "Hummer", "137": "Hummer",
"15G": "Gillig bus",
"16C": "Clenet Coachworks",
"16X": "Vixen", "16X": "Vixen",
"19U": "Acura", "19U": "Acura",
"19V": "Acura", "19V": "Acura",
@ -1549,7 +1524,7 @@ def decode_vin(vin):
return { return {
'VIN': vin, 'VIN': vin,
'Manufacturer': manufacturer, 'Manufacturer': manufacturer,
'Year': year[0], 'Year': year,
'Model': model 'Model': model
} }
@ -1558,7 +1533,7 @@ def decode_vin(vin):
# VR3USHNLWRJ521303 # VR3USHNLWRJ521303
# KNARH81E8P5194005 # KNARH81E8P5194005
# Example usage # Example usage
vin_number = 'LJD5AA1DXR0104257' vin_number = 'LGWCBE196SB652802'
decoded_vin = decode_vin(vin_number) decoded_vin = decode_vin(vin_number)
print(decoded_vin) print(decoded_vin)

View File

@ -5,17 +5,22 @@ from . import models
admin.site.register(models.Dealer) admin.site.register(models.Dealer)
admin.site.register(models.Vendor) admin.site.register(models.Vendor)
admin.site.register(models.Customer) admin.site.register(models.Customer)
admin.site.register(models.SaleQuotation)
admin.site.register(models.SaleQuotationCar)
admin.site.register(models.Car) admin.site.register(models.Car)
admin.site.register(models.CarFinance) admin.site.register(models.CarFinance)
admin.site.register(models.CarColors) admin.site.register(models.CarColors)
admin.site.register(models.CarRegistration) admin.site.register(models.CarRegistration)
admin.site.register(models.CustomCard) admin.site.register(models.CustomCard)
admin.site.register(models.CarSpecificationValue) admin.site.register(models.CarSpecificationValue)
admin.site.register(models.ExteriorColors)
admin.site.register(models.InteriorColors)
@admin.register(models.CarMake) @admin.register(models.CarMake)
class CarMakeAdmin(admin.ModelAdmin): class CarMakeAdmin(admin.ModelAdmin):
list_display = ('name', 'arabic_name', 'is_sa_import') list_display = ('name', 'arabic_name', 'is_sa_import')
search_fields = ('name', 'arabic_name') search_fields = ('name', 'arabic_name')
list_filter = ('is_sa_import', 'name',)
class Meta: class Meta:
verbose_name = "Car Make" verbose_name = "Car Make"

View File

@ -1,5 +1,6 @@
from django import forms from django import forms
from .mixins import AddClassMixin from .mixins import AddClassMixin
from django.forms.models import inlineformset_factory
from .models import ( from .models import (
Dealer, Dealer,
# Branch, # Branch,
@ -9,9 +10,12 @@ from .models import (
CarFinance, CarFinance,
CustomCard, CustomCard,
CarRegistration, CarRegistration,
CarColors CarColors,
ExteriorColors,
InteriorColors,
SaleQuotation
) )
from django.contrib.contenttypes.forms import generic_inlineformset_factory from django.forms import ModelMultipleChoiceField
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -19,7 +23,7 @@ from django.utils.translation import gettext_lazy as _
class DealerForm(forms.ModelForm): class DealerForm(forms.ModelForm):
class Meta: class Meta:
model = Dealer model = Dealer
fields = ['crn', 'vrn', 'arabic_name', 'name', 'phone_number', 'address', 'logo'] fields = ['name', 'arabic_name', 'crn', 'vrn', 'phone_number', 'address', 'logo']
# Customer Form # Customer Form
@ -87,57 +91,10 @@ class CarUpdateForm(forms.ModelForm, AddClassMixin):
class CarFinanceForm(AddClassMixin, forms.ModelForm): class CarFinanceForm(AddClassMixin, forms.ModelForm):
profit_margin_percentage = forms.DecimalField(
max_digits=10,
decimal_places=2,
min_value=0,
max_value=100,
label="Profit Margin",
required=True,
widget=forms.NumberInput(attrs={'min': '0', 'max': '100', 'step': '0.01'})
)
vat_rate_percentage = forms.DecimalField(
max_digits=10,
decimal_places=2,
min_value=0,
max_value=100,
label="Vat Rate",
required=True,
widget=forms.NumberInput(attrs={'min': '0', 'max': '100', 'step': '0.01'})
)
class Meta: class Meta:
model = CarFinance model = CarFinance
fields = ['cost_price'] exclude = ['car', 'profit_margin', 'vat_amount', 'total', 'vat_rate']
def __init__(self, *args, **kwargs):
super(CarFinanceForm, self).__init__(*args, **kwargs)
if self.instance and self.instance.pk:
# Convert profit_margin from decimal to percentage for initial display
self.fields['profit_margin_percentage'].initial = self.instance.profit_margin * 100
self.fields['vat_rate_percentage'].initial = self.instance.vat_rate * 100
def clean_profit_margin_percentage(self):
profit_margin_percentage = self.cleaned_data['profit_margin_percentage']
if not (0 <= profit_margin_percentage <= 100):
raise forms.ValidationError('Profit margin must be between 0 and 100.')
return profit_margin_percentage
def clean_vat_rate_percentage(self):
vat_rate_percentage = self.cleaned_data['vat_rate_percentage']
if not (0 <= vat_rate_percentage <= 100):
raise forms.ValidationError('vat rate must be between 0 and 100.')
return vat_rate_percentage
def save(self, commit=True):
instance = super(CarFinanceForm, self).save(commit=False)
profit_margin_percentage = self.cleaned_data['profit_margin_percentage']
vat_rate_percentage = self.cleaned_data['vat_rate_percentage']
instance.profit_margin = profit_margin_percentage / 100
instance.vat_rate = vat_rate_percentage / 100
if commit:
instance.save()
return instance
# Custom Card Form # Custom Card Form
@ -165,3 +122,52 @@ class VendorForm(forms.ModelForm):
class Meta: class Meta:
model = Vendor model = Vendor
exclude = ['dealer'] exclude = ['dealer']
class CarColorsForm(forms.ModelForm):
class Meta:
model = CarColors
fields = ['exterior', 'interior']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['exterior'].queryset = ExteriorColors.objects.all()
self.fields['exterior'].widget = forms.RadioSelect(attrs={'class': 'form-check-input'})
self.fields['exterior'].choices = [
(color.id, f"{color.get_local_name}") for color in ExteriorColors.objects.all().order_by('-name')
]
self.fields['interior'].queryset = InteriorColors.objects.all()
self.fields['interior'].widget = forms.RadioSelect(attrs={'class': 'form-check-input'})
self.fields['interior'].choices = [
(color.id, f"{color.get_local_name}") for color in InteriorColors.objects.all().order_by('-name')
]
def clean(self):
cleaned_data = super().clean()
exterior = cleaned_data.get("exterior")
interior = cleaned_data.get("interior")
if not exterior or not interior:
raise forms.ValidationError(_("Both exterior and interior colors must be selected."))
return cleaned_data
class QuotationForm(forms.ModelForm):
cars = ModelMultipleChoiceField(
queryset=Car.objects.none(), # Default empty queryset
widget=forms.CheckboxSelectMultiple,
label="Select Cars"
)
class Meta:
model = SaleQuotation
fields = ['customer', 'cars', 'remarks']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['cars'].queryset = Car.objects.filter(
finances__isnull=False
).distinct()

View File

@ -0,0 +1,100 @@
from django.core.management.base import BaseCommand
from inventory.models import ExteriorColors, InteriorColors
class Command(BaseCommand):
help = "Populates the ExteriorColors and InteriorColors models with predefined data"
exterior_colors = [
{"rgb": "192, 192, 192", "name": "Silver Metallic", "arabic_name": "فضي معدني"},
{"rgb": "0, 0, 0", "name": "Jet Black", "arabic_name": "أسود نفاث"},
{"rgb": "255, 255, 255", "name": "Bright White", "arabic_name": "أبيض ناصع"},
{"rgb": "128, 128, 128", "name": "Graphite Gray", "arabic_name": "رمادي غرافيتي"},
{"rgb": "80, 80, 80", "name": "Gunmetal Gray", "arabic_name": "رمادي معدني"},
{"rgb": "255, 0, 0", "name": "Racing Red", "arabic_name": "أحمر سباق"},
{"rgb": "255, 69, 0", "name": "Inferno Orange", "arabic_name": "برتقالي جهنمي"},
{"rgb": "0, 0, 255", "name": "Deep Blue Pearl", "arabic_name": "أزرق لؤلؤي عميق"},
{"rgb": "75, 0, 130", "name": "Indigo Night", "arabic_name": "ليلة نيلي"},
{"rgb": "255, 215, 0", "name": "Solar Gold", "arabic_name": "ذهبي شمسي"},
{"rgb": "34, 139, 34", "name": "Emerald Green", "arabic_name": "أخضر زمردي"},
{"rgb": "60, 179, 113", "name": "Forest Mist Green", "arabic_name": "أخضر ضباب الغابة"},
{"rgb": "255, 140, 0", "name": "Burnt Amber", "arabic_name": "كهرماني محروق"},
{"rgb": "160, 82, 45", "name": "Copper Brown", "arabic_name": "بني نحاسي"},
{"rgb": "128, 0, 0", "name": "Crimson Maroon", "arabic_name": "ماروني قرمزي"},
{"rgb": "245, 245, 220", "name": "Beige Champagne", "arabic_name": "بيج شامبين"},
{"rgb": "169, 169, 169", "name": "Shadow Gray", "arabic_name": "رمادي ظل"},
{"rgb": "255, 250, 205", "name": "Lemon Pearl", "arabic_name": "لؤلؤي ليموني"},
{"rgb": "220, 220, 220", "name": "Platinum Silver", "arabic_name": "فضي بلاتيني"},
{"rgb": "105, 105, 105", "name": "Charcoal Metallic", "arabic_name": "رمادي فحمي معدني"},
{"rgb": "128, 0, 128", "name": "Royal Purple", "arabic_name": "أرجواني ملكي"},
{"rgb": "210, 105, 30", "name": "Sunset Bronze", "arabic_name": "برونزي الغروب"},
{"rgb": "0, 128, 128", "name": "Teal Lagoon", "arabic_name": "أزرق مخضر بحري"},
{"rgb": "72, 61, 139", "name": "Midnight Blue", "arabic_name": "أزرق منتصف الليل"},
{"rgb": "255, 20, 147", "name": "Blazing Pink", "arabic_name": "وردي لامع"},
{"rgb": "192, 57, 43", "name": "Crimson Flame", "arabic_name": "لهب قرمزي"},
{"rgb": "255, 228, 196", "name": "Cream Sand", "arabic_name": "كريم رملي"},
{"rgb": "112, 128, 144", "name": "Steel Gray", "arabic_name": "رمادي فولاذي"},
{"rgb": "0, 100, 0", "name": "Hunter Green", "arabic_name": "أخضر صياد"},
{"rgb": "255, 223, 0", "name": "Bright Yellow", "arabic_name": "أصفر ساطع"},
{"rgb": "85, 107, 47", "name": "Olive Metallic", "arabic_name": "زيتوني معدني"},
{"rgb": "128, 128, 0", "name": "Mustard Gold", "arabic_name": "ذهبي خردلي"},
{"rgb": "139, 69, 19", "name": "Cocoa Brown", "arabic_name": "بني كاكاو"},
{"rgb": "255, 165, 0", "name": "Tangerine Flame", "arabic_name": "لهب المندرين"},
{"rgb": "0, 0, 139", "name": "Navy Sapphire", "arabic_name": "كحلي ياقوتي"},
{"rgb": "70, 130, 180", "name": "Skyline Blue", "arabic_name": "أزرق أفق"},
{"rgb": "220, 20, 60", "name": "Crimson Passion", "arabic_name": "شغف قرمزي"},
{"rgb": "189, 183, 107", "name": "Khaki Dune", "arabic_name": "كاكي كثيب"},
{"rgb": "50, 205, 50", "name": "Lime Essence", "arabic_name": "ليموني نقي"},
{"rgb": "139, 0, 139", "name": "Amethyst Glow", "arabic_name": "توهج جمشت"},
{"rgb": "255, 215, 180", "name": "Rosé Gold", "arabic_name": "ذهبي وردي"},
{"rgb": "46, 139, 87", "name": "Moss Green", "arabic_name": "أخضر طحلبي"},
{"rgb": "72, 209, 204", "name": "Caribbean Aqua", "arabic_name": "أكوا كاريبي"},
{"rgb": "255, 240, 245", "name": "Pearl Blush", "arabic_name": "تورد لؤلؤي"},
{"rgb": "244, 164, 96", "name": "Sierra Sunset", "arabic_name": "غروب سييرا"},
{"rgb": "139, 0, 0", "name": "Crimson Ruby", "arabic_name": "روبي قرمزي"},
{"rgb": "192, 192, 192", "name": "Chrome", "arabic_name": "كروم"},
{"rgb": "255, 105, 180", "name": "Hot Magenta", "arabic_name": "ماجنتا ساخن"},
{"rgb": "0, 255, 255", "name": "Ice Blue", "arabic_name": "أزرق جليدي"},
{"rgb": "184, 134, 11", "name": "Golden Bronze", "arabic_name": "برونزي ذهبي"},
{"rgb": "128, 128, 64", "name": "Bronze Olive", "arabic_name": "زيتوني برونزي"},
]
interior_colors = [
{"rgb": "0, 0, 0", "name": "Jet Black", "arabic_name": "أسود نفاث"},
{"rgb": "54, 69, 79", "name": "Charcoal Black", "arabic_name": "أسود فحمي"},
{"rgb": "255, 255, 255", "name": "Bright White", "arabic_name": "أبيض ناصع"},
{"rgb": "245, 245, 220", "name": "Off-White", "arabic_name": "أبيض مصفر"},
{"rgb": "210, 180, 140", "name": "Beige", "arabic_name": "بيج"},
{"rgb": "203, 194, 190", "name": "Sandstone", "arabic_name": "حجر رملي"},
{"rgb": "80, 80, 80", "name": "Graphite Gray", "arabic_name": "رمادي غرافيتي"},
{"rgb": "127, 128, 133", "name": "Ebony/Medium Slate", "arabic_name": "إيبوني/لائحة متوسطة"},
{"rgb": "124, 79, 58", "name": "Mocha", "arabic_name": "موكا"},
{"rgb": "193, 154, 107", "name": "Camel Tan", "arabic_name": "بيج الجمل"},
{"rgb": "128, 0, 0", "name": "Maroon", "arabic_name": "مارون"},
{"rgb": "0, 0, 128", "name": "Navy Blue", "arabic_name": "أزرق بحري"},
{"rgb": "25, 25, 112", "name": "Midnight Blue", "arabic_name": "أزرق منتصف الليل"},
{"rgb": "72, 60, 50", "name": "Taupe", "arabic_name": "توب"},
]
def handle(self, *args, **kwargs):
self.stdout.write("Populating Exterior Colors...")
for color in self.exterior_colors:
obj, created = ExteriorColors.objects.get_or_create(
name=color["name"], arabic_name=color["arabic_name"], rgb=color["rgb"]
)
if created:
self.stdout.write(f"Added Exterior Color: {obj.name} ({obj.rgb})")
else:
self.stdout.write(f"Exterior Color already exists: {obj.name} ({obj.rgb})")
self.stdout.write("Populating Interior Colors...")
for color in self.interior_colors:
obj, created = InteriorColors.objects.get_or_create(
name=color["name"], arabic_name=color["arabic_name"], rgb=color["rgb"]
)
if created:
self.stdout.write(f"Added Interior Color: {obj.name} ({obj.rgb})")
else:
self.stdout.write(f"Interior Color already exists: {obj.name} ({obj.rgb})")
self.stdout.write("Finished populating colors.")

View File

@ -1,8 +1,9 @@
# Generated by Django 5.1.4 on 2024-12-04 23:43 # Generated by Django 5.1.4 on 2024-12-09 13:58
import django.db.models.deletion import django.db.models.deletion
import inventory.mixins import inventory.mixins
import phonenumber_field.modelfields import phonenumber_field.modelfields
from decimal import Decimal
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
@ -47,21 +48,56 @@ class Migration(migrations.Migration):
}, },
bases=(models.Model, inventory.mixins.LocalizedNameMixin), bases=(models.Model, inventory.mixins.LocalizedNameMixin),
), ),
migrations.CreateModel(
name='ExteriorColors',
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')),
('rgb', models.CharField(blank=True, max_length=24, null=True, verbose_name='RGB')),
],
options={
'verbose_name': 'Exterior Colors',
'verbose_name_plural': 'Exterior Colors',
},
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
),
migrations.CreateModel(
name='InteriorColors',
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')),
('rgb', models.CharField(blank=True, max_length=24, null=True, verbose_name='RGB')),
],
options={
'verbose_name': 'Interior Colors',
'verbose_name_plural': 'Interior Colors',
},
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
),
migrations.CreateModel( migrations.CreateModel(
name='CarFinance', name='CarFinance',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('cost_price', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Cost Price')), ('cost_price', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Cost Price')),
('profit_margin', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Profit Margin')), ('selling_price', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Selling Price')),
('selling_price', models.DecimalField(decimal_places=2, editable=False, max_digits=14, verbose_name='Selling Price')), ('profit_margin', models.DecimalField(decimal_places=2, editable=False, max_digits=14, verbose_name='Profit Margin')),
('vat_rate', models.DecimalField(decimal_places=2, default=0.15, max_digits=10, verbose_name='VAT Rate')), ('discount_amount', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Discount Amount')),
('vat_amount', models.DecimalField(decimal_places=2, editable=False, max_digits=12, verbose_name='VAT Amount')), ('registration_fee', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Registration Fee')),
('total', models.DecimalField(decimal_places=2, editable=False, max_digits=14, verbose_name='Total Amount')), ('administration_fee', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Administration Fee')),
('transportation_fee', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Transportation Fee')),
('custom_card_fee', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Custom Card Fee')),
('vat_rate', models.DecimalField(decimal_places=2, default=Decimal('0.15'), max_digits=14, verbose_name='VAT Rate')),
('administration_vat_amount', models.DecimalField(decimal_places=2, default=Decimal('0.00'), editable=False, max_digits=12, verbose_name='Administration VAT')),
('transportation_vat_amount', models.DecimalField(decimal_places=2, default=Decimal('0.00'), editable=False, max_digits=12, verbose_name='Transportation VAT')),
('custom_card_vat_amount', models.DecimalField(decimal_places=2, default=Decimal('0.00'), editable=False, max_digits=12, verbose_name='Custom Card VAT')),
('selling_vat_amount', models.DecimalField(decimal_places=2, default=Decimal('0.00'), editable=False, max_digits=12, verbose_name='Selling VAT')),
('total_vat_amount', models.DecimalField(decimal_places=2, default=Decimal('0.00'), editable=False, max_digits=12, verbose_name='Total VAT')),
('total_before_vat', models.DecimalField(decimal_places=2, default=Decimal('0.00'), editable=False, max_digits=14, verbose_name='Total Before VAT')),
('total', models.DecimalField(decimal_places=2, default=Decimal('0.00'), editable=False, max_digits=14, verbose_name='Total Amount')),
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='finances', to='inventory.car')), ('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='finances', to='inventory.car')),
], ],
options={
'verbose_name': 'Car Financial Details',
},
), ),
migrations.AddField( migrations.AddField(
model_name='car', model_name='car',
@ -171,7 +207,7 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('custom_number', models.CharField(max_length=255, verbose_name='Custom Number')), ('custom_number', models.CharField(max_length=255, verbose_name='Custom Number')),
('custom_date', models.DateTimeField(verbose_name='Custom Date')), ('custom_date', models.DateField(verbose_name='Custom Date')),
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='custom_cards', to='inventory.car', verbose_name='Car')), ('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='custom_cards', to='inventory.car', verbose_name='Car')),
], ],
options={ options={
@ -223,34 +259,22 @@ class Migration(migrations.Migration):
field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='cars', to='inventory.dealer', verbose_name='Dealer'), field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='cars', to='inventory.dealer', verbose_name='Dealer'),
), ),
migrations.CreateModel( migrations.CreateModel(
name='ExteriorColors', name='SaleQuotation',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, verbose_name='Name')), ('remarks', models.TextField(blank=True, null=True)),
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')), ('created_at', models.DateTimeField(auto_now_add=True)),
('rgb', models.CharField(blank=True, max_length=24, null=True, verbose_name='RGB')), ('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.customer')),
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='exteriorcolor', to='inventory.car')),
], ],
options={
'verbose_name': 'Exterior Color',
'verbose_name_plural': 'Exterior Colors',
},
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
), ),
migrations.CreateModel( migrations.CreateModel(
name='InteriorColors', name='SaleQuotationCar',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, verbose_name='Name')), ('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.car')),
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')), ('financial_details', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='quotation_finances', to='inventory.carfinance')),
('rgb', models.CharField(blank=True, max_length=24, null=True, verbose_name='RGB')), ('quotation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='quotation_cars', to='inventory.salequotation')),
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interiorcolor', to='inventory.car', verbose_name='Car')),
], ],
options={
'verbose_name': 'Interior Color',
'verbose_name_plural': 'Interior Colors',
},
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
), ),
migrations.CreateModel( migrations.CreateModel(
name='Vendor', name='Vendor',
@ -263,6 +287,7 @@ class Migration(migrations.Migration):
('contact_person', models.CharField(max_length=100, verbose_name='Contact Person')), ('contact_person', models.CharField(max_length=100, verbose_name='Contact Person')),
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone 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')), ('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')),
('logo', models.ImageField(blank=True, null=True, upload_to='logos/vendors', verbose_name='Logo')),
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vendors', to='inventory.dealer')), ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vendors', to='inventory.dealer')),
], ],
options={ options={
@ -290,4 +315,18 @@ class Migration(migrations.Migration):
'unique_together': {('car', 'reserved_until')}, 'unique_together': {('car', 'reserved_until')},
}, },
), ),
migrations.CreateModel(
name='CarColors',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='colors', to='inventory.car')),
('exterior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='colors', to='inventory.exteriorcolors')),
('interior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='colors', to='inventory.interiorcolors')),
],
options={
'verbose_name': 'Color',
'verbose_name_plural': 'Colors',
'unique_together': {('car', 'exterior', 'interior')},
},
),
] ]

View File

@ -1,39 +0,0 @@
# Generated by Django 5.1.4 on 2024-12-06 14:30
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='interiorcolors',
name='car',
),
migrations.CreateModel(
name='CarColors',
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')),
('rgb', models.CharField(blank=True, max_length=24, null=True, verbose_name='RGB')),
('color_type', models.CharField(choices=[('exterior', 'Exterior'), ('interior', 'Interior')], default='exterior', max_length=10, verbose_name='Color Type')),
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='colors', to='inventory.car')),
],
options={
'verbose_name': 'Color',
'verbose_name_plural': 'Colors',
},
),
migrations.DeleteModel(
name='ExteriorColors',
),
migrations.DeleteModel(
name='InteriorColors',
),
]

View File

@ -0,0 +1,98 @@
# Generated by Django 5.1.4 on 2024-12-09 20:42
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='salequotationcar',
name='financial_details',
),
migrations.AddField(
model_name='salequotation',
name='status',
field=models.CharField(choices=[('DRAFT', 'Draft'), ('CONFIRMED', 'Confirmed'), ('CANCELED', 'Canceled')], default='DRAFT', max_length=10, verbose_name='Status'),
),
migrations.AddField(
model_name='salequotation',
name='updated_at',
field=models.DateTimeField(auto_now=True, verbose_name='Updated At'),
),
migrations.AddField(
model_name='salequotationcar',
name='administration_fee',
field=models.DecimalField(decimal_places=2, default=150, max_digits=14, verbose_name='Administration Fee'),
preserve_default=False,
),
migrations.AddField(
model_name='salequotationcar',
name='custom_card_fee',
field=models.DecimalField(decimal_places=2, default=70, max_digits=14, verbose_name='Custom Card Fee'),
preserve_default=False,
),
migrations.AddField(
model_name='salequotationcar',
name='selling_price',
field=models.DecimalField(decimal_places=2, default=120000, max_digits=14, verbose_name='Selling Price'),
preserve_default=False,
),
migrations.AddField(
model_name='salequotationcar',
name='total_amount',
field=models.DecimalField(decimal_places=2, default=135000, max_digits=14, verbose_name='Total Amount'),
preserve_default=False,
),
migrations.AddField(
model_name='salequotationcar',
name='transportation_fee',
field=models.DecimalField(decimal_places=2, default=500, max_digits=14, verbose_name='Transportation Fee'),
preserve_default=False,
),
migrations.AddField(
model_name='salequotationcar',
name='vat_amount',
field=models.DecimalField(decimal_places=2, default=4352, max_digits=14, verbose_name='VAT Amount'),
preserve_default=False,
),
migrations.AlterField(
model_name='salequotation',
name='created_at',
field=models.DateTimeField(auto_now_add=True, verbose_name='Created At'),
),
migrations.AlterField(
model_name='salequotation',
name='customer',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='quotations', to='inventory.customer', verbose_name='Customer'),
),
migrations.AlterField(
model_name='salequotation',
name='remarks',
field=models.TextField(blank=True, null=True, verbose_name='Remarks'),
),
migrations.AlterField(
model_name='salequotationcar',
name='car',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.car', verbose_name='Car'),
),
migrations.AlterField(
model_name='salequotationcar',
name='quotation',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='quotation_cars', to='inventory.salequotation', verbose_name='Quotation'),
),
migrations.CreateModel(
name='SalesOrder',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')),
('total_amount', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Total Amount')),
('quotation', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='sales_order', to='inventory.salequotation', verbose_name='Quotation')),
],
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 5.1.4 on 2024-12-08 08:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0002_remove_interiorcolors_car_carcolors_and_more'),
]
operations = [
migrations.AlterField(
model_name='customcard',
name='custom_date',
field=models.DateField(verbose_name='Custom Date'),
),
]

View File

@ -0,0 +1,37 @@
# Generated by Django 5.1.4 on 2024-12-09 21:06
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0002_remove_salequotationcar_financial_details_and_more'),
]
operations = [
migrations.RemoveField(
model_name='salequotationcar',
name='administration_fee',
),
migrations.RemoveField(
model_name='salequotationcar',
name='custom_card_fee',
),
migrations.RemoveField(
model_name='salequotationcar',
name='selling_price',
),
migrations.RemoveField(
model_name='salequotationcar',
name='total_amount',
),
migrations.RemoveField(
model_name='salequotationcar',
name='transportation_fee',
),
migrations.RemoveField(
model_name='salequotationcar',
name='vat_amount',
),
]

View File

@ -0,0 +1,37 @@
# Generated by Django 5.1.4 on 2024-12-09 21:59
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0003_remove_salequotationcar_administration_fee_and_more'),
]
operations = [
migrations.RemoveField(
model_name='carfinance',
name='administration_vat_amount',
),
migrations.RemoveField(
model_name='carfinance',
name='custom_card_vat_amount',
),
migrations.RemoveField(
model_name='carfinance',
name='selling_vat_amount',
),
migrations.RemoveField(
model_name='carfinance',
name='total_before_vat',
),
migrations.RemoveField(
model_name='carfinance',
name='total_vat_amount',
),
migrations.RemoveField(
model_name='carfinance',
name='transportation_vat_amount',
),
]

View File

@ -14,10 +14,12 @@ from django_ledger.models import (
UnitOfMeasureModel, UnitOfMeasureModel,
CustomerModel, CustomerModel,
ItemModelQuerySet, ItemModelQuerySet,
) )
from decimal import Decimal, InvalidOperation
from django.core.exceptions import ValidationError
from phonenumber_field.modelfields import PhoneNumberField from phonenumber_field.modelfields import PhoneNumberField
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from decimal import Decimal
from django.utils.timezone import now from django.utils.timezone import now
from .mixins import LocalizedNameMixin from .mixins import LocalizedNameMixin
@ -26,7 +28,7 @@ class CarMake(models.Model, LocalizedNameMixin):
id_car_make = models.AutoField(primary_key=True) id_car_make = models.AutoField(primary_key=True)
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
arabic_name = models.CharField(max_length=255) arabic_name = models.CharField(max_length=255)
logo = models.ImageField(_("logo"), upload_to="car_make", blank=True, null=True) logo = models.ImageField(_('logo'), upload_to='car_make', blank=True, null=True)
is_sa_import = models.BooleanField(default=False) is_sa_import = models.BooleanField(default=False)
def __str__(self): def __str__(self):
@ -38,7 +40,7 @@ class CarMake(models.Model, LocalizedNameMixin):
class CarModel(models.Model, LocalizedNameMixin): class CarModel(models.Model, LocalizedNameMixin):
id_car_model = models.AutoField(primary_key=True) id_car_model = models.AutoField(primary_key=True)
id_car_make = models.ForeignKey(CarMake, models.DO_NOTHING, db_column="id_car_make") id_car_make = models.ForeignKey(CarMake, models.DO_NOTHING, db_column='id_car_make')
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
arabic_name = models.CharField(max_length=255) arabic_name = models.CharField(max_length=255)
@ -51,9 +53,7 @@ class CarModel(models.Model, LocalizedNameMixin):
class CarSerie(models.Model, LocalizedNameMixin): class CarSerie(models.Model, LocalizedNameMixin):
id_car_serie = models.AutoField(primary_key=True) id_car_serie = models.AutoField(primary_key=True)
id_car_model = models.ForeignKey( id_car_model = models.ForeignKey(CarModel, models.DO_NOTHING, db_column='id_car_model')
CarModel, models.DO_NOTHING, db_column="id_car_model"
)
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
arabic_name = models.CharField(max_length=255) arabic_name = models.CharField(max_length=255)
@ -66,9 +66,7 @@ class CarSerie(models.Model, LocalizedNameMixin):
class CarTrim(models.Model, LocalizedNameMixin): class CarTrim(models.Model, LocalizedNameMixin):
id_car_trim = models.AutoField(primary_key=True) id_car_trim = models.AutoField(primary_key=True)
id_car_serie = models.ForeignKey( id_car_serie = models.ForeignKey(CarSerie, models.DO_NOTHING, db_column='id_car_serie')
CarSerie, models.DO_NOTHING, db_column="id_car_serie"
)
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
arabic_name = models.CharField(max_length=255) arabic_name = models.CharField(max_length=255)
start_production_year = models.IntegerField(blank=True, null=True) start_production_year = models.IntegerField(blank=True, null=True)
@ -85,9 +83,7 @@ class CarSpecification(models.Model, LocalizedNameMixin):
id_car_specification = models.AutoField(primary_key=True) id_car_specification = models.AutoField(primary_key=True)
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
arabic_name = models.CharField(max_length=255) arabic_name = models.CharField(max_length=255)
id_parent = models.ForeignKey( id_parent = models.ForeignKey('self', models.DO_NOTHING, db_column='id_parent', blank=True, null=True)
"self", models.DO_NOTHING, db_column="id_parent", blank=True, null=True
)
def __str__(self): def __str__(self):
return self.name return self.name
@ -98,10 +94,8 @@ class CarSpecification(models.Model, LocalizedNameMixin):
class CarSpecificationValue(models.Model): class CarSpecificationValue(models.Model):
id_car_specification_value = models.AutoField(primary_key=True) id_car_specification_value = models.AutoField(primary_key=True)
id_car_trim = models.ForeignKey(CarTrim, models.DO_NOTHING, db_column="id_car_trim") id_car_trim = models.ForeignKey(CarTrim, models.DO_NOTHING, db_column='id_car_trim')
id_car_specification = models.ForeignKey( id_car_specification = models.ForeignKey(CarSpecification, models.DO_NOTHING, db_column='id_car_specification')
CarSpecification, models.DO_NOTHING, db_column="id_car_specification"
)
value = models.CharField(max_length=500) value = models.CharField(max_length=500)
unit = models.CharField(max_length=255, blank=True, null=True) unit = models.CharField(max_length=255, blank=True, null=True)
@ -114,21 +108,24 @@ class CarSpecificationValue(models.Model):
# Car Model # Car Model
class CarStatusChoices(models.TextChoices): class CarStatusChoices(models.TextChoices):
AVAILABLE = "available", _("Available") AVAILABLE = 'available', _('Available')
SOLD = "sold", _("Sold") SOLD = 'sold', _('Sold')
HOLD = "hold", _("Hold") HOLD = 'hold', _('Hold')
DAMAGED = "damaged", _("Damaged") DAMAGED = 'damaged', _('Damaged')
class CarStockTypeChoices(models.TextChoices): class CarStockTypeChoices(models.TextChoices):
NEW = "new", _("New") NEW = 'new', _('New')
USED = "used", _("Used") USED = 'used', _('Used')
class Car(models.Model): class Car(models.Model):
vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN")) vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN"))
dealer = models.ForeignKey( dealer = models.ForeignKey(
"Dealer", models.DO_NOTHING, related_name="cars", verbose_name=_("Dealer") "Dealer",
models.DO_NOTHING,
related_name='cars',
verbose_name=_("Dealer")
) )
vendor = models.ForeignKey( vendor = models.ForeignKey(
@ -136,53 +133,53 @@ class Car(models.Model):
models.DO_NOTHING, models.DO_NOTHING,
null=True, null=True,
blank=True, blank=True,
related_name="cars", related_name='cars',
verbose_name=_("Vendor"), verbose_name=_("Vendor")
) )
id_car_make = models.ForeignKey( id_car_make = models.ForeignKey(
CarMake, CarMake,
models.DO_NOTHING, models.DO_NOTHING,
db_column="id_car_make", db_column='id_car_make',
null=True, null=True,
blank=True, blank=True,
verbose_name=_("Make"), verbose_name=_("Make")
) )
id_car_model = models.ForeignKey( id_car_model = models.ForeignKey(
CarModel, CarModel,
models.DO_NOTHING, models.DO_NOTHING,
db_column="id_car_model", db_column='id_car_model',
null=True, null=True,
blank=True, blank=True,
verbose_name=_("Model"), verbose_name=_("Model")
) )
year = models.IntegerField(verbose_name=_("Year")) year = models.IntegerField(verbose_name=_("Year"))
id_car_serie = models.ForeignKey( id_car_serie = models.ForeignKey(
CarSerie, CarSerie,
models.DO_NOTHING, models.DO_NOTHING,
db_column="id_car_serie", db_column='id_car_serie',
null=True, null=True,
blank=True, blank=True,
verbose_name=_("Series"), verbose_name=_("Series")
) )
id_car_trim = models.ForeignKey( id_car_trim = models.ForeignKey(
CarTrim, CarTrim,
models.DO_NOTHING, models.DO_NOTHING,
db_column="id_car_trim", db_column='id_car_trim',
null=True, null=True,
blank=True, blank=True,
verbose_name=_("Trim"), verbose_name=_("Trim")
) )
status = models.CharField( status = models.CharField(
max_length=10, max_length=10,
choices=CarStatusChoices.choices, choices=CarStatusChoices.choices,
default=CarStatusChoices.AVAILABLE, default=CarStatusChoices.AVAILABLE,
verbose_name=_("Status"), verbose_name=_("Status")
) )
stock_type = models.CharField( stock_type = models.CharField(
max_length=10, max_length=10,
choices=CarStockTypeChoices.choices, choices=CarStockTypeChoices.choices,
default=CarStockTypeChoices.NEW, default=CarStockTypeChoices.NEW,
verbose_name=_("Stock Type"), verbose_name=_("Stock Type")
) )
remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks")) remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks"))
mileage = models.IntegerField(blank=True, null=True, verbose_name=_("Mileage")) mileage = models.IntegerField(blank=True, null=True, verbose_name=_("Mileage"))
@ -205,23 +202,21 @@ class Car(models.Model):
@property @property
def selling_price(self): def selling_price(self):
finance = self.finances.first() finance = self.finances.first()
return finance.selling_price if finance else Decimal("0.00") return finance.selling_price if finance else Decimal('0.00')
@property @property
def vat_amount(self): def vat_amount(self):
finance = self.finances.first() finance = self.finances.first()
return finance.vat_amount if finance else Decimal("0.00") return finance.vat_amount if finance else Decimal('0.00')
@property @property
def total(self): def total(self):
finance = self.finances.first() finance = self.finances.first()
return finance.total if finance else Decimal("0.00") return finance.total if finance else Decimal('0.00')
class CarReservation(models.Model): class CarReservation(models.Model):
car = models.ForeignKey( car = models.ForeignKey('Car', on_delete=models.CASCADE, related_name='reservations')
"Car", on_delete=models.CASCADE, related_name="reservations"
)
reserved_by = models.ForeignKey(User, on_delete=models.CASCADE) reserved_by = models.ForeignKey(User, on_delete=models.CASCADE)
reserved_at = models.DateTimeField(auto_now_add=True) reserved_at = models.DateTimeField(auto_now_add=True)
reserved_until = models.DateTimeField() reserved_until = models.DateTimeField()
@ -230,81 +225,98 @@ class CarReservation(models.Model):
return self.reserved_until > now() return self.reserved_until > now()
class Meta: class Meta:
unique_together = ("car", "reserved_until") unique_together = ('car', 'reserved_until')
ordering = ["-reserved_at"] ordering = ['-reserved_at']
# Car Finance Model # Car Finance Model
class CarFinance(models.Model): class CarFinance(models.Model):
car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name="finances") car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name='finances')
cost_price = models.DecimalField( cost_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Cost Price"))
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,
profit_margin = models.DecimalField( decimal_places=2,
max_digits=10, decimal_places=2, verbose_name=_("Profit Margin") verbose_name=_("Profit Margin"),
) editable=False)
selling_price = models.DecimalField( discount_amount = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Discount Amount"),
max_digits=14, decimal_places=2, verbose_name=_("Selling Price"), editable=False default=Decimal('0.00'))
) registration_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Registration Fee"),
vat_rate = models.DecimalField( default=Decimal('0.00'))
max_digits=10, decimal_places=2, default=0.15, verbose_name=_("VAT Rate") administration_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Administration Fee"),
) default=Decimal('0.00'))
vat_amount = models.DecimalField( transportation_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Transportation Fee"),
max_digits=12, decimal_places=2, verbose_name=_("VAT Amount"), editable=False default=Decimal('0.00'))
) custom_card_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Custom Card Fee"),
total = models.DecimalField( default=Decimal('0.00'))
max_digits=14, decimal_places=2, verbose_name=_("Total Amount"), editable=False vat_rate = models.DecimalField(max_digits=14, decimal_places=2, default=Decimal('0.15'), verbose_name=_("VAT Rate"),)
)
class Meta: class Meta:
verbose_name = _("Car Financial Details") verbose_name = _("Car Financial Details")
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
self.full_clean() self.full_clean()
self.selling_price = self.cost_price * (1 + self.profit_margin) try:
self.vat_amount = self.selling_price * self.vat_rate self.profit_margin = self.selling_price - self.cost_price - self.discount_amount
self.total = self.selling_price + self.vat_amount 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.total = price_after_discount + services + total_vat_amount + self.registration_fee
except InvalidOperation as e:
raise ValidationError(_("Invalid decimal operation: %s") % str(e))
super().save(*args, **kwargs) super().save(*args, **kwargs)
@property
def total_vat_amount(self):
return self.total if self.total else Decimal('0.00')
class ExteriorColors(models.Model, LocalizedNameMixin):
name = models.CharField(max_length=255, verbose_name=_("Name"))
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
rgb = models.CharField(max_length=24, blank=True, null=True, verbose_name=_("RGB"))
class Meta:
verbose_name = _("Exterior Colors")
verbose_name_plural = _("Exterior Colors")
def __str__(self): def __str__(self):
return ( return f"{self.name} ({self.rgb})"
f"Car Financial Details for {self.car}: Selling Price {self.selling_price}"
)
class InteriorColors(models.Model, LocalizedNameMixin):
name = models.CharField(max_length=255, verbose_name=_("Name"))
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
rgb = models.CharField(max_length=24, blank=True, null=True, verbose_name=_("RGB"))
class Meta:
verbose_name = _("Interior Colors")
verbose_name_plural = _("Interior Colors")
def __str__(self):
return f"{self.name} ({self.rgb})"
# Colors Model # Colors Model
class CarColors(models.Model): class CarColors(models.Model):
class ColorType(models.TextChoices): car = models.ForeignKey('Car', on_delete=models.CASCADE, related_name='colors')
EXTERIOR = "exterior", _("Exterior") exterior = models.ForeignKey('ExteriorColors', on_delete=models.CASCADE, related_name='colors')
INTERIOR = "interior", _("Interior") interior = models.ForeignKey('InteriorColors', on_delete=models.CASCADE, related_name='colors')
car = models.ForeignKey("Car", on_delete=models.CASCADE, related_name="colors")
name = models.CharField(max_length=255, verbose_name=_("Name"))
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
rgb = models.CharField(max_length=24, blank=True, null=True, verbose_name=_("RGB"))
color_type = models.CharField(
max_length=10,
choices=ColorType.choices,
default=ColorType.EXTERIOR,
verbose_name=_("Color Type"),
)
class Meta: class Meta:
verbose_name = _("Color") verbose_name = _("Color")
verbose_name_plural = _("Colors") verbose_name_plural = _("Colors")
unique_together = ('car', 'exterior', 'interior')
def __str__(self): def __str__(self):
return f"{self.get_color_type_display()} - {self.name} ({self.rgb})" return f"{self.car} ({self.exterior.name}) ({self.interior.name})"
# Custom Card Model # Custom Card Model
class CustomCard(models.Model): class CustomCard(models.Model):
car = models.ForeignKey( car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name='custom_cards', verbose_name=_("Car"))
Car,
on_delete=models.CASCADE,
related_name="custom_cards",
verbose_name=_("Car"),
)
custom_number = models.CharField(max_length=255, verbose_name=_("Custom Number")) custom_number = models.CharField(max_length=255, verbose_name=_("Custom Number"))
custom_date = models.DateField(verbose_name=_("Custom Date")) custom_date = models.DateField(verbose_name=_("Custom Date"))
@ -318,12 +330,7 @@ class CustomCard(models.Model):
# Car Registration Model # Car Registration Model
class CarRegistration(models.Model): class CarRegistration(models.Model):
car = models.ForeignKey( car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name='registrations', verbose_name=_("Car"))
Car,
on_delete=models.CASCADE,
related_name="registrations",
verbose_name=_("Car"),
)
plate_number = models.IntegerField(verbose_name=_("Plate Number")) plate_number = models.IntegerField(verbose_name=_("Plate Number"))
text1 = models.CharField(max_length=1, verbose_name=_("Text 1")) text1 = models.CharField(max_length=1, verbose_name=_("Text 1"))
text2 = models.CharField(max_length=1, verbose_name=_("Text 2")) text2 = models.CharField(max_length=1, verbose_name=_("Text 2"))
@ -349,20 +356,14 @@ class TimestampedModel(models.Model):
# Dealer Model # Dealer Model
class Dealer(models.Model, LocalizedNameMixin): class Dealer(models.Model, LocalizedNameMixin):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="dealer") user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='dealer')
crn = models.CharField( crn = models.CharField(max_length=10, verbose_name=_("Commercial Registration Number"))
max_length=10, verbose_name=_("Commercial Registration Number")
)
vrn = models.CharField(max_length=15, verbose_name=_("VAT Registration Number")) vrn = models.CharField(max_length=15, verbose_name=_("VAT Registration Number"))
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name")) arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
name = models.CharField(max_length=255, verbose_name=_("English Name")) name = models.CharField(max_length=255, verbose_name=_("English Name"))
phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number")) phone_number = PhoneNumberField(region='SA', verbose_name=_("Phone Number"))
address = models.CharField( address = models.CharField(max_length=200, blank=True, null=True, verbose_name=_("Address"))
max_length=200, blank=True, null=True, verbose_name=_("Address") logo = models.ImageField(upload_to="logos/users", blank=True, null=True, verbose_name=_("Logo"))
)
logo = models.ImageField(
upload_to="logos/users", blank=True, null=True, verbose_name=_("Logo")
)
class Meta: class Meta:
verbose_name = _("Dealer") verbose_name = _("Dealer")
@ -374,20 +375,15 @@ class Dealer(models.Model, LocalizedNameMixin):
# Vendor Model # Vendor Model
class Vendor(models.Model, LocalizedNameMixin): class Vendor(models.Model, LocalizedNameMixin):
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="vendors") dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name='vendors')
crn = models.CharField( crn = models.CharField(max_length=10, unique=True, verbose_name=_("Commercial Registration Number"))
max_length=10, unique=True, verbose_name=_("Commercial Registration Number") vrn = models.CharField(max_length=15, unique=True, verbose_name=_("VAT Registration Number"))
)
vrn = models.CharField(
max_length=15, unique=True, verbose_name=_("VAT Registration Number")
)
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name")) arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
name = models.CharField(max_length=255, verbose_name=_("English Name")) name = models.CharField(max_length=255, verbose_name=_("English Name"))
contact_person = models.CharField(max_length=100, verbose_name=_("Contact Person")) contact_person = models.CharField(max_length=100, verbose_name=_("Contact Person"))
phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number")) phone_number = PhoneNumberField(region='SA', verbose_name=_("Phone Number"))
address = models.CharField( address = models.CharField(max_length=200, blank=True, null=True, verbose_name=_("Address"))
max_length=200, blank=True, null=True, verbose_name=_("Address") logo = models.ImageField(upload_to="logos/vendors", blank=True, null=True, verbose_name=_("Logo"))
)
class Meta: class Meta:
verbose_name = _("Vendor") verbose_name = _("Vendor")
@ -399,24 +395,14 @@ class Vendor(models.Model, LocalizedNameMixin):
# Customer Model # Customer Model
class Customer(models.Model): class Customer(models.Model):
dealer = models.ForeignKey( dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name='customers')
Dealer, on_delete=models.CASCADE, related_name="customers"
)
first_name = models.CharField(max_length=50, verbose_name=_("First Name")) first_name = models.CharField(max_length=50, verbose_name=_("First Name"))
middle_name = models.CharField( middle_name = models.CharField(max_length=50, blank=True, null=True, verbose_name=_("Middle Name"))
max_length=50, blank=True, null=True, verbose_name=_("Middle Name")
)
last_name = models.CharField(max_length=50, verbose_name=_("Last Name")) last_name = models.CharField(max_length=50, verbose_name=_("Last Name"))
email = models.EmailField(unique=True, verbose_name=_("Email")) email = models.EmailField(unique=True, verbose_name=_("Email"))
national_id = models.CharField( national_id = models.CharField(max_length=10, unique=True, verbose_name=_("National ID"))
max_length=10, unique=True, verbose_name=_("National ID") phone_number = PhoneNumberField(region='SA', unique=True, verbose_name=_("Phone Number"))
) address = models.CharField(max_length=200, blank=True, null=True, verbose_name=_("Address"))
phone_number = PhoneNumberField(
region="SA", unique=True, verbose_name=_("Phone Number")
)
address = models.CharField(
max_length=200, blank=True, null=True, verbose_name=_("Address")
)
created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created")) created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created"))
class Meta: class Meta:
@ -424,9 +410,84 @@ class Customer(models.Model):
verbose_name_plural = _("Customers") verbose_name_plural = _("Customers")
def __str__(self): def __str__(self):
middle = f" {self.middle_name}" if self.middle_name else "" middle = f" {self.middle_name}" if self.middle_name else ''
return f"{self.first_name}{middle} {self.last_name}" return f"{self.first_name}{middle} {self.last_name}"
class SaleQuotation(models.Model):
STATUS_CHOICES = [
("DRAFT", _("Draft")),
("CONFIRMED", _("Confirmed")),
("CANCELED", _("Canceled")),
]
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name="quotations", verbose_name=_("Customer"))
remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks"))
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default="DRAFT", verbose_name=_("Status"))
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At"))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At"))
def confirm(self):
"""Confirm the quotation and lock financial details."""
if self.status != "DRAFT":
raise ValueError(_("Only draft quotations can be confirmed."))
self.status = "CONFIRMED"
self.save()
def cancel(self):
"""Cancel the quotation."""
if self.status == "CONFIRMED":
raise ValueError(_("Cannot cancel a confirmed quotation."))
self.status = "CANCELED"
self.save()
def __str__(self):
return f"Quotation #{self.id} for {self.customer}"
class SaleQuotationCar(models.Model):
quotation = models.ForeignKey(
SaleQuotation,
on_delete=models.CASCADE,
related_name="quotation_cars",
verbose_name=_("Quotation")
)
car = models.ForeignKey(
Car,
on_delete=models.CASCADE,
verbose_name=_("Car")
)
def get_financial_details(self):
"""Retrieve financial details dynamically from CarFinance."""
car_finance = self.car.finances.first()
if not car_finance:
return None
return {
"selling_price": car_finance.selling_price,
"administration_fee": car_finance.administration_fee,
"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,
}
def __str__(self):
return f"{self.car} - Quotation #{self.quotation.id}"
class SalesOrder(models.Model):
quotation = models.OneToOneField(SaleQuotation, on_delete=models.CASCADE, related_name="sales_order", verbose_name=_("Quotation"))
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At"))
total_amount = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Total Amount"))
def __str__(self):
return f"Sales Order #{self.id} from Quotation #{self.quotation.id}"
# Create Entity # Create Entity
@receiver(post_save, sender=Dealer) @receiver(post_save, sender=Dealer)
def create_ledger_entity(sender, instance, created, **kwargs): def create_ledger_entity(sender, instance, created, **kwargs):

View File

@ -25,6 +25,7 @@ def decode_vin_pyvin(vin):
print(data) print(data)
return data return data
# vehicle-info # vehicle-info
# c2729afb # c2729afb
# 6d397471920412d672af1b8a02ca52ea # 6d397471920412d672af1b8a02ca52ea
@ -44,197 +45,10 @@ def elm(vin):
payload = {} payload = {}
response = requests.request("GET", url, headers=headers, data=payload) response = requests.request("GET", url, headers=headers, data=payload)
car_info = json.loads(response.text) car_info = json.loads(response.text)
print(car_info)
return car_info return car_info
def get_unique_colors(api_response):
print(api_response)
colors = api_response.get("data", [])
print(colors)
unique_colors = {}
for color in colors:
color_name = color.get("name")
rgb = color.get("rgb")
if color_name not in unique_colors:
unique_colors[color_name] = rgb
return [{"name": name, "rgb": rgb} for name, rgb in unique_colors.items()]
def fetch_colors(car_data):
car_colors = {
"data": [
{"rgb": "192, 192, 192", "name": "Silver Metallic"},
{"rgb": "0, 0, 0", "name": "Jet Black"},
{"rgb": "255, 255, 255", "name": "Bright White"},
{"rgb": "128, 128, 128", "name": "Graphite Gray"},
{"rgb": "80, 80, 80", "name": "Gunmetal Gray"},
{"rgb": "255, 0, 0", "name": "Racing Red"},
{"rgb": "255, 69, 0", "name": "Inferno Orange"},
{"rgb": "0, 0, 255", "name": "Deep Blue Pearl"},
{"rgb": "75, 0, 130", "name": "Indigo Night"},
{"rgb": "255, 215, 0", "name": "Solar Gold"},
{"rgb": "34, 139, 34", "name": "Emerald Green"},
{"rgb": "60, 179, 113", "name": "Forest Mist Green"},
{"rgb": "255, 140, 0", "name": "Burnt Amber"},
{"rgb": "160, 82, 45", "name": "Copper Brown"},
{"rgb": "128, 0, 0", "name": "Crimson Maroon"},
{"rgb": "245, 245, 220", "name": "Beige Champagne"},
{"rgb": "169, 169, 169", "name": "Shadow Gray"},
{"rgb": "255, 250, 205", "name": "Lemon Pearl"},
{"rgb": "220, 220, 220", "name": "Platinum Silver"},
{"rgb": "105, 105, 105", "name": "Charcoal Metallic"},
{"rgb": "128, 0, 128", "name": "Royal Purple"},
{"rgb": "210, 105, 30", "name": "Sunset Bronze"},
{"rgb": "0, 128, 128", "name": "Teal Lagoon"},
{"rgb": "72, 61, 139", "name": "Midnight Blue"},
{"rgb": "255, 20, 147", "name": "Blazing Pink"},
{"rgb": "192, 57, 43", "name": "Crimson Flame"},
{"rgb": "255, 228, 196", "name": "Cream Sand"},
{"rgb": "112, 128, 144", "name": "Steel Gray"},
{"rgb": "0, 100, 0", "name": "Hunter Green"},
{"rgb": "255, 223, 0", "name": "Bright Yellow"},
{"rgb": "85, 107, 47", "name": "Olive Metallic"},
{"rgb": "128, 128, 0", "name": "Mustard Gold"},
{"rgb": "139, 69, 19", "name": "Cocoa Brown"},
{"rgb": "255, 165, 0", "name": "Tangerine Flame"},
{"rgb": "0, 0, 139", "name": "Navy Sapphire"},
{"rgb": "70, 130, 180", "name": "Skyline Blue"},
{"rgb": "220, 20, 60", "name": "Crimson Passion"},
{"rgb": "189, 183, 107", "name": "Khaki Dune"},
{"rgb": "50, 205, 50", "name": "Lime Essence"},
{"rgb": "139, 0, 139", "name": "Amethyst Glow"},
{"rgb": "255, 215, 180", "name": "Rosé Gold"},
{"rgb": "46, 139, 87", "name": "Moss Green"},
{"rgb": "72, 209, 204", "name": "Caribbean Aqua"},
{"rgb": "255, 240, 245", "name": "Pearl Blush"},
{"rgb": "244, 164, 96", "name": "Sierra Sunset"},
{"rgb": "139, 0, 0", "name": "Crimson Ruby"},
{"rgb": "192, 192, 192", "name": "Chrome"},
{"rgb": "255, 105, 180", "name": "Hot Magenta"},
{"rgb": "0, 255, 255", "name": "Ice Blue"},
{"rgb": "184, 134, 11", "name": "Golden Bronze"},
{"rgb": "128, 128, 64", "name": "Bronze Olive"},
{"rgb": "245, 222, 179", "name": "Wheat Cream"}
]
}
jwt_token = get_jwt_token()
if not jwt_token:
print("Failed to retrieve JWT token.")
return None
year = car_data['year']
make = car_data['make']
model = car_data['model']
url = "https://carapi.app/api/exterior-colors?year={}&make={}&model={}".format(year, make, model)
params = {
'limit': '1000',
'sort': 'name',
'direction': 'asc',
'verbose': 'no',
'all_trims': 'no',
}
headers = {
'Accept': 'application/json',
'Authorization': f'Bearer {jwt_token}',
}
try:
response = requests.get(url, headers=headers, params=params)
color_info = response.json()
if not color_info["data"] == []:
return get_unique_colors(color_info)
else:
return car_colors["data"]
except requests.exceptions.RequestException as e:
print(f"Error fetching color information: {e}")
return None
def fetch_interior_colors(car_data):
car_colors = {
"data": [
{"rgb": "0, 0, 0", "name": "Jet Black"},
{"rgb": "54, 69, 79", "name": "Charcoal Black"},
{"rgb": "255, 255, 255", "name": "Bright White"},
{"rgb": "245, 245, 220", "name": "Off-White"},
{"rgb": "210, 180, 140", "name": "Beige"},
{"rgb": "205, 133, 63", "name": "Tan"},
{"rgb": "128, 128, 128", "name": "Gray"},
{"rgb": "80, 80, 80", "name": "Graphite Gray"},
{"rgb": "112, 128, 144", "name": "Gunmetal Gray"},
{"rgb": "192, 192, 192", "name": "Silver Metallic"},
{"rgb": "139, 69, 19", "name": "Cognac Brown"},
{"rgb": "149, 94, 55", "name": "Chestnut Brown"},
{"rgb": "97, 63, 43", "name": "Espresso Brown"},
{"rgb": "72, 40, 34", "name": "Dark Chocolate"},
{"rgb": "139, 69, 19", "name": "Saddle Brown"},
{"rgb": "124, 79, 58", "name": "Mocha"},
{"rgb": "193, 154, 107", "name": "Camel Tan"},
{"rgb": "128, 0, 32", "name": "Burgundy"},
{"rgb": "128, 0, 0", "name": "Maroon"},
{"rgb": "139, 0, 0", "name": "Deep Red"},
{"rgb": "0, 0, 128", "name": "Navy Blue"},
{"rgb": "65, 105, 225", "name": "Royal Blue"},
{"rgb": "34, 139, 34", "name": "Forest Green"},
{"rgb": "80, 200, 120", "name": "Emerald Green"},
{"rgb": "255, 255, 240", "name": "Ivory"},
{"rgb": "242, 242, 242", "name": "Pearl White"},
{"rgb": "169, 169, 169", "name": "Stone Gray"},
{"rgb": "112, 128, 144", "name": "Slate Gray"},
{"rgb": "150, 111, 51", "name": "Ash Brown"},
{"rgb": "128, 128, 0", "name": "Olive Green"},
{"rgb": "25, 25, 112", "name": "Midnight Blue"},
{"rgb": "72, 60, 50", "name": "Taupe"}
]
}
jwt_token = get_jwt_token()
if not jwt_token:
print("Failed to retrieve JWT token.")
return None
year = car_data['year']
make = car_data['make']
model = car_data['model']
url = "https://carapi.app/api/interior-colors?year={}&make={}&model={}".format(year, make, model)
params = {
'limit': '100',
'sort': 'name',
'direction': 'asc',
'verbose': 'no',
'all_trims': 'no',
}
headers = {
'Accept': 'application/json',
'Authorization': f'Bearer {jwt_token}',
}
try:
response = requests.get(url, headers=headers, params=params)
color_info = response.json()
if not color_info["data"] == []:
return get_unique_colors(color_info)
else:
return car_colors["data"]
except requests.exceptions.RequestException as e:
print(f"Error fetching color information: {e}")
return None
def translate(content, *args, **kwargs): def translate(content, *args, **kwargs):
client = OpenAI(api_key=settings.OPENAI_API_KEY) client = OpenAI(api_key=settings.OPENAI_API_KEY)
completion = client.chat.completions.create( completion = client.chat.completions.create(

View File

@ -55,11 +55,20 @@ urlpatterns = [
path('cars/finance/update/<int:pk>/', views.CarFinanceUpdateView.as_view(), name='car_finance_update'), path('cars/finance/update/<int:pk>/', views.CarFinanceUpdateView.as_view(), name='car_finance_update'),
path('cars/add/', views.CarCreateView.as_view(), name='car_add'), path('cars/add/', views.CarCreateView.as_view(), name='car_add'),
path('ajax/', views.AjaxHandlerView.as_view(), name='ajax_handler'), path('ajax/', views.AjaxHandlerView.as_view(), name='ajax_handler'),
path('cars/<int:car_pk>/add-color/', views.CarColorCreateView.as_view(), name='add_color'), path('cars/<int:car_pk>/add-color/', views.CarColorCreate.as_view(), name='add_color'),
path('cars/<int:car_pk>/colors/<int:pk>/update/', # path('cars/<int:car_pk>/colors/<int:pk>/update/',views.CarColorUpdateView.as_view(),name='color_update'),
views.CarColorUpdateView.as_view(),
name='color_update'),
path('cars/reserve/<int:car_id>/', views.reserve_car_view, name='reserve_car'), path('cars/reserve/<int:car_id>/', views.reserve_car_view, name='reserve_car'),
path('reservations/<int:reservation_id>/', views.manage_reservation, name='reservations'), 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'), path('cars/<int:car_pk>/add-custom-card/', views.CustomCardCreateView.as_view(), name='add_custom_card'),
]
# Sales URLs
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'),
path('sales/quotations/<int:pk>/confirm/', views.confirm_quotation, name='confirm_quotation'),
path('sales/orders/detail/<int:order_id>/', views.SalesOrderDetailView.as_view(), name='order_detail'),
]

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-12-08 16:18+0300\n" "POT-Creation-Date: 2024-12-09 09:40+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -19,9 +19,9 @@ msgstr ""
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" "&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
#: api/models.py:6 inventory/models.py:122 #: api/models.py:6 inventory/models.py:123
#: templates/inventory/car_detail.html:61 templates/inventory/car_form.html:83 #: templates/inventory/car_detail.html:61 templates/inventory/car_form.html:83
#: templates/inventory/car_inventory.html:40 #: templates/inventory/car_inventory.html:53
#: templates/inventory/car_list.html:67 templates/inventory/car_list.html:69 #: templates/inventory/car_list.html:67 templates/inventory/car_list.html:69
msgid "VIN" msgid "VIN"
msgstr "رقم الهيكل" msgstr "رقم الهيكل"
@ -34,47 +34,51 @@ msgstr "الإنجليزية"
msgid "Arabic" msgid "Arabic"
msgstr "العربية" msgstr "العربية"
#: inventory/forms.py:147 inventory/models.py:284 #: inventory/forms.py:102 inventory/models.py:357
#: templates/inventory/car_detail.html:135 #: templates/inventory/car_detail.html:135
msgid "Custom Date" msgid "Custom Date"
msgstr "تاريخ البطاقة الجمركية" msgstr "تاريخ البطاقة الجمركية"
#: inventory/models.py:30 templates/vendors/vendors_list.html:44 #: inventory/forms.py:151
msgid "Both exterior and interior colors must be selected."
msgstr "يجب اختيار اللونين الخارجي والداخلي."
#: inventory/models.py:31 templates/vendors/vendors_list.html:44
msgid "logo" msgid "logo"
msgstr "الشعار" msgstr "الشعار"
#: inventory/models.py:110 #: inventory/models.py:111
#: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/invoice/tags/invoice_item_formset.html:21 #: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/invoice/tags/invoice_item_formset.html:21
msgid "Available" msgid "Available"
msgstr "متاح" msgstr "متاح"
#: inventory/models.py:111 #: inventory/models.py:112
msgid "Sold" msgid "Sold"
msgstr "تم البيع" msgstr "تم البيع"
#: inventory/models.py:112 #: inventory/models.py:113
msgid "Hold" msgid "Hold"
msgstr "في الانتظار" msgstr "في الانتظار"
#: inventory/models.py:113 #: inventory/models.py:114
msgid "Damaged" msgid "Damaged"
msgstr "تالف" msgstr "تالف"
#: inventory/models.py:117 #: inventory/models.py:118
msgid "New" msgid "New"
msgstr "جديد" msgstr "جديد"
#: inventory/models.py:118 #: inventory/models.py:119
msgid "Used" msgid "Used"
msgstr "مستعمل" msgstr "مستعمل"
#: inventory/models.py:127 inventory/models.py:332 #: inventory/models.py:128 inventory/models.py:405
msgid "Dealer" msgid "Dealer"
msgstr "المعرض" msgstr "المعرض"
#: inventory/models.py:136 inventory/models.py:351 #: inventory/models.py:137 inventory/models.py:424
#: templates/inventory/car_detail.html:108 #: templates/inventory/car_detail.html:108
#: templates/inventory/car_form.html:225 #: templates/inventory/car_form.html:230
#: venv/lib/python3.11/site-packages/django_ledger/models/bill.py:359 #: venv/lib/python3.11/site-packages/django_ledger/models/bill.py:359
#: venv/lib/python3.11/site-packages/django_ledger/models/vendor.py:191 #: venv/lib/python3.11/site-packages/django_ledger/models/vendor.py:191
#: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/bills/tags/bill_table.html:12 #: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/bills/tags/bill_table.html:12
@ -82,28 +86,28 @@ msgstr "المعرض"
msgid "Vendor" msgid "Vendor"
msgstr "المورد" msgstr "المورد"
#: inventory/models.py:144 templates/inventory/car_inventory.html:42 #: inventory/models.py:145 templates/inventory/car_inventory.html:55
msgid "Make" msgid "Make"
msgstr "الصانع" msgstr "الصانع"
#: inventory/models.py:152 templates/inventory/car_inventory.html:43 #: inventory/models.py:153 templates/inventory/car_inventory.html:56
msgid "Model" msgid "Model"
msgstr "الموديل" msgstr "الموديل"
#: inventory/models.py:154 templates/inventory/car_form.html:117 #: inventory/models.py:155 templates/inventory/car_form.html:118
#: templates/inventory/car_inventory.html:41 #: templates/inventory/car_inventory.html:54
msgid "Year" msgid "Year"
msgstr "السنة" msgstr "السنة"
#: inventory/models.py:161 templates/inventory/car_form.html:177 #: inventory/models.py:162 templates/inventory/car_form.html:182
msgid "Series" msgid "Series"
msgstr "السلسلة" msgstr "السلسلة"
#: inventory/models.py:169 #: inventory/models.py:170
msgid "Trim" msgid "Trim"
msgstr "الفئة" msgstr "الفئة"
#: inventory/models.py:175 templates/inventory/car_detail.html:86 #: inventory/models.py:176 templates/inventory/car_detail.html:86
#: templates/inventory/car_list.html:163 #: templates/inventory/car_list.html:163
#: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/bills/tags/bill_table.html:10 #: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/bills/tags/bill_table.html:10
#: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/estimate/includes/card_estimate.html:12 #: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/estimate/includes/card_estimate.html:12
@ -114,171 +118,201 @@ msgstr "الفئة"
msgid "Status" msgid "Status"
msgstr "الحالة" msgstr "الحالة"
#: inventory/models.py:181 templates/inventory/car_detail.html:90 #: inventory/models.py:182 templates/inventory/car_detail.html:90
#: templates/inventory/car_form.html:243 templates/inventory/car_list.html:177 #: templates/inventory/car_form.html:248 templates/inventory/car_list.html:177
msgid "Stock Type" msgid "Stock Type"
msgstr "نوع المخزون" msgstr "نوع المخزون"
#: inventory/models.py:183 templates/inventory/car_detail.html:113 #: inventory/models.py:184 templates/inventory/car_detail.html:113
#: templates/inventory/car_form.html:296 templates/inventory/car_list.html:200 #: templates/inventory/car_form.html:301 templates/inventory/car_list.html:200
msgid "Remarks" msgid "Remarks"
msgstr "ملاحظات" msgstr "ملاحظات"
#: inventory/models.py:184 templates/inventory/car_detail.html:94 #: inventory/models.py:185 templates/inventory/car_detail.html:94
#: templates/inventory/car_form.html:260 templates/inventory/car_list.html:191 #: templates/inventory/car_form.html:265 templates/inventory/car_list.html:191
#: templates/inventory/car_list.html:192 #: templates/inventory/car_list.html:192
msgid "Mileage" msgid "Mileage"
msgstr "عدد الكيلومترات" msgstr "عدد الكيلومترات"
#: inventory/models.py:185 templates/inventory/car_detail.html:98 #: inventory/models.py:186 templates/inventory/car_detail.html:98
#: templates/inventory/car_form.html:278 #: templates/inventory/car_form.html:283
msgid "Receiving Date" msgid "Receiving Date"
msgstr "تاريخ الاستلام" msgstr "تاريخ الاستلام"
#: inventory/models.py:188 inventory/models.py:282 inventory/models.py:296 #: inventory/models.py:189 inventory/models.py:355 inventory/models.py:369
msgid "Car" msgid "Car"
msgstr "السيارة" msgstr "السيارة"
#: inventory/models.py:189 #: inventory/models.py:190
msgid "Cars" msgid "Cars"
msgstr "السيارات" msgstr "السيارات"
#: inventory/models.py:234 templates/inventory/car_detail.html:160 #: inventory/models.py:237 templates/inventory/car_detail.html:160
#: templates/inventory/car_finance_form.html:33
msgid "Cost Price" msgid "Cost Price"
msgstr "سعر التكلفة" msgstr "سعر التكلفة"
#: inventory/models.py:235 templates/inventory/car_detail.html:164 #: inventory/models.py:240
#: templates/inventory/car_finance_form.html:40
msgid "Profit Margin" msgid "Profit Margin"
msgstr "هامش الربح" msgstr "هامش الربح"
#: inventory/models.py:236 templates/inventory/car_detail.html:168 #: inventory/models.py:244 templates/inventory/car_detail.html:165
#: templates/inventory/car_finance_form.html:54
msgid "Selling Price" msgid "Selling Price"
msgstr "سعر البيع" msgstr "سعر البيع"
#: inventory/models.py:237 templates/inventory/car_detail.html:172 #: inventory/models.py:247 templates/inventory/car_detail.html:189
#: templates/inventory/car_finance_form.html:47
msgid "VAT Rate"
msgstr "نسبة ضريبة القيمة المضافة"
#: inventory/models.py:238 templates/inventory/car_detail.html:176
#: templates/inventory/car_finance_form.html:58
msgid "VAT Amount" msgid "VAT Amount"
msgstr "مبلغ ضريبة القيمة المضافة" msgstr "مبلغ ضريبة القيمة المضافة"
#: inventory/models.py:239 templates/inventory/car_finance_form.html:62 #: inventory/models.py:251 templates/inventory/car_detail.html:173
msgid "Registration Fee"
msgstr "رسوم التسجيل"
#: inventory/models.py:255 templates/inventory/car_detail.html:169
msgid "Administration Fee"
msgstr "الرسوم الادارية"
#: inventory/models.py:259 templates/inventory/car_detail.html:177
msgid "Transportation Fee"
msgstr "رسوم النقل"
#: inventory/models.py:263 templates/inventory/car_detail.html:181
msgid "Custom Card Fee"
msgstr "رسوم البطاقة الجمركية"
#: inventory/models.py:267 templates/inventory/car_detail.html:185
msgid "Discount Amount"
msgstr "مبلغ الخصم"
#: inventory/models.py:271
msgid "Total Amount" msgid "Total Amount"
msgstr "المبلغ الإجمالي" msgstr "المبلغ الإجمالي"
#: inventory/models.py:242 #: inventory/models.py:275
msgid "Car Financial Details" msgid "Car Financial Details"
msgstr "التفاصيل المالية السيارة" msgstr "تفاصيل المالية للسيارة"
#: inventory/models.py:258 #: inventory/models.py:280
msgid "Exterior" msgid "Selling price cannot be negative."
msgstr "الخارجي" msgstr "لا يمكن أن يكون سعر البيع سالبًا."
#: inventory/models.py:259 #: inventory/models.py:282
msgid "Interior" msgid "Discount amount cannot be negative."
msgstr "الداخلي" msgstr "لا يمكن أن يكون مبلغ الخصم سالبًا."
#: inventory/models.py:262 templates/dealers/dealer_detail.html:26 #: inventory/models.py:284
msgid "Discount amount cannot exceed selling price."
msgstr "لا يمكن أن يتجاوز مبلغ الخصم سعر البيع."
#: inventory/models.py:289
msgid "Fees cannot be negative."
msgstr "لا يمكن أن تكون الرسوم سلبية."
#: inventory/models.py:304
#, python-format
msgid "Invalid decimal operation: %s"
msgstr "عملية عشرية غير صالحة: %s"
#: inventory/models.py:313 inventory/models.py:326
#: templates/dealers/dealer_detail.html:26
#: templates/vendors/view_vendor.html:39 #: templates/vendors/view_vendor.html:39
#: venv/lib/python3.11/site-packages/django_ledger/forms/coa.py:16 #: venv/lib/python3.11/site-packages/django_ledger/forms/coa.py:16
#: venv/lib/python3.11/site-packages/django_ledger/forms/coa.py:37 #: venv/lib/python3.11/site-packages/django_ledger/forms/coa.py:37
msgid "Name" msgid "Name"
msgstr "الاسم" msgstr "الاسم"
#: inventory/models.py:263 inventory/models.py:325 inventory/models.py:344 #: inventory/models.py:314 inventory/models.py:327 inventory/models.py:398
#: inventory/models.py:417
msgid "Arabic Name" msgid "Arabic Name"
msgstr "الاسم بالعربية" msgstr "الاسم بالعربية"
#: inventory/models.py:264 #: inventory/models.py:315 inventory/models.py:328
msgid "RGB" msgid "RGB"
msgstr "آر جي بي" msgstr "آر جي بي"
#: inventory/models.py:269 templates/inventory/color_palette.html:83 #: inventory/models.py:318 inventory/models.py:319
msgid "Color Type" msgid "Exterior Colors"
msgstr "نوع اللون" msgstr "الألوان الخارجية"
#: inventory/models.py:273 #: inventory/models.py:331 inventory/models.py:332
msgid "Interior Colors"
msgstr "الألوان الداخلية"
#: inventory/models.py:345
msgid "Color" msgid "Color"
msgstr "اللون" msgstr "اللون"
#: inventory/models.py:274 #: inventory/models.py:346
msgid "Colors" msgid "Colors"
msgstr "الألوان" msgstr "الألوان"
#: inventory/models.py:283 templates/inventory/car_detail.html:131 #: inventory/models.py:356 templates/inventory/car_detail.html:131
msgid "Custom Number" msgid "Custom Number"
msgstr "رقم البطاقة الجمركية" msgstr "رقم البطاقة الجمركية"
#: inventory/models.py:287 templates/inventory/car_detail.html:16 #: inventory/models.py:360 templates/inventory/car_detail.html:16
#: templates/inventory/car_detail.html:141 #: templates/inventory/car_detail.html:141
msgid "Custom Card" msgid "Custom Card"
msgstr "البطاقة الجمركية" msgstr "البطاقة الجمركية"
#: inventory/models.py:288 #: inventory/models.py:361
msgid "Custom Cards" msgid "Custom Cards"
msgstr "البطاقات الجمركية" msgstr "البطاقات الجمركية"
#: inventory/models.py:297 #: inventory/models.py:370
msgid "Plate Number" msgid "Plate Number"
msgstr "رقم اللوحة" msgstr "رقم اللوحة"
#: inventory/models.py:298 #: inventory/models.py:371
msgid "Text 1" msgid "Text 1"
msgstr "النص 1" msgstr "النص 1"
#: inventory/models.py:299 #: inventory/models.py:372
msgid "Text 2" msgid "Text 2"
msgstr "النص 2" msgstr "النص 2"
#: inventory/models.py:300 #: inventory/models.py:373
msgid "Text 3" msgid "Text 3"
msgstr "النص 3" msgstr "النص 3"
#: inventory/models.py:301 #: inventory/models.py:374
msgid "Registration Date" msgid "Registration Date"
msgstr "تاريخ التسجيل" msgstr "تاريخ التسجيل"
#: inventory/models.py:304 #: inventory/models.py:377
msgid "Registration" msgid "Registration"
msgstr "التسجيل" msgstr "التسجيل"
#: inventory/models.py:305 #: inventory/models.py:378
msgid "Registrations" msgid "Registrations"
msgstr "تسجيل السيارات" msgstr "تسجيل السيارات"
#: inventory/models.py:313 inventory/models.py:368 #: inventory/models.py:386 inventory/models.py:441
#: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/chart_of_accounts/includes/coa_card.html:38 #: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/chart_of_accounts/includes/coa_card.html:38
#: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/closing_entry/tags/closing_entry_table.html:12 #: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/closing_entry/tags/closing_entry_table.html:12
#: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/ledger/tags/ledgers_table.html:17 #: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/ledger/tags/ledgers_table.html:17
msgid "Created" msgid "Created"
msgstr "تاريخ الإنشاء" msgstr "تاريخ الإنشاء"
#: inventory/models.py:314 #: inventory/models.py:387
#: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/chart_of_accounts/includes/coa_card.html:41 #: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/chart_of_accounts/includes/coa_card.html:41
msgid "Updated" msgid "Updated"
msgstr "تم التحديث" msgstr "تم التحديث"
#: inventory/models.py:323 inventory/models.py:342 #: inventory/models.py:396 inventory/models.py:415
#: templates/dealers/dealer_detail.html:30 #: templates/dealers/dealer_detail.html:30
msgid "Commercial Registration Number" msgid "Commercial Registration Number"
msgstr "رقم السجل التجاري" msgstr "رقم السجل التجاري"
#: inventory/models.py:324 inventory/models.py:343 #: inventory/models.py:397 inventory/models.py:416
#: templates/dealers/dealer_detail.html:34 #: templates/dealers/dealer_detail.html:34
msgid "VAT Registration Number" msgid "VAT Registration Number"
msgstr "رقم التسجيل في ضريبة القيمة المضافة" msgstr "رقم التسجيل في ضريبة القيمة المضافة"
#: inventory/models.py:326 inventory/models.py:345 #: inventory/models.py:399 inventory/models.py:418
msgid "English Name" msgid "English Name"
msgstr "الاسم بالإنجليزية" msgstr "الاسم بالإنجليزية"
#: inventory/models.py:327 inventory/models.py:347 inventory/models.py:366 #: inventory/models.py:400 inventory/models.py:420 inventory/models.py:439
#: templates/customers/view_customer.html:53 #: templates/customers/view_customer.html:53
#: templates/dealers/dealer_detail.html:38 #: templates/dealers/dealer_detail.html:38
#: templates/vendors/view_vendor.html:44 #: templates/vendors/view_vendor.html:44
@ -286,7 +320,7 @@ msgstr "الاسم بالإنجليزية"
msgid "Phone Number" msgid "Phone Number"
msgstr "رقم الهاتف" msgstr "رقم الهاتف"
#: inventory/models.py:328 inventory/models.py:348 inventory/models.py:367 #: inventory/models.py:401 inventory/models.py:421 inventory/models.py:440
#: templates/customers/view_customer.html:54 #: templates/customers/view_customer.html:54
#: templates/dealers/dealer_detail.html:42 #: templates/dealers/dealer_detail.html:42
#: templates/vendors/view_vendor.html:46 #: templates/vendors/view_vendor.html:46
@ -295,47 +329,47 @@ msgstr "رقم الهاتف"
msgid "Address" msgid "Address"
msgstr "العنوان" msgstr "العنوان"
#: inventory/models.py:329 #: inventory/models.py:402
msgid "Logo" msgid "Logo"
msgstr "الشعار" msgstr "الشعار"
#: inventory/models.py:333 #: inventory/models.py:406
msgid "Dealers" msgid "Dealers"
msgstr "المعارض" msgstr "المعارض"
#: inventory/models.py:346 templates/vendors/view_vendor.html:43 #: inventory/models.py:419 templates/vendors/view_vendor.html:43
msgid "Contact Person" msgid "Contact Person"
msgstr "الشخص المسؤول" msgstr "الشخص المسؤول"
#: inventory/models.py:352 templates/header.html:85 templates/header.html:100 #: inventory/models.py:425 templates/header.html:85 templates/header.html:100
#: templates/vendors/vendor_form.html:4 templates/vendors/vendors_list.html:4 #: templates/vendors/vendor_form.html:4 templates/vendors/vendors_list.html:4
#: templates/vendors/vendors_list.html:5 #: templates/vendors/vendors_list.html:5
msgid "Vendors" msgid "Vendors"
msgstr "الموردين" msgstr "الموردين"
#: inventory/models.py:361 templates/customers/view_customer.html:46 #: inventory/models.py:434 templates/customers/view_customer.html:46
msgid "First Name" msgid "First Name"
msgstr "الاسم الأول" msgstr "الاسم الأول"
#: inventory/models.py:362 templates/customers/view_customer.html:47 #: inventory/models.py:435 templates/customers/view_customer.html:47
msgid "Middle Name" msgid "Middle Name"
msgstr "اسم الأب" msgstr "اسم الأب"
#: inventory/models.py:363 templates/customers/view_customer.html:48 #: inventory/models.py:436 templates/customers/view_customer.html:48
msgid "Last Name" msgid "Last Name"
msgstr "اسم العائلة" msgstr "اسم العائلة"
#: inventory/models.py:364 templates/customers/view_customer.html:51 #: inventory/models.py:437 templates/customers/view_customer.html:51
#: templates/vendors/view_vendor.html:45 #: templates/vendors/view_vendor.html:45
#: venv/lib/python3.11/site-packages/django_ledger/models/mixins.py:111 #: venv/lib/python3.11/site-packages/django_ledger/models/mixins.py:111
msgid "Email" msgid "Email"
msgstr "البريد الإلكتروني" msgstr "البريد الإلكتروني"
#: inventory/models.py:365 templates/customers/view_customer.html:52 #: inventory/models.py:438 templates/customers/view_customer.html:52
msgid "National ID" msgid "National ID"
msgstr "رقم الهوية الوطنية" msgstr "رقم الهوية الوطنية"
#: inventory/models.py:371 #: inventory/models.py:444
#: venv/lib/python3.11/site-packages/django_ledger/models/customer.py:199 #: venv/lib/python3.11/site-packages/django_ledger/models/customer.py:199
#: venv/lib/python3.11/site-packages/django_ledger/models/estimate.py:252 #: venv/lib/python3.11/site-packages/django_ledger/models/estimate.py:252
#: venv/lib/python3.11/site-packages/django_ledger/models/invoice.py:318 #: venv/lib/python3.11/site-packages/django_ledger/models/invoice.py:318
@ -344,11 +378,11 @@ msgstr "رقم الهوية الوطنية"
msgid "Customer" msgid "Customer"
msgstr "العميل" msgstr "العميل"
#: inventory/models.py:372 #: inventory/models.py:445
msgid "Customers" msgid "Customers"
msgstr "العملاء" msgstr "العملاء"
#: inventory/models.py:394 #: inventory/models.py:467
#: venv/lib/python3.11/site-packages/django_ledger/models/accounts.py:436 #: venv/lib/python3.11/site-packages/django_ledger/models/accounts.py:436
#: venv/lib/python3.11/site-packages/django_ledger/models/coa.py:152 #: venv/lib/python3.11/site-packages/django_ledger/models/coa.py:152
msgid "Chart of Accounts" msgid "Chart of Accounts"
@ -370,116 +404,96 @@ msgstr "نسيت كلمة المرور؟"
msgid "You are not associated with any dealer." msgid "You are not associated with any dealer."
msgstr "أنت غير مرتبط بأي معرض." msgstr "أنت غير مرتبط بأي معرض."
#: inventory/views.py:248 templates/header.html:33 templates/index.html:20 #: inventory/views.py:260 templates/header.html:33 templates/index.html:20
#: templates/inventory/car_inventory.html:5 #: templates/inventory/car_inventory.html:5
#: templates/inventory/car_inventory.html:7 #: templates/inventory/car_inventory.html:7
msgid "inventory" msgid "inventory"
msgstr "المخزون" msgstr "المخزون"
#: inventory/views.py:370 #: inventory/views.py:401
msgid "Car finance details saved successfully." msgid "Car finance details saved successfully."
msgstr "تم حفظ تفاصيل المالية للسيارة بنجاح." msgstr "تم حفظ تفاصيل المالية للسيارة بنجاح."
#: inventory/views.py:388 #: inventory/views.py:419
msgid "Car finance updated successfully." msgid "Car finance updated successfully."
msgstr "تم تحديث التفاصيل المالية للسيارة بنجاح." msgstr "تم تحديث التفاصيل المالية للسيارة بنجاح."
#: inventory/views.py:401 #: inventory/views.py:432
msgid "Car updated successfully." msgid "Car updated successfully."
msgstr "تم تحديث السيارة بنجاح" msgstr "تم تحديث السيارة بنجاح"
#: inventory/views.py:414 #: inventory/views.py:445
msgid "Car deleted successfully." msgid "Car deleted successfully."
msgstr "تم حذف السيارة بنجاح." msgstr "تم حذف السيارة بنجاح."
#: inventory/views.py:434 #: inventory/views.py:465
msgid "Custom Card added successfully." msgid "Custom Card added successfully."
msgstr "تم إضافة البطاقة الجمركية بنجاح." msgstr "تم إضافة البطاقة الجمركية بنجاح."
#: inventory/views.py:452 inventory/views.py:521 #: inventory/views.py:474
msgid "Select a Color"
msgstr "اختر اللون"
#: inventory/views.py:457
msgid "Select Color Type"
msgstr "حدد نوع اللون"
#: inventory/views.py:485 inventory/views.py:548
msgid "Invalid color selection."
msgstr "تحديد اللون غير صالح."
#: inventory/views.py:495
msgid "Color added successfully."
msgstr "تمت إضافة اللون بنجاح."
#: inventory/views.py:555
msgid "Exterior color updated successfully."
msgstr "تم تحديث اللون الخارجي بنجاح."
#: inventory/views.py:572
msgid "This car is already reserved." msgid "This car is already reserved."
msgstr "هذه السيارة محجوزة بالفعل." msgstr "هذه السيارة محجوزة بالفعل."
#: inventory/views.py:582 #: inventory/views.py:484
msgid "Car reserved successfully." msgid "Car reserved successfully."
msgstr "تم حجز السيارة بنجاح." msgstr "تم حجز السيارة بنجاح."
#: inventory/views.py:599 #: inventory/views.py:501
msgid "Reservation renewed successfully." msgid "Reservation renewed successfully."
msgstr "تم تجديد الحجز بنجاح" msgstr "تم تجديد الحجز بنجاح"
#: inventory/views.py:604 #: inventory/views.py:506
msgid "Reservation canceled successfully." msgid "Reservation canceled successfully."
msgstr "تم إلغاء الحجز بنجاح." msgstr "تم إلغاء الحجز بنجاح."
#: inventory/views.py:608 #: inventory/views.py:510
msgid "Invalid action." msgid "Invalid action."
msgstr "إجراء غير صالح." msgstr "إجراء غير صالح."
#: inventory/views.py:610 #: inventory/views.py:512
msgid "Invalid request method." msgid "Invalid request method."
msgstr "طريقة الطلب غير صالحة" msgstr "طريقة الطلب غير صالحة"
#: inventory/views.py:632 #: inventory/views.py:534
msgid "Dealer created successfully." msgid "Dealer created successfully."
msgstr "تم إنشاء المعرض بنجاح." msgstr "تم إنشاء المعرض بنجاح."
#: inventory/views.py:643 #: inventory/views.py:545
msgid "Dealer updated successfully." msgid "Dealer updated successfully."
msgstr "تم تحديث المعرض بنجاح." msgstr "تم تحديث المعرض بنجاح."
#: inventory/views.py:653 #: inventory/views.py:555
msgid "Dealer deleted successfully." msgid "Dealer deleted successfully."
msgstr "تم حذف المعرض بنجاح." msgstr "تم حذف المعرض بنجاح."
#: inventory/views.py:659 templates/customers/customer_form.html:4 #: inventory/views.py:561 templates/customers/customer_form.html:4
#: templates/customers/customer_list.html:5 #: templates/customers/customer_list.html:5
#: templates/customers/customer_list.html:6 templates/header.html:59 #: templates/customers/customer_list.html:6 templates/header.html:59
#: templates/header.html:74 #: templates/header.html:74
msgid "customers" msgid "customers"
msgstr "العملاء" msgstr "العملاء"
#: inventory/views.py:698 #: inventory/views.py:600
msgid "Customer created successfully." msgid "Customer created successfully."
msgstr "تم إنشاء العميل بنجاح." msgstr "تم إنشاء العميل بنجاح."
#: inventory/views.py:714 #: inventory/views.py:616
msgid "Customer updated successfully." msgid "Customer updated successfully."
msgstr "تم تحديث العميل بنجاح." msgstr "تم تحديث العميل بنجاح."
#: inventory/views.py:724 #: inventory/views.py:626
msgid "Customer deleted successfully." msgid "Customer deleted successfully."
msgstr "تم حذف العميل بنجاح." msgstr "تم حذف العميل بنجاح."
#: inventory/views.py:750 #: inventory/views.py:652
msgid "Vendor created successfully." msgid "Vendor created successfully."
msgstr "تم إنشاء المورد بنجاح." msgstr "تم إنشاء المورد بنجاح."
#: inventory/views.py:766 #: inventory/views.py:668
msgid "Vendor updated successfully." msgid "Vendor updated successfully."
msgstr "تم تحديث المورد بنجاح" msgstr "تم تحديث المورد بنجاح"
#: inventory/views.py:776 #: inventory/views.py:678
msgid "Vendor deleted successfully." msgid "Vendor deleted successfully."
msgstr "تم حذف المورد بنجاح." msgstr "تم حذف المورد بنجاح."
@ -583,8 +597,10 @@ msgid "Add Customer"
msgstr "إضافة عميل" msgstr "إضافة عميل"
#: templates/customers/customer_form.html:31 #: templates/customers/customer_form.html:31
#: templates/inventory/add_colors.html:54
#: templates/inventory/add_custom_card.html:7 #: templates/inventory/add_custom_card.html:7
#: templates/inventory/car_edit.html:41 templates/inventory/car_form.html:320 #: templates/inventory/car_edit.html:41
#: templates/inventory/car_finance_form.html:40
#: templates/inventory/color_palette.html:106 #: templates/inventory/color_palette.html:106
#: templates/vendors/vendor_form.html:31 #: templates/vendors/vendor_form.html:31
#: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/bills/tags/bill_item_formset.html:81 #: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/bills/tags/bill_item_formset.html:81
@ -598,10 +614,11 @@ msgid "Save"
msgstr "حفظ" msgstr "حفظ"
#: templates/customers/customer_form.html:33 #: templates/customers/customer_form.html:33
#: templates/inventory/add_colors.html:55
#: templates/inventory/add_custom_card.html:8 #: templates/inventory/add_custom_card.html:8
#: templates/inventory/car_confirm_delete.html:14 #: templates/inventory/car_confirm_delete.html:14
#: templates/inventory/car_detail.html:266 #: templates/inventory/car_detail.html:282
#: templates/inventory/car_finance_form.html:68 #: templates/inventory/car_finance_form.html:41
#: templates/inventory/color_palette.html:107 #: templates/inventory/color_palette.html:107
#: templates/inventory/reserve_car.html:30 #: templates/inventory/reserve_car.html:30
#: templates/vendors/vendor_form.html:33 #: templates/vendors/vendor_form.html:33
@ -616,7 +633,7 @@ msgid "Cancel"
msgstr "إلغاء" msgstr "إلغاء"
#: templates/customers/customer_list.html:20 #: templates/customers/customer_list.html:20
#: templates/inventory/car_inventory.html:29 #: templates/inventory/car_inventory.html:31
#: templates/inventory/car_list.html:70 templates/vendors/vendors_list.html:21 #: templates/inventory/car_list.html:70 templates/vendors/vendors_list.html:21
msgid "search" msgid "search"
msgstr "بحث" msgstr "بحث"
@ -644,7 +661,7 @@ msgstr "الإجراءات"
#: templates/customers/customer_list.html:59 #: templates/customers/customer_list.html:59
#: templates/inventory/car_detail.html:124 #: templates/inventory/car_detail.html:124
#: templates/inventory/car_inventory.html:62 #: templates/inventory/car_inventory.html:75
#: templates/vendors/vendors_list.html:65 #: templates/vendors/vendors_list.html:65
msgid "view" msgid "view"
msgstr "عرض" msgstr "عرض"
@ -674,8 +691,7 @@ msgid "Customer Details"
msgstr "تفاصيل العميل" msgstr "تفاصيل العميل"
#: templates/customers/view_customer.html:61 #: templates/customers/view_customer.html:61
#: templates/inventory/car_detail.html:217 #: templates/inventory/car_detail.html:307
#: templates/inventory/car_detail.html:291
#: templates/vendors/view_vendor.html:48 #: templates/vendors/view_vendor.html:48
#: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/customer/includes/card_customer.html:28 #: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/customer/includes/card_customer.html:28
#: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/journal_entry/tags/je_table.html:83 #: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/journal_entry/tags/je_table.html:83
@ -717,7 +733,7 @@ msgid "Delete"
msgstr "حذف" msgstr "حذف"
#: templates/customers/view_customer.html:72 #: templates/customers/view_customer.html:72
#: templates/inventory/car_detail.html:293 #: templates/inventory/car_detail.html:309
msgid "Back to List" msgid "Back to List"
msgstr "العودة إلى القائمة" msgstr "العودة إلى القائمة"
@ -941,7 +957,7 @@ msgid "View your best-selling cars."
msgstr "عرض السيارات الأكثر مبيعاً." msgstr "عرض السيارات الأكثر مبيعاً."
#: templates/index.html:151 templates/inventory/car_detail.html:74 #: templates/index.html:151 templates/inventory/car_detail.html:74
#: templates/inventory/car_form.html:155 templates/inventory/car_list.html:97 #: templates/inventory/car_form.html:159 templates/inventory/car_list.html:97
msgid "model" msgid "model"
msgstr "الموديل" msgstr "الموديل"
@ -1000,13 +1016,31 @@ msgstr "الجمعة"
msgid "Sat" msgid "Sat"
msgstr "السبت" msgstr "السبت"
#: templates/inventory/add_colors.html:5
msgid "Add Colors"
msgstr "إضافة لون"
#: templates/inventory/add_colors.html:6
msgid "Select exterior and interior colors for"
msgstr "اختر الألوان الخارجية والداخلية لـ"
#: templates/inventory/add_colors.html:13
#: templates/inventory/car_detail.html:219
msgid "Exterior"
msgstr "الخارجي"
#: templates/inventory/add_colors.html:32
#: templates/inventory/car_detail.html:228
msgid "Interior"
msgstr "الداخلي"
#: templates/inventory/car_detail.html:6 templates/inventory/car_detail.html:58 #: templates/inventory/car_detail.html:6 templates/inventory/car_detail.html:58
msgid "Car Details" msgid "Car Details"
msgstr "تفاصيل السيارة" msgstr "تفاصيل السيارة"
#: templates/inventory/car_detail.html:37 #: templates/inventory/car_detail.html:37
#: templates/inventory/car_detail.html:117 templates/inventory/car_form.html:37 #: templates/inventory/car_detail.html:117 templates/inventory/car_form.html:37
#: templates/inventory/car_form.html:316 templates/inventory/car_list.html:47 #: templates/inventory/car_form.html:322 templates/inventory/car_list.html:47
#: templates/inventory/car_list.html:221 #: templates/inventory/car_list.html:221
msgid "specifications" msgid "specifications"
msgstr "المواصفات" msgstr "المواصفات"
@ -1015,7 +1049,7 @@ msgstr "المواصفات"
msgid "year" msgid "year"
msgstr "السنة" msgstr "السنة"
#: templates/inventory/car_detail.html:69 templates/inventory/car_form.html:137 #: templates/inventory/car_detail.html:69 templates/inventory/car_form.html:140
#: templates/inventory/car_list.html:79 #: templates/inventory/car_list.html:79
msgid "make" msgid "make"
msgstr "الصانع" msgstr "الصانع"
@ -1024,7 +1058,7 @@ msgstr "الصانع"
msgid "series" msgid "series"
msgstr "السلسلة" msgstr "السلسلة"
#: templates/inventory/car_detail.html:82 templates/inventory/car_form.html:199 #: templates/inventory/car_detail.html:82 templates/inventory/car_form.html:204
#: templates/inventory/car_list.html:141 #: templates/inventory/car_list.html:141
msgid "trim" msgid "trim"
msgstr "الفئة" msgstr "الفئة"
@ -1041,7 +1075,7 @@ msgstr "إضافة"
msgid "Financial Details" msgid "Financial Details"
msgstr "التفاصيل المالية" msgstr "التفاصيل المالية"
#: templates/inventory/car_detail.html:180 #: templates/inventory/car_detail.html:193
#: templates/inventory/inventory_stats.html:61 #: templates/inventory/inventory_stats.html:61
#: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/bills/bill_detail.html:98 #: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/bills/bill_detail.html:98
#: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/bills/bill_detail.html:127 #: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/bills/bill_detail.html:127
@ -1060,44 +1094,44 @@ msgstr "التفاصيل المالية"
msgid "Total" msgid "Total"
msgstr "الإجمالي" msgstr "الإجمالي"
#: templates/inventory/car_detail.html:187 #: templates/inventory/car_detail.html:200
msgid "Edit Finance Details" msgid "Edit Finance Details"
msgstr "تعديل التفاصيل المالية" msgstr "تعديل التفاصيل المالية"
#: templates/inventory/car_detail.html:191 #: templates/inventory/car_detail.html:204
msgid "No finance details available." msgid "No finance details available."
msgstr "لا توجد تفاصيل مالية متاحة." msgstr "لا توجد تفاصيل مالية متاحة."
#: templates/inventory/car_detail.html:194 #: templates/inventory/car_detail.html:207
msgid "Add Finance Details" msgid "Add Finance Details"
msgstr "إضافة التفاصيل المالية" msgstr "إضافة التفاصيل المالية"
#: templates/inventory/car_detail.html:200 #: templates/inventory/car_detail.html:213
msgid "Colors Details" msgid "Colors Details"
msgstr "تفاصيل الألوان" msgstr "تفاصيل الألوان"
#: templates/inventory/car_detail.html:224 #: templates/inventory/car_detail.html:240
msgid "No colors available for this car." msgid "No colors available for this car."
msgstr "لا تتوفر ألوان لهذه السيارة." msgstr "لا تتوفر ألوان لهذه السيارة."
#: templates/inventory/car_detail.html:231 #: templates/inventory/car_detail.html:247
msgid "Get Colors" msgid "Get Colors"
msgstr "الحصول على الألوان" msgstr "الحصول على الألوان"
#: templates/inventory/car_detail.html:238 #: templates/inventory/car_detail.html:254
msgid "Reservations Details" msgid "Reservations Details"
msgstr "تفاصيل الحجز" msgstr "تفاصيل الحجز"
#: templates/inventory/car_detail.html:243 #: templates/inventory/car_detail.html:259
msgid "Reserved By" msgid "Reserved By"
msgstr "محجوز بواسطة" msgstr "محجوز بواسطة"
#: templates/inventory/car_detail.html:244 #: templates/inventory/car_detail.html:260
msgid "Expires At" msgid "Expires At"
msgstr "ينتهي في" msgstr "ينتهي في"
#: templates/inventory/car_detail.html:245 #: templates/inventory/car_detail.html:261
#: templates/inventory/car_inventory.html:44 #: templates/inventory/car_inventory.html:57
#: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/account/tags/account_txs_table.html:29 #: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/account/tags/account_txs_table.html:29
#: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/account/tags/accounts_table.html:29 #: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/account/tags/accounts_table.html:29
#: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/account/tags/accounts_table.html:92 #: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/account/tags/accounts_table.html:92
@ -1124,27 +1158,27 @@ msgstr "ينتهي في"
msgid "Actions" msgid "Actions"
msgstr "الإجراءات" msgstr "الإجراءات"
#: templates/inventory/car_detail.html:260 #: templates/inventory/car_detail.html:276
msgid "renew" msgid "renew"
msgstr "تجديد" msgstr "تجديد"
#: templates/inventory/car_detail.html:277 #: templates/inventory/car_detail.html:293
#: templates/inventory/reserve_car.html:29 #: templates/inventory/reserve_car.html:29
msgid "Reserve" msgid "Reserve"
msgstr "حجز" msgstr "حجز"
#: templates/inventory/car_detail.html:289 #: templates/inventory/car_detail.html:305
#: templates/inventory/transfer_car.html:23 #: templates/inventory/transfer_car.html:23
msgid "transfer" msgid "transfer"
msgstr "نقل" msgstr "نقل"
#: templates/inventory/car_detail.html:378 #: templates/inventory/car_detail.html:394
#: templates/inventory/car_list.html:542 #: templates/inventory/car_list.html:542
#: templates/partials/specifications_modal.html:11 #: templates/partials/specifications_modal.html:11
msgid "No specifications available." msgid "No specifications available."
msgstr "لا توجد مواصفات متاحة." msgstr "لا توجد مواصفات متاحة."
#: templates/inventory/car_detail.html:382 #: templates/inventory/car_detail.html:398
#: templates/inventory/car_list.html:546 #: templates/inventory/car_list.html:546
msgid "Error loading specifications." msgid "Error loading specifications."
msgstr "حدث خطأ أثناء تحميل المواصفات." msgstr "حدث خطأ أثناء تحميل المواصفات."
@ -1153,30 +1187,14 @@ msgstr "حدث خطأ أثناء تحميل المواصفات."
msgid "Edit Car" msgid "Edit Car"
msgstr "تعديل السيارة" msgstr "تعديل السيارة"
#: templates/inventory/car_finance_form.html:5 #: templates/inventory/car_finance_form.html:6
msgid "Car Finance Details" msgid "Car Finance Details"
msgstr "التفاصيل المالية السيارة" msgstr "التفاصيل المالية السيارة"
#: templates/inventory/car_finance_form.html:9 #: templates/inventory/car_finance_form.html:10
msgid "Finance Details for" msgid "Finance Details for"
msgstr "التفاصيل المالية لـ" msgstr "التفاصيل المالية لـ"
#: templates/inventory/car_finance_form.html:36
msgid "Please provide a valid cost price."
msgstr "يرجى تقديم سعر تكلفة صالح."
#: templates/inventory/car_finance_form.html:43
msgid "Please provide a profit margin between 0 and 100."
msgstr "يجب أن يكون هامش الربح بين 0 و 100"
#: templates/inventory/car_finance_form.html:50
msgid "Please provide a valid VAT rate."
msgstr "يرجى تقديم معدل صالح لضريبة القيمة المضافة."
#: templates/inventory/car_finance_form.html:67
msgid "Save Finance Details"
msgstr "حفظ التفاصيل المالية"
#: templates/inventory/car_form.html:42 templates/inventory/car_form.html:58 #: templates/inventory/car_form.html:42 templates/inventory/car_form.html:58
#: templates/partials/scanner_modal.html:6 #: templates/partials/scanner_modal.html:6
#: templates/partials/specifications_modal.html:8 #: templates/partials/specifications_modal.html:8
@ -1190,7 +1208,7 @@ msgstr "الماسح الضوئي"
#: templates/inventory/car_form.html:62 #: templates/inventory/car_form.html:62
#: templates/partials/scanner_modal.html:10 #: templates/partials/scanner_modal.html:10
msgid "VIN will appear here." msgid "VIN will appear here."
msgstr "" msgstr "رقم الهيكل سيظهر هنا."
#: templates/inventory/car_form.html:63 #: templates/inventory/car_form.html:63
#: templates/partials/scanner_modal.html:11 #: templates/partials/scanner_modal.html:11
@ -1201,34 +1219,42 @@ msgstr "التعرف الآلي على الحروف"
msgid "Search" msgid "Search"
msgstr "بحث" msgstr "بحث"
#: templates/inventory/car_form.html:160 templates/inventory/car_form.html:182 #: templates/inventory/car_form.html:165 templates/inventory/car_form.html:187
#: templates/inventory/car_form.html:204 templates/inventory/car_form.html:498 #: templates/inventory/car_form.html:209 templates/inventory/car_form.html:519
#: templates/inventory/car_form.html:515 templates/inventory/car_form.html:516 #: templates/inventory/car_form.html:536 templates/inventory/car_form.html:537
#: templates/inventory/car_form.html:534 #: templates/inventory/car_form.html:555
msgid "Select" msgid "Select"
msgstr "اختيار" msgstr "اختيار"
#: templates/inventory/car_form.html:381 #: templates/inventory/car_form.html:329
msgid "Save and Add Another"
msgstr "حفظ وإضافة آخر"
#: templates/inventory/car_form.html:335
msgid "Save and Go to Inventory"
msgstr "حفظ والانتقال إلى المخزون"
#: templates/inventory/car_form.html:399
msgid "Please Wait" msgid "Please Wait"
msgstr "الرجاء الإنتظار" msgstr "الرجاء الإنتظار"
#: templates/inventory/car_form.html:382 #: templates/inventory/car_form.html:400
msgid "Loading" msgid "Loading"
msgstr "تحميل" msgstr "تحميل"
#: templates/inventory/car_form.html:413 #: templates/inventory/car_form.html:431
msgid "Please enter a valid VIN." msgid "Please enter a valid VIN."
msgstr "الرجاء إدخال رقم هيكل صالح مكون من 17 حرفًا." msgstr "الرجاء إدخال رقم هيكل صالح مكون من 17 حرفًا."
#: templates/inventory/car_form.html:429 #: templates/inventory/car_form.html:447
msgid "Failed to decode VIN." msgid "Failed to decode VIN."
msgstr "فشل في فك تشفير رقم الهيكل" msgstr "فشل في فك تشفير رقم الهيكل"
#: templates/inventory/car_form.html:434 #: templates/inventory/car_form.html:452
msgid "An error occurred while decoding the VIN." msgid "An error occurred while decoding the VIN."
msgstr "حدث خطأ أثناء فك تشفير الهيكل" msgstr "حدث خطأ أثناء فك تشفير الهيكل"
#: templates/inventory/car_inventory.html:67 #: templates/inventory/car_inventory.html:80
msgid "No cars available." msgid "No cars available."
msgstr "لا توجد سيارات متاحة." msgstr "لا توجد سيارات متاحة."
@ -1294,6 +1320,10 @@ msgstr "تحديث اللون"
msgid "Add Color for" msgid "Add Color for"
msgstr "إضافة لون الى" msgstr "إضافة لون الى"
#: templates/inventory/color_palette.html:83
msgid "Color Type"
msgstr "نوع اللون"
#: templates/inventory/inventory_stats.html:5 #: templates/inventory/inventory_stats.html:5
msgid "Inventory Statistics" msgid "Inventory Statistics"
msgstr "إحصائيات المخزون" msgstr "إحصائيات المخزون"

BIN
static/.DS_Store vendored

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
static/images/logos/suppliers/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
templates/.DS_Store vendored

Binary file not shown.

View File

@ -30,7 +30,10 @@
min-height: 100vh; min-height: 100vh;
text-transform: uppercase; text-transform: uppercase;
} }
small, .small {
font-size: 0.9rem;
line-height: 0;
}
.btn { .btn {
text-transform: uppercase; text-transform: uppercase;
} }
@ -45,7 +48,7 @@
<!-- Main content goes here --> <!-- Main content goes here -->
{% endblock %} {% endblock %}
{% include 'footer.html' %}
<!-- JavaScript Files --> <!-- JavaScript Files -->

View File

@ -1,9 +0,0 @@
{% load i18n %}
<footer class="mt-3 mb-0 fixed-bottom">
<div class="container text-center">
<div class="text-body-secondary fw-light">
<span>&#169; 2024</span> <span>{% trans 'All right reserved' %}:</span> <a class="text-muted" href="https://tenhal.sa">{% trans 'Tenhal' %}</a><span> </span>
</div>
</div>
</footer>

View File

@ -0,0 +1,61 @@
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<div class="container mt-4">
<h5 class="text-center">{% trans "Add Colors" %}</h5>
<p class="text-center">{% trans "Select exterior and interior colors for" %} {{ car.id_car_make.get_local_name }} {{ car.id_car_model.get_local_name }}</p>
<form method="post">
{% csrf_token %}
<!-- Exterior Colors -->
<div class="row g-4">
<p class="fs-5 mb-0">{% trans 'Exterior Colors' %}</p>
{% for color in form.fields.exterior.queryset %}
<div class="col-lg-4 col-xl-2">
<div class="card rounded shadow">
<div class="card-body" style="background-color: rgb({{ color.rgb }});">
<div class="form-check">
<input class="form-check-input"
type="radio"
name="exterior"
id="id_exterior" value="{{ color.id }}" >
<label class="form-check-label" for="id_exterior">
<small>{{ color.get_local_name }}</small>
</label>
</div>
</div>
</div>
</div>
{% endfor %}
<!-- Interior Colors -->
<p class="fs-5 mt-3 mb-0">{% trans 'Interior Colors' %}</p>
{% for color in form.fields.interior.queryset %}
<div class="col-lg-4 col-xl-2">
<div class="card rounded shadow">
<div class="card-body" style="background-color: rgb({{ color.rgb }});">
<div class="form-check">
<input class="form-check-input"
type="radio"
name="interior"
id="id_interior" value="{{ color.id }}" >
<label class="form-check-label" for="id_interior">
<small>{{ color.get_local_name }}</small>
</label>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<!-- Save and Cancel Buttons -->
<div class="row g-1 mt-4">
<div class="btn-group">
<button type="submit" class="btn btn-success btn-sm me-1">{% trans "Save" %}</button>
<a href="{% url 'car_detail' car.pk %}" class="btn btn-secondary btn-sm">{% trans "Cancel" %}</a>
</div>
</div>
</form>
</div>
{% endblock %}

View File

@ -31,7 +31,7 @@
aria-hidden="true"> aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable"> <div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content glossy-modal"> <div class="modal-content glossy-modal">
<div class="modal-header bg-success"> <div class="modal-header bg-primary text-light">
<h5 class="modal-title" <h5 class="modal-title"
id="specificationsModalLabel"> id="specificationsModalLabel">
{% trans 'specifications'|upper %} {% trans 'specifications'|upper %}
@ -152,7 +152,6 @@
</table> </table>
</div> </div>
<div class="col-lg-6 col-xl-6"> <div class="col-lg-6 col-xl-6">
<p class="fs-5">{% trans 'Financial Details' %}</p> <p class="fs-5">{% trans 'Financial Details' %}</p>
{% if car.finances.exists %} {% for finance in car.finances.all %} {% if car.finances.exists %} {% for finance in car.finances.all %}
<table class="table table-sm table-responsive mb-3 align-middle"> <table class="table table-sm table-responsive mb-3 align-middle">
@ -160,17 +159,29 @@
<th>{% trans "Cost Price" %}</th> <th>{% trans "Cost Price" %}</th>
<td>{{ finance.cost_price }}</td> <td>{{ finance.cost_price }}</td>
</tr> </tr>
<tr>
<th>{% trans "Profit Margin" %}</th>
<td>{{ finance.profit_margin|percentage }}</td>
</tr>
<tr> <tr>
<th>{% trans "Selling Price" %}</th> <th>{% trans "Selling Price" %}</th>
<td>{{ finance.selling_price }}</td> <td>{{ finance.selling_price }}</td>
</tr> </tr>
<tr> <tr>
<th>{% trans "VAT Rate" %}</th> <td><small class="ms-5">{% trans "Administration Fee" %}</small></td>
<td>{{ finance.vat_rate|percentage }}</td> <td><small>{{ finance.administration_fee }}</small></td>
</tr>
<tr>
<td><small class="ms-5">{% trans "Registration Fee" %}</small></td>
<td><small>{{ finance.registration_fee }}</small></td>
</tr>
<tr>
<td><small class="ms-5">{% trans "Transportation Fee" %}</small></td>
<td><small>{{ finance.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>
<tr>
<th>{% trans "Discount Amount" %}</th>
<td>{{ finance.discount_amount }} - </td>
</tr> </tr>
<tr> <tr>
<th>{% trans "VAT Amount" %}</th> <th>{% trans "VAT Amount" %}</th>
@ -200,25 +211,28 @@
<p class="fs-5 mt-2">{% trans 'Colors Details' %}</p> <p class="fs-5 mt-2">{% trans 'Colors Details' %}</p>
<table class="table table-sm table-responsive align-middle"> <table class="table table-sm table-responsive align-middle">
<tbody class=" align-middle"> <tbody class=" align-middle">
{% for color in car.colors.all %} {% if car.colors.exists %}
{% for color in car.colors.all %}
<tr> <tr>
<td> <th>{% trans 'Exterior' %}</th>
{{ color.get_color_type_display }}
</td>
<td> <td>
<span>{{ color.name.get_local_name }}</span> <span>{{ color.exterior.get_local_name }}</span>
</td> </td>
<td class="align-middle"> <td class="align-middle">
<div class="text-end" style="width: 150px; height: 32px; background-color: rgb({{ color.rgb }}); border-radius: 5px; border-style: solid; border-color: #000;"></div> <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>
</td> </td>
<td class="align-middle">
<a class="btn btn-warning btn-sm"
href="{% url 'color_update' car.pk color.pk %}">
{% trans 'Edit' %}
<i class="bi bi-arrow-repeat"></i></a>
</td>
</tr> </tr>
{% empty %} <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>
</td>
</tr>
{% endfor %}
{% else %}
<tr> <tr>
<td colspan="2"> <td colspan="2">
{% trans "No colors available for this car." %} {% trans "No colors available for this car." %}
@ -232,7 +246,7 @@
</a> </a>
</td> </td>
</tr> </tr>
{% endfor %} {% endif %}
</tbody> </tbody>
</table> </table>
<p class="fs-5 mt-2">{% trans 'Reservations Details' %}</p> <p class="fs-5 mt-2">{% trans 'Reservations Details' %}</p>
@ -250,22 +264,26 @@
<tr> <tr>
<td>{{ reservation.reserved_by.username }}</td> <td>{{ reservation.reserved_by.username }}</td>
<td>{{ reservation.reserved_until }}</td> <td>{{ reservation.reserved_until }}</td>
<td colspan="2"> <td>
{% if reservation.is_active %}
<form method="post" action="{% url 'reservations' reservation.id %}"> <form method="post" action="{% url 'reservations' reservation.id %}">
{% csrf_token %} {% csrf_token %}
<button type="submit" <button type="submit"
name="action" name="action"
value="renew" value="renew"
class="btn btn-sm btn-primary"> class="btn btn-sm btn-success">
<small>{% trans "renew" %}</small> <small>{% trans "Renew" %}</small>
</button> </button>
<button type="submit" <button type="submit"
name="action" name="action"
value="cancel" value="cancel"
class="btn btn-sm btn-danger"> class="btn btn-sm btn-secondary">
<small>{% trans "Cancel" %}</small> <small>{% trans "Cancel" %}</small>
</button> </button>
</form> </form>
{% else %}
<span class="badge bg-danger" style="width: 120px;">{% trans "Expired" %}</span>
{% endif %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -1,4 +1,5 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load crispy_forms_filters %}
{% load i18n %} {% load i18n %}
{% load custom_filters %} {% load custom_filters %}
@ -25,87 +26,25 @@
</ul> </ul>
</div> </div>
{% endif %} {% endif %}
<form method="post" class="needs-validation" novalidate>
<form method="post" class="needs-validation" novalidate> <div class="row g-1">
{% csrf_token %} <div class="col-lg-4 col-xl-12">
<div class="row g-1">
<div class="col-lg-4 col-xl-2"> {% csrf_token %}
<label for="id_cost_price" class="form-label">{% trans 'Cost Price' %}</label>
{{ form.cost_price|add_class:"form-control form-control-sm" }} {{ form|crispy }}
<div class="invalid-feedback">
{% trans 'Please provide a valid cost price.' %}
</div>
</div>
<div class="col-lg-4 col-xl-2">
<label for="id_profit_margin_percentage" class="form-label">{% trans 'Profit Margin' %} (%)</label>
<input type="number" name="profit_margin_percentage" id="id_profit_margin_percentage" class="form-control form-control-sm" min="0" max="100" step="1" required>
<div class="invalid-feedback">
{% trans 'Please provide a profit margin between 0 and 100.' %}
</div>
</div>
<div class="col-lg-4 col-xl-2">
<label for="id_vat_rate_percentage" class="form-label">{% trans 'VAT Rate' %} (%)</label>
<input type="number" name="vat_rate_percentage" id="id_vat_rate_percentage" class="form-control form-control-sm" min="0" max="100" step="1" required>
<div class="invalid-feedback">
{% trans 'Please provide a valid VAT rate.' %}
</div>
</div>
<div class="col-lg-2 col-xl-12">
<label class="form-label">{% trans 'Selling Price' %}</label>
<input type="text" class="form-control" id="selling_price_display" readonly>
</div>
<div class="col-lg-2 col-xl-12">
<label class="form-label">{% trans 'VAT Amount' %}</label>
<input type="text" class="form-control" id="vat_amount_display" readonly>
</div>
<div class="col-lg-2 col-xl-12">
<label class="form-label">{% trans 'Total Amount' %}</label>
<input type="text" class="form-control" id="total_display" readonly>
</div>
</div> </div>
<div class="d-flex justify-content-end mt-4"> </div>
<button type="submit" class="btn btn-sm btn-primary me-1">{% trans "Save Finance Details" %}</button> <div class="row g-1">
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-sm btn-danger">{% trans "Cancel" %}</a> <div class="btn-group">
<button type="submit" class="btn btn-sm btn-success me-1">{% trans "Save" %}</button>
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-sm btn-danger">{% trans "Cancel" %}</a>
</div>
</div> </div>
</form> </form>
</div> </div>
<!-- JavaScript Section --> <!-- JavaScript Section -->
<script>
document.addEventListener("DOMContentLoaded", function() {
const costPriceInput = document.getElementById('id_cost_price');
const profitMarginInput = document.getElementById('id_profit_margin_percentage');
const vatRateInput = document.getElementById('id_vat_rate_percentage');
const sellingPriceDisplay = document.getElementById('selling_price_display');
const vatAmountDisplay = document.getElementById('vat_amount_display');
const totalDisplay = document.getElementById('total_display');
function calculateFinanceDetails() {
const costPrice = parseFloat(costPriceInput.value) || 0;
const profitMarginPercentage = parseFloat(profitMarginInput.value) || 0;
const vatRatePercentage = parseFloat(vatRateInput.value) || 0;
const profitMarginDecimal = profitMarginPercentage / 100;
const vatRateDecimal = vatRatePercentage / 100;
const sellingPrice = costPrice * (1 + profitMarginDecimal);
const vatAmount = sellingPrice * vatRateDecimal;
const totalAmount = sellingPrice + vatAmount;
sellingPriceDisplay.value = sellingPrice.toFixed(2);
vatAmountDisplay.value = vatAmount.toFixed(2);
totalDisplay.value = totalAmount.toFixed(2);
}
// Initial calculation
calculateFinanceDetails();
// Event listeners
costPriceInput.addEventListener('input', calculateFinanceDetails);
profitMarginInput.addEventListener('input', calculateFinanceDetails);
vatRateInput.addEventListener('input', calculateFinanceDetails);
});
</script>
{% endblock %} {% endblock %}

View File

@ -112,10 +112,12 @@
<div class="col-lg-4 col-xl-3"> <div class="col-lg-4 col-xl-3">
<div class="card h-100 border-1 rounded shadow"> <div class="card h-100 border-1 rounded shadow">
<div class="card-body" id="year-container"> <div class="card-body" id="year-container">
<label class="form-label" <label class="form-label"
for="{{ form.year.id_for_label }}"> for="{{ form.year.id_for_label }}">
{% trans 'Year' %}: {% trans 'Year' %}:
</label> </label>
<span class="text-success fw-bold" id="year-check"></span>
<input type="number" <input type="number"
class="form-control form-control-sm" class="form-control form-control-sm"
id="{{ form.year.id_for_label }}" id="{{ form.year.id_for_label }}"
@ -132,10 +134,12 @@
<div class="col-lg-4 col-xl-3"> <div class="col-lg-4 col-xl-3">
<div class="card h-100 border-1 rounded shadow"> <div class="card h-100 border-1 rounded shadow">
<div id="make-container" class="card-body"> <div id="make-container" class="card-body">
<div class="status"></div>
<label class="form-label" <label class="form-label"
for="{{ form.id_car_make.id_for_label }}"> for="{{ form.id_car_make.id_for_label }}">
{% trans 'make'|capfirst %}: {% trans 'make'|capfirst %}:
</label> </label>
<span class="text-success fw-bold" id="make-check"></span>
{{ form.id_car_make|add_class:"form-select form-select-sm" }} {{ form.id_car_make|add_class:"form-select form-select-sm" }}
{% if form.id_car_make.errors %} {% if form.id_car_make.errors %}
<div class="text-danger small"> <div class="text-danger small">
@ -154,6 +158,7 @@
for="{{ form.id_car_model.id_for_label }}"> for="{{ form.id_car_model.id_for_label }}">
{% trans 'model'|capfirst %}: {% trans 'model'|capfirst %}:
</label> </label>
<span class="text-success fw-bold" id="model-check"></span>
<select class="form-select form-select-sm" <select class="form-select form-select-sm"
id="{{ form.id_car_model.id_for_label }}" id="{{ form.id_car_model.id_for_label }}"
name="{{ form.id_car_model.html_name }}"> name="{{ form.id_car_model.html_name }}">
@ -307,18 +312,30 @@
</div> </div>
<!-- Specifications Buttons --> <!-- Specifications Buttons -->
<div class="col-lg-4 col-xl-6 justify-content-between"> <div class="row g-1">
<button type="button" <div class="btn-group">
class="btn btn-danger mt-1" <button type="button"
class="btn btn-sm btn-danger me-1"
id="specification-btn" id="specification-btn"
data-bs-toggle="modal" data-bs-toggle="modal"
data-bs-target="#specificationsModal" data-bs-target="#specificationsModal"
disabled>{% trans 'specifications'|capfirst %} disabled>{% trans 'specifications'|capfirst %}
</button> </button>
<button type="submit" <!--<div class="form-group">-->
class="btn btn-primary mt-1" <button type="submit"
id="saveCarBtn" disabled>{% trans 'Save' %} name="add_another"
</button> value="true"
class="btn btn-sm btn-success me-1">
{% trans "Save and Add Another" %}
</button>
<button type="submit"
name="go_to_stats"
value="true"
class="btn btn-sm btn-primary">
{% trans "Save and Go to Inventory" %}
</button>
<!--</div>-->
</div>
</div> </div>
</main> </main>
</div> </div>
@ -364,7 +381,7 @@ document.addEventListener("DOMContentLoaded", function() {
const yearBg = document.getElementById('year-container'); const yearBg = document.getElementById('year-container');
const serieBg = document.getElementById('serie-container'); const serieBg = document.getElementById('serie-container');
const trimBg = document.getElementById('trim-container'); const trimBg = document.getElementById('trim-container');
const saveCarBtn = document.getElementById('saveCarBtn'); /*const saveCarBtn = document.getElementById('saveCarBtn');*/
const ajaxUrl = "{% url 'ajax_handler' %}"; const ajaxUrl = "{% url 'ajax_handler' %}";
@ -373,7 +390,8 @@ document.addEventListener("DOMContentLoaded", function() {
const videoElement = document.getElementById('video'); const videoElement = document.getElementById('video');
const resultDisplay = document.getElementById('result'); const resultDisplay = document.getElementById('result');
const fallbackButton = document.getElementById('ocr-fallback-btn'); const fallbackButton = document.getElementById('ocr-fallback-btn');
const codeReader = new ZXing.BrowserMultiFormatReader(); let codeReader;
codeReader = new ZXing.BrowserMultiFormatReader();
let currentStream = null; let currentStream = null;
function showLoading() { function showLoading() {
@ -439,16 +457,19 @@ async function updateFields(vinData) {
console.log(vinData) console.log(vinData)
if (vinData.make_id) { if (vinData.make_id) {
makeSelect.value = vinData.make_id; makeSelect.value = vinData.make_id;
document.getElementById("make-check").innerHTML = '&#10003;';
await loadModels(vinData.make_id); await loadModels(vinData.make_id);
} }
if (vinData.model_id) { if (vinData.model_id) {
modelSelect.value = vinData.model_id; modelSelect.value = vinData.model_id;
document.getElementById("model-check").innerHTML = '&#10003;';
await loadSeries(vinData.model_id); await loadSeries(vinData.model_id);
} }
if (vinData.year) { if (vinData.year) {
yearSelect.value = vinData.year; yearSelect.value = vinData.year;
document.getElementById("year-check").innerHTML = '&#10003;';
} }
checkFormCompletion(); /*checkFormCompletion();*/
} }
// Start the scanner // Start the scanner
@ -489,12 +510,12 @@ function stopScanner() {
function resetDropdown(dropdown, placeholder) { function resetDropdown(dropdown, placeholder) {
dropdown.innerHTML = `<option value="">${placeholder}</option>`; dropdown.innerHTML = `<option value="">${placeholder}</option>`;
} }
/*
function checkFormCompletion() { function checkFormCompletion() {
const isFormComplete = vinInput.value.length === 17 && stockTypeSelect.value; const isFormComplete = vinInput.value.length === 17 && stockTypeSelect.value;
saveCarBtn.disabled = !isFormComplete; saveCarBtn.disabled = !isFormComplete;
} }
*/
async function loadModels(makeId) { async function loadModels(makeId) {
resetDropdown(modelSelect, '{% trans "Select" %}'); resetDropdown(modelSelect, '{% trans "Select" %}');
const response = await fetch(`${ajaxUrl}?action=get_models&make_id=${makeId}`, { const response = await fetch(`${ajaxUrl}?action=get_models&make_id=${makeId}`, {
@ -551,6 +572,7 @@ async function loadTrims(serie_id, model_id) {
} }
async function loadSpecifications(trimId){ async function loadSpecifications(trimId){
specificationsContent.innerHTML = '';
const response = await fetch(`${ajaxUrl}?action=get_specifications&trim_id=${trimId}`, { const response = await fetch(`${ajaxUrl}?action=get_specifications&trim_id=${trimId}`, {
headers: { headers: {
'X-Requested-With': 'XMLHttpRequest', 'X-Requested-With': 'XMLHttpRequest',
@ -592,9 +614,9 @@ async function loadSpecifications(trimId){
}); });
closeButton.addEventListener('click', closeModal); closeButton.addEventListener('click', closeModal);
stockTypeSelect.addEventListener('change', checkFormCompletion); /*stockTypeSelect.addEventListener('change', checkFormCompletion);
mileageInput.addEventListener('input', checkFormCompletion); mileageInput.addEventListener('input', checkFormCompletion);
remarksInput.addEventListener('input', checkFormCompletion); remarksInput.addEventListener('input', checkFormCompletion);*/
decodeVinBtn.addEventListener('click', decodeVin); decodeVinBtn.addEventListener('click', decodeVin);
}); });
</script> </script>

View File

@ -91,9 +91,9 @@
{% for choice in form.color.field.choices %} {% for choice in form.color.field.choices %}
<div class="col-lg-3 col-xl-3 text-center"> <div class="col-lg-3 col-xl-3 text-center">
<label class="color-option" id="color-option-{{ forloop.counter }}"> <label class="color-option" id="color-option-{{ forloop.counter }}">
<input type="radio" name="color" value="{{ choice.0 }}" <input type="radio" name="color" value="{{ choice.0 }}"
{% if choice.0 == form.color.value %}checked{% endif %} {% if choice.0 == form.color.value %}checked{% endif %}
style="display: none;" style="display: none;"
onchange="highlightSelectedColor({{ forloop.counter }})"> onchange="highlightSelectedColor({{ forloop.counter }})">
<div class="color-box" style="background-color: rgb({{ choice.0 }});"></div> <div class="color-box" style="background-color: rgb({{ choice.0 }});"></div>
<span><small class="fw-lighter">{{ choice.1 }}</small></span> <span><small class="fw-lighter">{{ choice.1 }}</small></span>

View File

@ -0,0 +1,82 @@
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<div class="container mt-4">
<div class="card">
<div class="card-header">
<h4>{% trans "Quotation Details" %} - {{ quotation.id }}</h4>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<h5>{% trans "Customer Details" %}</h5>
<p>
<strong>{% trans "Name" %}:</strong>
{{ quotation.customer.first_name }} {{ quotation.customer.last_name_name }}</p>
<p><strong>{% trans "Address" %}:</strong> {{ quotation.customer.address }}</p>
<p><strong>{% trans "VAT No" %}:</strong> {{ quotation.customer.vat_number }}</p>
</div>
<div class="col-md-6">
<h5>{% trans "Quotation Information" %}</h5>
<p><strong>{% trans "Quotation No" %}:</strong> {{ quotation.id }}</p>
<p><strong>{% trans "Date" %}:</strong> {{ quotation.created_at|date }}</p>
<p><strong>{% trans "Remarks" %}:</strong> {{ quotation.remarks }}</p>
</div>
</div>
<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 Before 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.total_vat_amount }}</td>
<td>{{ item.car.total_before_vat }}</td>
<td>{{ item.car.total }}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<th colspan="3">Totals</th>
<th>{{ item.car.total_vat_amount }}</th>
<th>{{ item.car.total_before_vat }}</th>
<th>{{ item.car.total }}</th>
</tr>
</tfoot>
</table>
<h5 class="mt-4">{% trans "Summary" %}</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>
</table>
</div>
<div class="card-footer text-end">
<a href="{% url 'quotation_list' %}" class="btn btn-secondary">{% trans "Back to Quotations" %}</a>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,23 @@
{% extends "base.html" %}
{% load crispy_forms_filters %}
{% load i18n static %}
{% block title %}{{ _("Create Quotation") }}{% endblock title %}
{% block content %}
<div class="container mt-4">
<h3 class="text-center">{% trans "Create Quotation" %}</h3>
<form method="post" class="needs-validation" novalidate>
{% csrf_token %}
<div class="row g-3">
{{ form|crispy }}
</div>
<!-- Buttons -->
<div class="mt-4 text-center">
<button type="submit" class="btn btn-success me-2">{% trans "Save" %}</button>
<a href="{% url 'quotation_list' %}" class="btn btn-secondary">{% trans "Cancel" %}</a>
</div>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,48 @@
{% extends "base.html" %}
{% load i18n static %}
{% block title %}{{ _("Quotations") }}{% endblock title %}
{% block content %}
<div class="container mt-4">
<h3 class="text-center">{% trans "Quotations" %}</h3>
<div class="table-responsive">
<table class="table table-striped align-middle">
<thead>
<tr>
<th>#</th>
<th>{% trans "Customer" %}</th>
<th>{% trans "Total Cars" %}</th>
<th>{% trans "Total Amount" %}</th>
<th>{% trans "Created At" %}</th>
<th>{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for quotation in quotations %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ quotation.customer.get_full_name }}</td>
<td>{{ quotation.quotation_cars.count }}</td>
<td>{{ quotation.total }}</td>
<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" %}
</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="6" class="text-center">{% trans "No Quotations Found" %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="d-flex justify-content-center">
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,39 @@
{% extends "base.html" %}
{% block title %}{{ _("Sales Order Details") }}{% endblock title %}
{% block content %}
<div class="container">
<h2>{{ _("Sales Order Details") }}</h2>
<table>
<tr>
<th>{{ _("Quotation ID") }}</th>
<td>{{ sales_order.quotation.id }}</td>
</tr>
<tr>
<th>{{ _("Customer") }}</th>
<td>{{ sales_order.quotation.customer }}</td>
</tr>
<tr>
<th>{{ _("Total Amount") }}</th>
<td>{{ sales_order.total_amount }}</td>
</tr>
</table>
<h3>{{ _("Cars in Sales Order") }}</h3>
<table>
<tr>
<th>{{ _("Car") }}</th>
<th>{{ _("Selling Price") }}</th>
<th>{{ _("VAT Amount") }}</th>
<th>{{ _("Total Amount") }}</th>
</tr>
{% for car in sales_order.quotation.quotation_cars.all %}
<tr>
<td>{{ car.car }}</td>
<td>{{ car.selling_price }}</td>
<td>{{ car.vat_amount }}</td>
<td>{{ car.total_amount }}</td>
</tr>
{% endfor %}
</table>
</div>
{% endblock %}

View File

@ -1,134 +1,110 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load i18n %} {% load i18n %}
{% load static %} {% load static %}
{% block title %}{% trans 'Vendors'|capfirst %}{% endblock title %} {% block title %}{{ _('Vendors')|capfirst }}{% endblock title %}
{% block vendors %}<a class="nav-link active">{% trans "Vendors"|capfirst %}</a>{% endblock %} {% block vendors %}<a class="nav-link active">{{ _("Vendors")|capfirst }}</a>{% endblock %}
{% block content %} {% block content %}
<div class="container-fluid p-3">
<div class="container-fluid"> <div class="card shadow-sm">
<div class="card mb-3"> <div class="card-header d-flex justify-content-between align-items-center">
<div class="card-header fw-light mb-0"> <h6 class="mb-0">{{ _("Vendors")|capfirst }}</h6>
{% include 'breadcrumbs.html' %} <form method="get" class="d-inline-block">
</div>
<div class="card-body">
<div class="container-fluid p-2">
<form method="get">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<button id="inputGroup-sizing-sm" <button class="btn btn-secondary" type="submit">
class="btn btn-sm btn-secondary rounded-start" type="submit"> {{ _("Search")|capfirst }}
{% trans 'search'|capfirst %}
</button> </button>
<input type="text" <input type="text"
name="q" name="q"
class="form-control form-control-sm rounded-end" class="form-control"
value="{{ request.GET.q }}" placeholder="{{ _('Enter vendor name') }}"
aria-describedby="inputGroup-sizing-sm"/> value="{{ request.GET.q }}">
<!-- Clear Button --> {% if request.GET.q %}
{% if request.GET.q %} <a href="{% url request.resolver_match.view_name %}" class="btn btn-outline-danger ms-1">
<a href="{% url request.resolver_match.view_name %}" <i class="bi bi-x-lg"></i>
class="btn btn-sm btn-outline-danger ms-1 rounded"> </a>
<i class="bi bi-x-lg"></i> {% endif %}
</a>
{% endif %}
</div> </div>
</form> </form>
</div> </div>
<table class="table table-hover table-responsive-sm"> <div class="card-body p-0">
<thead> <table class="table table-hover table-striped mb-0">
<tr> <thead class="table-light">
<th>{% trans 'name'|capfirst %}</th> <tr>
<th>{{ _("Name")|capfirst }}</th>
<th>{% trans 'logo'|capfirst %}</th> <th>{{ _("Logo")|capfirst }}</th>
<th>{% trans 'address'|capfirst %}</th> <th>{{ _("Address")|capfirst }}</th>
<th>{% trans 'actions'|capfirst %}</th> <th>{{ _("Actions")|capfirst }}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% if page_obj.object_list %} {% if page_obj.object_list %}
{% for vendor in page_obj.object_list %} {% for vendor in vendors %}
<tr> <tr>
<td> <td>{{ vendor.get_local_name }}</td>
{{ vendor.get_local_name }} <td>
</td> <img src="{{ vendor.logo.url }}"
<td> alt="{{ vendor.get_local_name }}"
{% if vendor.logo %} class="img-thumbnail"
<img src="{% static 'images/' %}{{ vendor.logo }}" width="100" height="30"> style="max-width: 100px;">
</td>
<td>{{ vendor.address }}</td>
<td>
<a href="{% url 'vendor_detail' vendor.id %}" class="btn btn-warning btn-sm">
{{ _("View")|capfirst }}
</a>
</td>
</tr>
{% endfor %}
{% else %} {% else %}
<img src="{% static 'images/logos/default_no_logo.png' %}" width="100" height="30"> <tr>
<td colspan="4" class="text-center">{{ _("No vendors found")|capfirst }}</td>
</tr>
{% endif %} {% endif %}
</td> </tbody>
<td>{{ vendor.address }}</td> </table>
<td>
<a class="btn btn-sm btn-warning" href="{% url 'vendor_detail' vendor.id %}">{% trans 'view'|capfirst %}</a>
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td class="border-0" colspan="6">{% trans 'no vendors found'|capfirst %}</td>
</tr>
{% endif %}
</tbody>
</table>
<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="?{% if request.GET.q %}q={{ request.GET.q }}&{% endif %}page=1" aria-label={% trans 'first'|capfirst %}>
<span aria-hidden="true">&laquo;&laquo;</span>
</a>
</li>
<li class="page-item">
<a class="page-link" href="?{% if request.GET.q %}q={{ request.GET.q }}&{% endif %}page={{ page_obj.previous_page_number }}" aria-label={% trans 'previous'|capfirst %}>
<span aria-hidden="true">&laquo;</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link" aria-hidden="true">&laquo;&laquo;</span>
</li>
<li class="page-item disabled">
<span class="page-link" aria-hidden="true">&laquo;</span>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active">
<span class="page-link">{{ num }}</span>
</li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?{% if request.GET.q %}q={{ request.GET.q }}&{% endif %}page={{ num }}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?{% if request.GET.q %}q={{ request.GET.q }}&{% endif %}page={{ page_obj.next_page_number }}" aria-label={% trans 'next'|capfirst %}>
<span aria-hidden="true">&raquo;</span>
</a>
</li>
<li class="page-item">
<a class="page-link" href="?{% if request.GET.q %}q={{ request.GET.q }}&{% endif %}page={{ page_obj.paginator.num_pages }}" aria-label={% trans 'last'|capfirst %}>
<span aria-hidden="true">&raquo;&raquo;</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link" aria-hidden="true">&raquo;</span>
</li>
<li class="page-item disabled">
<span class="page-link" aria-hidden="true">&raquo;&raquo;</span>
</li>
{% endif %}
</ul>
</nav>
</div> </div>
</div> </div>
<!-- Optional: Pagination -->
{% if is_paginated %}
<nav aria-label="Page navigation">
<ul class="pagination pagination-sm justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item py-0">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active"><a class="page-link" href="?page={{ num }}">{{ num }}</a></li>
{% else %}
<li class="page-item"><a class="page-link" href="?page={{ num }}">{{ num }}</a></li>
{% endif %}
{% endfor %} {% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,23 +1,23 @@
from vin import VIN # from vin import VIN
import requests import requests
import json import json
#
vin_no = 'VR7ED9HP6SJ522156' # vin_no = 'VR7ED9HP6SJ522156'
#
details = { # details = {
# 'Description': VIN(vin_no).description, # # 'Description': VIN(vin_no).description,
'make': VIN(vin_no).make, # 'make': VIN(vin_no).make,
'year': VIN(vin_no).model_year, # 'year': VIN(vin_no).model_year,
'model': VIN(vin_no).model, # 'model': VIN(vin_no).model,
'trim': VIN(vin_no).trim, # 'trim': VIN(vin_no).trim,
'Series': VIN(vin_no).series, # 'Series': VIN(vin_no).series,
'body_class': VIN(vin_no).body_class, # 'body_class': VIN(vin_no).body_class,
'Type': VIN(vin_no).vehicle_type, # 'Type': VIN(vin_no).vehicle_type,
'Electrification level': VIN(vin_no).electrification_level, # 'Electrification level': VIN(vin_no).electrification_level,
} # }
#
print(details) # print(details)
# from vininfo import Vin # from vininfo import Vin
# #
@ -120,3 +120,11 @@ print(details)
# #
# except Exception as e: # except Exception as e:
# print(e) # print(e)
vin_no = "LS5A3DKR0SA966230"
url = "https://vin17.com/Search/query?vin=+"+vin_no
response = requests.request("GET", url)
car_info = json.loads(response.text)
print(car_info)