add qotation
# Conflicts: # car_inventory/settings.py # inventory/models.py # inventory/services.py # inventory/urls.py # inventory/views.py
@ -26,7 +26,7 @@ SECRET_KEY = 'django-insecure-gc9bh4*3=b6hihdnaom0edjsbxh$5t)aap@e8p&340r7)*)qb8
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = ['10.10.1.109','10.10.1.120', 'localhost', '127.0.0.1', '192.168.1.135', '172.20.10.4']
|
||||
ALLOWED_HOSTS = ['10.10.1.109', 'localhost', '127.0.0.1', '192.168.1.135', '172.20.10.4']
|
||||
|
||||
# Application definition
|
||||
|
||||
@ -110,9 +110,9 @@ WSGI_APPLICATION = 'car_inventory.wsgi.application'
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django_prometheus.db.backends.postgresql",
|
||||
"NAME": "haikal",
|
||||
"USER": "haikal",
|
||||
"PASSWORD": "haikal",
|
||||
"NAME": "haikal_app",
|
||||
"USER": "f95166",
|
||||
"PASSWORD": "Kfsh&rc9788",
|
||||
"HOST": "localhost",
|
||||
"PORT": 5432,
|
||||
}
|
||||
|
||||
69
carapi.py
Normal 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)
|
||||
79
haikalna.py
@ -6,23 +6,24 @@ from typing import List
|
||||
|
||||
def vin_years(vin_char: str) -> List[int]:
|
||||
letters = 'ABCDEFGHJKLMNPRSTVWXY123456789'
|
||||
year_char = vin_char.upper()
|
||||
|
||||
if year_char not in letters:
|
||||
raise ValueError("Invalid VIN character. Must be A-Z (excluding I, O, Q) or 1-9.")
|
||||
year_letter = vin_char
|
||||
|
||||
year = 1979
|
||||
current_year = datetime.now().year
|
||||
year_current = datetime.now().year
|
||||
|
||||
result = []
|
||||
|
||||
for letter in cycle(letters):
|
||||
year += 1
|
||||
if letter == year_char:
|
||||
|
||||
if letter == year_letter:
|
||||
result.append(year)
|
||||
if year >= current_year:
|
||||
|
||||
if year == year_current:
|
||||
break
|
||||
|
||||
result.sort(reverse=True)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@ -150,7 +151,6 @@ wmi_manufacturer_mapping = {
|
||||
"J8T": "GMC",
|
||||
"J8Z": "Chevrolet",
|
||||
"KF3": "Merkavim",
|
||||
"KF6": "Automotive Industries,",
|
||||
"KL": "Daewoo",
|
||||
"KLA": "Daewoo",
|
||||
"KLP": "CT&T United",
|
||||
@ -190,9 +190,9 @@ wmi_manufacturer_mapping = {
|
||||
"KPT": "SsangYong",
|
||||
"LA6": "King Long",
|
||||
"LC0": "BYD",
|
||||
"LBE": "Beijing-Hyundai Shouwang",
|
||||
"LBE": "Hyundai Shouwang",
|
||||
"LBM": "Zongshen Piaggio",
|
||||
"LBV": "BMW Brilliance",
|
||||
"LBV": "BMW",
|
||||
"LB1": "Fujian Benz",
|
||||
"LB3": "Geely",
|
||||
"LCR": "Gonow",
|
||||
@ -222,17 +222,15 @@ wmi_manufacturer_mapping = {
|
||||
"LGL": "Guilin Daewoo",
|
||||
"LGW": "Great Wall",
|
||||
"LGX": "BYD",
|
||||
"LGZ": "Guangzhou Denway Bus",
|
||||
"LHA": "Shuanghuan",
|
||||
"LHB": "BAIC",
|
||||
"LHG": "Guangzhou Honda",
|
||||
"LH1": "FAW",
|
||||
"LJC": "Jincheng",
|
||||
"LJD": "Human Horizons - HiPhi",
|
||||
"LJD": "Yueda Kia",
|
||||
"LJN": "Zhengzhou Nissan",
|
||||
"LJS": "Yaxing Coach",
|
||||
"LJU": "Lotus Geely",
|
||||
"LJV": "Chengdu Wangpai",
|
||||
"LJX": "JMC Ford",
|
||||
"LJ1": "Nio",
|
||||
"LJ8": "Zotye",
|
||||
@ -260,7 +258,7 @@ wmi_manufacturer_mapping = {
|
||||
"LPE": "BYD",
|
||||
"LPS": "Polestar",
|
||||
"LRB": "SAIC Buick",
|
||||
"LRD": "Foton Daimler",
|
||||
"LRD": "Foton",
|
||||
"LRE": "SAIC Cadillac",
|
||||
"LRW": "Tesla",
|
||||
"LSC": "Changan",
|
||||
@ -270,15 +268,14 @@ wmi_manufacturer_mapping = {
|
||||
"LSJ": "SAIC MG",
|
||||
"LSK": "SAIC Maxus",
|
||||
"LSV": "SAIC Volkswagen",
|
||||
"LSY": "Brilliance Jinbei GM",
|
||||
"LSY": "Brilliance",
|
||||
"LS4": "Changan",
|
||||
"LS5": "Changan",
|
||||
"LS6": "Changan",
|
||||
"LS7": "JMC",
|
||||
"LTA": "ZX Auto",
|
||||
"LTN": "Soueast",
|
||||
"LTP": "National Electric Vehicle Sweden AB",
|
||||
"LTV": "FAW Toyota (Tianjin)",
|
||||
"LTV": "FAW Toyota",
|
||||
"LUC": "Honda",
|
||||
"LUD": "Dongfeng Nissan",
|
||||
"LUX": "Dongfeng Yulon",
|
||||
@ -322,18 +319,15 @@ wmi_manufacturer_mapping = {
|
||||
"L4F": "Suzhou Eagle",
|
||||
"L5C": "KangDi",
|
||||
"L5K": "Yongkang",
|
||||
"L6T": "Geely, Lynk & Co, Zeekr",
|
||||
"L6T": "Lynk & Co",
|
||||
"L82": "Baotian",
|
||||
"L85": "Yongkang Huabao",
|
||||
"L9N": "Taotao",
|
||||
"MAB": "Mahindra & Mahindra",
|
||||
"MAC": "Mahindra & Mahindra",
|
||||
"MAH": "Fiat",
|
||||
"MAJ": "Ford",
|
||||
"MAK": "Honda",
|
||||
"MAL": "Hyundai",
|
||||
"MAT": "Tatar",
|
||||
"MA1": "Mahindra & Mahindra",
|
||||
"MA3": "Suzuki",
|
||||
"MA6": "GM",
|
||||
"MA7": "Hindustan",
|
||||
@ -348,10 +342,7 @@ wmi_manufacturer_mapping = {
|
||||
"MBX": "Piaggio",
|
||||
"MBY": "Asia Motors",
|
||||
"MB1": "Ashok Leyland",
|
||||
"MB7": "Reva Electric Company",
|
||||
"MCA": "FCA Pvt. Ltd",
|
||||
"MCB": "GM",
|
||||
"MCD": "Mahindra Two Wheelers",
|
||||
"MCG": "Atul",
|
||||
"MC1": "Force",
|
||||
"MC2": "Eicher",
|
||||
@ -361,7 +352,7 @@ wmi_manufacturer_mapping = {
|
||||
"MD6": "TVS",
|
||||
"MD9": "Shuttles",
|
||||
"MEC": "Daimler",
|
||||
"MEE": "Renault Private Limited",
|
||||
"MEE": "Renault",
|
||||
"MEG": "Harley-Davidson",
|
||||
"MER": "Benelli",
|
||||
"MET": "Piaggio",
|
||||
@ -502,7 +493,7 @@ wmi_manufacturer_mapping = {
|
||||
"SAJ": "Jaguar",
|
||||
"SAL": "Land Rover",
|
||||
"SAM": "Morris",
|
||||
"SAR": "Rover MG Rover",
|
||||
"SAR": "Rover MG",
|
||||
"SAT": "Triumph",
|
||||
"SAX": "Austin-Rover",
|
||||
"SAZ": "Freight Rover",
|
||||
@ -513,7 +504,6 @@ wmi_manufacturer_mapping = {
|
||||
"SBL": "Leyland",
|
||||
"SBM": "McLaren",
|
||||
"SBS": "Scammell",
|
||||
"SBV": "Kenworth Peterbilt",
|
||||
"SB1": "Toyota",
|
||||
"SCA": "Rolls Royce",
|
||||
"SCB": "Bentley",
|
||||
@ -568,13 +558,13 @@ wmi_manufacturer_mapping = {
|
||||
"TN9": "Karosa",
|
||||
"TRA": "Ikarus Bus",
|
||||
"TRC": "Csepel",
|
||||
"TRU": "Audi Hungary",
|
||||
"TRU": "Audi",
|
||||
"TSB": "Ikarus Bus",
|
||||
"TSE": "Ikarus,",
|
||||
"TSF": "Alfabusz",
|
||||
"TSM": "Suzuki Fiat",
|
||||
"TWG": "Ceatano Bus",
|
||||
"TW1": "Toyota Caetano",
|
||||
"TW1": "Toyota",
|
||||
"TW2": "Ford",
|
||||
"TW6": "Citroën",
|
||||
"TW7": "Mini",
|
||||
@ -670,7 +660,6 @@ wmi_manufacturer_mapping = {
|
||||
"WBU": "Bürstner",
|
||||
"WBX": "BMW",
|
||||
"WBY": "BMW",
|
||||
"WB0": "Böckmann Fahrzeugwerke GmbH",
|
||||
"WB1": "BMW",
|
||||
"WB5": "BMW",
|
||||
"WCD": "Freightliner",
|
||||
@ -691,14 +680,11 @@ wmi_manufacturer_mapping = {
|
||||
"WD6": "Freightliner",
|
||||
"WD8": "Dodge",
|
||||
"WEB": "Evobus GmbH",
|
||||
"WEL": "e.GO Mobile AG",
|
||||
"WFC": "Fendt",
|
||||
"WFD": "Fliegl Trailer",
|
||||
"WF0": "Ford",
|
||||
"WF1": "Merkur",
|
||||
"WHB": "Hobby",
|
||||
"WHD": "Humbaur GmbH",
|
||||
"WHW": "Hako GmbH",
|
||||
"WHY": "Hymer",
|
||||
"WJM": "Iveco",
|
||||
"WJR": "Irmscher",
|
||||
@ -760,12 +746,10 @@ wmi_manufacturer_mapping = {
|
||||
"XMC": "Mitsubishi",
|
||||
"XMD": "Mitsubishi",
|
||||
"XMG": "VDL Bus",
|
||||
"XMR": "Nooteboom Trailers",
|
||||
"XMR": "Nooteboom",
|
||||
"XM4": "RAVO Holding",
|
||||
"XNB": "Mitsubishi",
|
||||
"XNC": "Mitsubishi",
|
||||
"XNL": "VDL Bus & Coach",
|
||||
"XPN": "Knapen Trailers",
|
||||
"XP7": "Tesla",
|
||||
"XTA": "Lada",
|
||||
"XTB": "Moskvitch",
|
||||
@ -803,7 +787,6 @@ wmi_manufacturer_mapping = {
|
||||
"X9X": "Great Wall",
|
||||
"YAF": "Faymonville",
|
||||
"YAR": "Toyota",
|
||||
"YA9": "Lambrecht Constructie NV",
|
||||
"YBW": "Volkswagen",
|
||||
"YB1": "Volvo",
|
||||
"YB2": "Volvo",
|
||||
@ -812,7 +795,7 @@ wmi_manufacturer_mapping = {
|
||||
"YH1": "Solifer",
|
||||
"YH2": "BRP",
|
||||
"YH4": "Fisker",
|
||||
"YK1": "Saab-Valmet",
|
||||
"YK1": "Saab",
|
||||
"YSC": "Cadillac",
|
||||
"YSM": "Polestars",
|
||||
"YSP": "Volta AB",
|
||||
@ -827,7 +810,6 @@ wmi_manufacturer_mapping = {
|
||||
"YV3": "Volvos",
|
||||
"YV4": "Volvo",
|
||||
"YYC": "Think Nordic",
|
||||
"Y3J": "Belkommunmash",
|
||||
"Y3M": "MAZ",
|
||||
"Y4F": "Ford",
|
||||
"Y4K": "Geely",
|
||||
@ -841,19 +823,19 @@ wmi_manufacturer_mapping = {
|
||||
"Y8A": "LAZ",
|
||||
"Y9H": "LAZ",
|
||||
"ZAA": "Autobianchi",
|
||||
"ZAC": "Jeep Dodge Hornet",
|
||||
"ZAC": "Jeep Dodge",
|
||||
"ZAM": "Maserati",
|
||||
"ZAP": "Piaggio",
|
||||
"ZAR": "Alfa Romeo",
|
||||
"ZAS": "Alfa Romeo",
|
||||
"ZBB": "Bertone",
|
||||
"ZBN": "Benelli",
|
||||
"ZBW": "Rayton-Fissore Magnum",
|
||||
"ZBW": "Rayton-Fissore",
|
||||
"ZCB": "Bartoletti",
|
||||
"ZCF": "Iveco",
|
||||
"ZCG": "Cagiva",
|
||||
"ZCM": "Menarinibus",
|
||||
"ZC2": "Chrysler Maserati",
|
||||
"ZC2": "Chrysler",
|
||||
"ZDC": "Honda",
|
||||
"ZDF": "Ferrari",
|
||||
"ZDM": "Ducati",
|
||||
@ -895,8 +877,6 @@ wmi_manufacturer_mapping = {
|
||||
"Z8T": "PCMA",
|
||||
"Z9M": "Mercedes-Benz",
|
||||
"Z94": "Hyundai",
|
||||
"1AC": "American Corporation",
|
||||
"1AF": "American LaFrance",
|
||||
"1B3": "Dodge",
|
||||
"1B4": "Dodge",
|
||||
"1B7": "Dodge",
|
||||
@ -946,7 +926,7 @@ wmi_manufacturer_mapping = {
|
||||
"1GY": "Cadillac",
|
||||
"1HD": "Harley-Davidson",
|
||||
"1HG": "Honda",
|
||||
"1HS": "International & Caterpillar",
|
||||
"1HS": "Caterpillar",
|
||||
"1JC": "Jeep",
|
||||
"1JT": "Jeep",
|
||||
"1JU": "Marmon",
|
||||
@ -969,10 +949,7 @@ wmi_manufacturer_mapping = {
|
||||
"1P3": "Plymouth",
|
||||
"1P4": "Plymouth",
|
||||
"1PY": "John Deere",
|
||||
"1T7": "Thomas Built Buses",
|
||||
"1T8": "Thomas Built Buses",
|
||||
"1TC": "Coachmen",
|
||||
"1TU": "Transportation Manufacturing Corporation",
|
||||
"1UJ": "Jayco",
|
||||
"1UT": "Jeep",
|
||||
"1VH": "Orion Bus",
|
||||
@ -993,8 +970,6 @@ wmi_manufacturer_mapping = {
|
||||
"10T": "Oshkosh",
|
||||
"12A": "Avanti",
|
||||
"137": "Hummer",
|
||||
"15G": "Gillig bus",
|
||||
"16C": "Clenet Coachworks",
|
||||
"16X": "Vixen",
|
||||
"19U": "Acura",
|
||||
"19V": "Acura",
|
||||
@ -1549,7 +1524,7 @@ def decode_vin(vin):
|
||||
return {
|
||||
'VIN': vin,
|
||||
'Manufacturer': manufacturer,
|
||||
'Year': year[0],
|
||||
'Year': year,
|
||||
'Model': model
|
||||
}
|
||||
|
||||
@ -1558,7 +1533,7 @@ def decode_vin(vin):
|
||||
# VR3USHNLWRJ521303
|
||||
# KNARH81E8P5194005
|
||||
# Example usage
|
||||
vin_number = 'LJD5AA1DXR0104257'
|
||||
vin_number = 'LGWCBE196SB652802'
|
||||
decoded_vin = decode_vin(vin_number)
|
||||
print(decoded_vin)
|
||||
|
||||
|
||||
@ -5,17 +5,22 @@ from . import models
|
||||
admin.site.register(models.Dealer)
|
||||
admin.site.register(models.Vendor)
|
||||
admin.site.register(models.Customer)
|
||||
admin.site.register(models.SaleQuotation)
|
||||
admin.site.register(models.SaleQuotationCar)
|
||||
admin.site.register(models.Car)
|
||||
admin.site.register(models.CarFinance)
|
||||
admin.site.register(models.CarColors)
|
||||
admin.site.register(models.CarRegistration)
|
||||
admin.site.register(models.CustomCard)
|
||||
admin.site.register(models.CarSpecificationValue)
|
||||
admin.site.register(models.ExteriorColors)
|
||||
admin.site.register(models.InteriorColors)
|
||||
|
||||
@admin.register(models.CarMake)
|
||||
class CarMakeAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'arabic_name', 'is_sa_import')
|
||||
search_fields = ('name', 'arabic_name')
|
||||
list_filter = ('is_sa_import', 'name',)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Car Make"
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
from django import forms
|
||||
from .mixins import AddClassMixin
|
||||
from django.forms.models import inlineformset_factory
|
||||
from .models import (
|
||||
Dealer,
|
||||
# Branch,
|
||||
@ -9,9 +10,12 @@ from .models import (
|
||||
CarFinance,
|
||||
CustomCard,
|
||||
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 _
|
||||
|
||||
|
||||
@ -19,7 +23,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
class DealerForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Dealer
|
||||
fields = ['crn', 'vrn', 'arabic_name', 'name', 'phone_number', 'address', 'logo']
|
||||
fields = ['name', 'arabic_name', 'crn', 'vrn', 'phone_number', 'address', 'logo']
|
||||
|
||||
|
||||
# Customer Form
|
||||
@ -87,57 +91,10 @@ class CarUpdateForm(forms.ModelForm, AddClassMixin):
|
||||
|
||||
|
||||
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:
|
||||
model = CarFinance
|
||||
fields = ['cost_price']
|
||||
|
||||
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
|
||||
exclude = ['car', 'profit_margin', 'vat_amount', 'total', 'vat_rate']
|
||||
|
||||
|
||||
# Custom Card Form
|
||||
@ -165,3 +122,52 @@ class VendorForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Vendor
|
||||
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()
|
||||
|
||||
100
inventory/management/commands/populate_colors.py
Normal 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.")
|
||||
@ -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 inventory.mixins
|
||||
import phonenumber_field.modelfields
|
||||
from decimal import Decimal
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
@ -47,21 +48,56 @@ class Migration(migrations.Migration):
|
||||
},
|
||||
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(
|
||||
name='CarFinance',
|
||||
fields=[
|
||||
('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')),
|
||||
('profit_margin', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Profit Margin')),
|
||||
('selling_price', models.DecimalField(decimal_places=2, editable=False, max_digits=14, verbose_name='Selling Price')),
|
||||
('vat_rate', models.DecimalField(decimal_places=2, default=0.15, max_digits=10, verbose_name='VAT Rate')),
|
||||
('vat_amount', models.DecimalField(decimal_places=2, editable=False, max_digits=12, verbose_name='VAT Amount')),
|
||||
('total', models.DecimalField(decimal_places=2, editable=False, max_digits=14, verbose_name='Total Amount')),
|
||||
('selling_price', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Selling Price')),
|
||||
('profit_margin', models.DecimalField(decimal_places=2, editable=False, max_digits=14, verbose_name='Profit Margin')),
|
||||
('discount_amount', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Discount Amount')),
|
||||
('registration_fee', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Registration Fee')),
|
||||
('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')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Car Financial Details',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='car',
|
||||
@ -171,7 +207,7 @@ class Migration(migrations.Migration):
|
||||
fields=[
|
||||
('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_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')),
|
||||
],
|
||||
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'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ExteriorColors',
|
||||
name='SaleQuotation',
|
||||
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')),
|
||||
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='exteriorcolor', to='inventory.car')),
|
||||
('remarks', models.TextField(blank=True, null=True)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.customer')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Exterior Color',
|
||||
'verbose_name_plural': 'Exterior Colors',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='InteriorColors',
|
||||
name='SaleQuotationCar',
|
||||
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')),
|
||||
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interiorcolor', to='inventory.car', verbose_name='Car')),
|
||||
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.car')),
|
||||
('financial_details', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='quotation_finances', to='inventory.carfinance')),
|
||||
('quotation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='quotation_cars', to='inventory.salequotation')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Interior Color',
|
||||
'verbose_name_plural': 'Interior Colors',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Vendor',
|
||||
@ -263,6 +287,7 @@ class Migration(migrations.Migration):
|
||||
('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')),
|
||||
('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')),
|
||||
],
|
||||
options={
|
||||
@ -290,4 +315,18 @@ class Migration(migrations.Migration):
|
||||
'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')},
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
@ -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',
|
||||
),
|
||||
]
|
||||
@ -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')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@ -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'),
|
||||
),
|
||||
]
|
||||
@ -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',
|
||||
),
|
||||
]
|
||||
@ -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',
|
||||
),
|
||||
]
|
||||
@ -14,10 +14,12 @@ from django_ledger.models import (
|
||||
UnitOfMeasureModel,
|
||||
CustomerModel,
|
||||
ItemModelQuerySet,
|
||||
|
||||
)
|
||||
from decimal import Decimal, InvalidOperation
|
||||
from django.core.exceptions import ValidationError
|
||||
from phonenumber_field.modelfields import PhoneNumberField
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from decimal import Decimal
|
||||
from django.utils.timezone import now
|
||||
from .mixins import LocalizedNameMixin
|
||||
|
||||
@ -26,7 +28,7 @@ class CarMake(models.Model, LocalizedNameMixin):
|
||||
id_car_make = models.AutoField(primary_key=True)
|
||||
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)
|
||||
|
||||
def __str__(self):
|
||||
@ -38,7 +40,7 @@ class CarMake(models.Model, LocalizedNameMixin):
|
||||
|
||||
class CarModel(models.Model, LocalizedNameMixin):
|
||||
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)
|
||||
arabic_name = models.CharField(max_length=255)
|
||||
|
||||
@ -51,9 +53,7 @@ class CarModel(models.Model, LocalizedNameMixin):
|
||||
|
||||
class CarSerie(models.Model, LocalizedNameMixin):
|
||||
id_car_serie = models.AutoField(primary_key=True)
|
||||
id_car_model = models.ForeignKey(
|
||||
CarModel, models.DO_NOTHING, db_column="id_car_model"
|
||||
)
|
||||
id_car_model = models.ForeignKey(CarModel, models.DO_NOTHING, db_column='id_car_model')
|
||||
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):
|
||||
id_car_trim = models.AutoField(primary_key=True)
|
||||
id_car_serie = models.ForeignKey(
|
||||
CarSerie, models.DO_NOTHING, db_column="id_car_serie"
|
||||
)
|
||||
id_car_serie = models.ForeignKey(CarSerie, models.DO_NOTHING, db_column='id_car_serie')
|
||||
name = models.CharField(max_length=255)
|
||||
arabic_name = models.CharField(max_length=255)
|
||||
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)
|
||||
name = models.CharField(max_length=255)
|
||||
arabic_name = models.CharField(max_length=255)
|
||||
id_parent = models.ForeignKey(
|
||||
"self", models.DO_NOTHING, db_column="id_parent", blank=True, null=True
|
||||
)
|
||||
id_parent = models.ForeignKey('self', models.DO_NOTHING, db_column='id_parent', blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@ -98,10 +94,8 @@ class CarSpecification(models.Model, LocalizedNameMixin):
|
||||
|
||||
class CarSpecificationValue(models.Model):
|
||||
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_specification = models.ForeignKey(
|
||||
CarSpecification, models.DO_NOTHING, db_column="id_car_specification"
|
||||
)
|
||||
id_car_trim = models.ForeignKey(CarTrim, models.DO_NOTHING, db_column='id_car_trim')
|
||||
id_car_specification = models.ForeignKey(CarSpecification, models.DO_NOTHING, db_column='id_car_specification')
|
||||
value = models.CharField(max_length=500)
|
||||
unit = models.CharField(max_length=255, blank=True, null=True)
|
||||
|
||||
@ -114,21 +108,24 @@ class CarSpecificationValue(models.Model):
|
||||
|
||||
# Car Model
|
||||
class CarStatusChoices(models.TextChoices):
|
||||
AVAILABLE = "available", _("Available")
|
||||
SOLD = "sold", _("Sold")
|
||||
HOLD = "hold", _("Hold")
|
||||
DAMAGED = "damaged", _("Damaged")
|
||||
AVAILABLE = 'available', _('Available')
|
||||
SOLD = 'sold', _('Sold')
|
||||
HOLD = 'hold', _('Hold')
|
||||
DAMAGED = 'damaged', _('Damaged')
|
||||
|
||||
|
||||
class CarStockTypeChoices(models.TextChoices):
|
||||
NEW = "new", _("New")
|
||||
USED = "used", _("Used")
|
||||
NEW = 'new', _('New')
|
||||
USED = 'used', _('Used')
|
||||
|
||||
|
||||
class Car(models.Model):
|
||||
vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN"))
|
||||
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(
|
||||
@ -136,53 +133,53 @@ class Car(models.Model):
|
||||
models.DO_NOTHING,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="cars",
|
||||
verbose_name=_("Vendor"),
|
||||
related_name='cars',
|
||||
verbose_name=_("Vendor")
|
||||
)
|
||||
id_car_make = models.ForeignKey(
|
||||
CarMake,
|
||||
models.DO_NOTHING,
|
||||
db_column="id_car_make",
|
||||
db_column='id_car_make',
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=_("Make"),
|
||||
verbose_name=_("Make")
|
||||
)
|
||||
id_car_model = models.ForeignKey(
|
||||
CarModel,
|
||||
models.DO_NOTHING,
|
||||
db_column="id_car_model",
|
||||
db_column='id_car_model',
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=_("Model"),
|
||||
verbose_name=_("Model")
|
||||
)
|
||||
year = models.IntegerField(verbose_name=_("Year"))
|
||||
id_car_serie = models.ForeignKey(
|
||||
CarSerie,
|
||||
models.DO_NOTHING,
|
||||
db_column="id_car_serie",
|
||||
db_column='id_car_serie',
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=_("Series"),
|
||||
verbose_name=_("Series")
|
||||
)
|
||||
id_car_trim = models.ForeignKey(
|
||||
CarTrim,
|
||||
models.DO_NOTHING,
|
||||
db_column="id_car_trim",
|
||||
db_column='id_car_trim',
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=_("Trim"),
|
||||
verbose_name=_("Trim")
|
||||
)
|
||||
status = models.CharField(
|
||||
max_length=10,
|
||||
choices=CarStatusChoices.choices,
|
||||
default=CarStatusChoices.AVAILABLE,
|
||||
verbose_name=_("Status"),
|
||||
verbose_name=_("Status")
|
||||
)
|
||||
stock_type = models.CharField(
|
||||
max_length=10,
|
||||
choices=CarStockTypeChoices.choices,
|
||||
default=CarStockTypeChoices.NEW,
|
||||
verbose_name=_("Stock Type"),
|
||||
verbose_name=_("Stock Type")
|
||||
)
|
||||
remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks"))
|
||||
mileage = models.IntegerField(blank=True, null=True, verbose_name=_("Mileage"))
|
||||
@ -205,23 +202,21 @@ class Car(models.Model):
|
||||
@property
|
||||
def selling_price(self):
|
||||
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
|
||||
def vat_amount(self):
|
||||
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
|
||||
def total(self):
|
||||
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):
|
||||
car = models.ForeignKey(
|
||||
"Car", on_delete=models.CASCADE, related_name="reservations"
|
||||
)
|
||||
car = models.ForeignKey('Car', on_delete=models.CASCADE, related_name='reservations')
|
||||
reserved_by = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
reserved_at = models.DateTimeField(auto_now_add=True)
|
||||
reserved_until = models.DateTimeField()
|
||||
@ -230,81 +225,98 @@ class CarReservation(models.Model):
|
||||
return self.reserved_until > now()
|
||||
|
||||
class Meta:
|
||||
unique_together = ("car", "reserved_until")
|
||||
ordering = ["-reserved_at"]
|
||||
unique_together = ('car', 'reserved_until')
|
||||
ordering = ['-reserved_at']
|
||||
|
||||
|
||||
# Car Finance Model
|
||||
class CarFinance(models.Model):
|
||||
car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name="finances")
|
||||
cost_price = models.DecimalField(
|
||||
max_digits=14, decimal_places=2, verbose_name=_("Cost Price")
|
||||
)
|
||||
profit_margin = models.DecimalField(
|
||||
max_digits=10, decimal_places=2, verbose_name=_("Profit Margin")
|
||||
)
|
||||
selling_price = models.DecimalField(
|
||||
max_digits=14, decimal_places=2, verbose_name=_("Selling Price"), editable=False
|
||||
)
|
||||
vat_rate = models.DecimalField(
|
||||
max_digits=10, decimal_places=2, default=0.15, verbose_name=_("VAT Rate")
|
||||
)
|
||||
vat_amount = models.DecimalField(
|
||||
max_digits=12, decimal_places=2, verbose_name=_("VAT Amount"), editable=False
|
||||
)
|
||||
total = models.DecimalField(
|
||||
max_digits=14, decimal_places=2, verbose_name=_("Total Amount"), editable=False
|
||||
)
|
||||
car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name='finances')
|
||||
cost_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Cost Price"))
|
||||
selling_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Selling Price"))
|
||||
profit_margin = models.DecimalField(max_digits=14,
|
||||
decimal_places=2,
|
||||
verbose_name=_("Profit Margin"),
|
||||
editable=False)
|
||||
discount_amount = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Discount Amount"),
|
||||
default=Decimal('0.00'))
|
||||
registration_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Registration Fee"),
|
||||
default=Decimal('0.00'))
|
||||
administration_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Administration Fee"),
|
||||
default=Decimal('0.00'))
|
||||
transportation_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Transportation Fee"),
|
||||
default=Decimal('0.00'))
|
||||
custom_card_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Custom Card Fee"),
|
||||
default=Decimal('0.00'))
|
||||
vat_rate = models.DecimalField(max_digits=14, decimal_places=2, default=Decimal('0.15'), verbose_name=_("VAT Rate"),)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Car Financial Details")
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.full_clean()
|
||||
self.selling_price = self.cost_price * (1 + self.profit_margin)
|
||||
self.vat_amount = self.selling_price * self.vat_rate
|
||||
self.total = self.selling_price + self.vat_amount
|
||||
try:
|
||||
self.profit_margin = self.selling_price - self.cost_price - self.discount_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)
|
||||
|
||||
|
||||
@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):
|
||||
return (
|
||||
f"Car Financial Details for {self.car}: Selling Price {self.selling_price}"
|
||||
)
|
||||
return f"{self.name} ({self.rgb})"
|
||||
|
||||
|
||||
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
|
||||
class CarColors(models.Model):
|
||||
class ColorType(models.TextChoices):
|
||||
EXTERIOR = "exterior", _("Exterior")
|
||||
INTERIOR = "interior", _("Interior")
|
||||
|
||||
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"),
|
||||
)
|
||||
car = models.ForeignKey('Car', on_delete=models.CASCADE, related_name='colors')
|
||||
exterior = models.ForeignKey('ExteriorColors', on_delete=models.CASCADE, related_name='colors')
|
||||
interior = models.ForeignKey('InteriorColors', on_delete=models.CASCADE, related_name='colors')
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Color")
|
||||
verbose_name_plural = _("Colors")
|
||||
unique_together = ('car', 'exterior', 'interior')
|
||||
|
||||
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
|
||||
class CustomCard(models.Model):
|
||||
car = models.ForeignKey(
|
||||
Car,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="custom_cards",
|
||||
verbose_name=_("Car"),
|
||||
)
|
||||
car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name='custom_cards', verbose_name=_("Car"))
|
||||
custom_number = models.CharField(max_length=255, verbose_name=_("Custom Number"))
|
||||
custom_date = models.DateField(verbose_name=_("Custom Date"))
|
||||
|
||||
@ -318,12 +330,7 @@ class CustomCard(models.Model):
|
||||
|
||||
# Car Registration Model
|
||||
class CarRegistration(models.Model):
|
||||
car = models.ForeignKey(
|
||||
Car,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="registrations",
|
||||
verbose_name=_("Car"),
|
||||
)
|
||||
car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name='registrations', verbose_name=_("Car"))
|
||||
plate_number = models.IntegerField(verbose_name=_("Plate Number"))
|
||||
text1 = models.CharField(max_length=1, verbose_name=_("Text 1"))
|
||||
text2 = models.CharField(max_length=1, verbose_name=_("Text 2"))
|
||||
@ -349,20 +356,14 @@ class TimestampedModel(models.Model):
|
||||
|
||||
# Dealer Model
|
||||
class Dealer(models.Model, LocalizedNameMixin):
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="dealer")
|
||||
crn = models.CharField(
|
||||
max_length=10, verbose_name=_("Commercial Registration Number")
|
||||
)
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='dealer')
|
||||
crn = models.CharField(max_length=10, verbose_name=_("Commercial Registration Number"))
|
||||
vrn = models.CharField(max_length=15, verbose_name=_("VAT Registration Number"))
|
||||
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
|
||||
name = models.CharField(max_length=255, verbose_name=_("English Name"))
|
||||
phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number"))
|
||||
address = models.CharField(
|
||||
max_length=200, blank=True, null=True, verbose_name=_("Address")
|
||||
)
|
||||
logo = models.ImageField(
|
||||
upload_to="logos/users", blank=True, null=True, verbose_name=_("Logo")
|
||||
)
|
||||
phone_number = PhoneNumberField(region='SA', verbose_name=_("Phone Number"))
|
||||
address = models.CharField(max_length=200, blank=True, null=True, verbose_name=_("Address"))
|
||||
logo = models.ImageField(upload_to="logos/users", blank=True, null=True, verbose_name=_("Logo"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Dealer")
|
||||
@ -374,20 +375,15 @@ class Dealer(models.Model, LocalizedNameMixin):
|
||||
|
||||
# Vendor Model
|
||||
class Vendor(models.Model, LocalizedNameMixin):
|
||||
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="vendors")
|
||||
crn = models.CharField(
|
||||
max_length=10, unique=True, verbose_name=_("Commercial Registration Number")
|
||||
)
|
||||
vrn = models.CharField(
|
||||
max_length=15, unique=True, verbose_name=_("VAT Registration Number")
|
||||
)
|
||||
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name='vendors')
|
||||
crn = models.CharField(max_length=10, unique=True, verbose_name=_("Commercial 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"))
|
||||
name = models.CharField(max_length=255, verbose_name=_("English Name"))
|
||||
contact_person = models.CharField(max_length=100, verbose_name=_("Contact Person"))
|
||||
phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number"))
|
||||
address = models.CharField(
|
||||
max_length=200, blank=True, null=True, verbose_name=_("Address")
|
||||
)
|
||||
phone_number = PhoneNumberField(region='SA', verbose_name=_("Phone Number"))
|
||||
address = models.CharField(max_length=200, blank=True, null=True, verbose_name=_("Address"))
|
||||
logo = models.ImageField(upload_to="logos/vendors", blank=True, null=True, verbose_name=_("Logo"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Vendor")
|
||||
@ -399,24 +395,14 @@ class Vendor(models.Model, LocalizedNameMixin):
|
||||
|
||||
# Customer Model
|
||||
class Customer(models.Model):
|
||||
dealer = models.ForeignKey(
|
||||
Dealer, on_delete=models.CASCADE, related_name="customers"
|
||||
)
|
||||
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name='customers')
|
||||
first_name = models.CharField(max_length=50, verbose_name=_("First Name"))
|
||||
middle_name = models.CharField(
|
||||
max_length=50, blank=True, null=True, verbose_name=_("Middle Name")
|
||||
)
|
||||
middle_name = models.CharField(max_length=50, blank=True, null=True, verbose_name=_("Middle Name"))
|
||||
last_name = models.CharField(max_length=50, verbose_name=_("Last Name"))
|
||||
email = models.EmailField(unique=True, verbose_name=_("Email"))
|
||||
national_id = models.CharField(
|
||||
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")
|
||||
)
|
||||
national_id = models.CharField(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"))
|
||||
created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created"))
|
||||
|
||||
class Meta:
|
||||
@ -424,9 +410,84 @@ class Customer(models.Model):
|
||||
verbose_name_plural = _("Customers")
|
||||
|
||||
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}"
|
||||
|
||||
|
||||
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
|
||||
@receiver(post_save, sender=Dealer)
|
||||
def create_ledger_entity(sender, instance, created, **kwargs):
|
||||
|
||||
@ -25,6 +25,7 @@ def decode_vin_pyvin(vin):
|
||||
print(data)
|
||||
return data
|
||||
|
||||
|
||||
# vehicle-info
|
||||
# c2729afb
|
||||
# 6d397471920412d672af1b8a02ca52ea
|
||||
@ -44,197 +45,10 @@ def elm(vin):
|
||||
payload = {}
|
||||
response = requests.request("GET", url, headers=headers, data=payload)
|
||||
car_info = json.loads(response.text)
|
||||
print(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):
|
||||
client = OpenAI(api_key=settings.OPENAI_API_KEY)
|
||||
completion = client.chat.completions.create(
|
||||
|
||||
@ -55,11 +55,20 @@ urlpatterns = [
|
||||
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('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>/colors/<int:pk>/update/',
|
||||
views.CarColorUpdateView.as_view(),
|
||||
name='color_update'),
|
||||
path('cars/<int:car_pk>/add-color/', views.CarColorCreate.as_view(), name='add_color'),
|
||||
# path('cars/<int:car_pk>/colors/<int:pk>/update/',views.CarColorUpdateView.as_view(),name='color_update'),
|
||||
|
||||
path('cars/reserve/<int:car_id>/', views.reserve_car_view, name='reserve_car'),
|
||||
path('reservations/<int:reservation_id>/', views.manage_reservation, name='reservations'),
|
||||
path('cars/<int:car_pk>/add-custom-card/', views.CustomCardCreateView.as_view(), name='add_custom_card'),
|
||||
]
|
||||
|
||||
# Sales URLs
|
||||
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'),
|
||||
|
||||
]
|
||||
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\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"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\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 "
|
||||
"&& 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_inventory.html:40
|
||||
#: templates/inventory/car_inventory.html:53
|
||||
#: templates/inventory/car_list.html:67 templates/inventory/car_list.html:69
|
||||
msgid "VIN"
|
||||
msgstr "رقم الهيكل"
|
||||
@ -34,47 +34,51 @@ msgstr "الإنجليزية"
|
||||
msgid "Arabic"
|
||||
msgstr "العربية"
|
||||
|
||||
#: inventory/forms.py:147 inventory/models.py:284
|
||||
#: inventory/forms.py:102 inventory/models.py:357
|
||||
#: templates/inventory/car_detail.html:135
|
||||
msgid "Custom Date"
|
||||
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"
|
||||
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
|
||||
msgid "Available"
|
||||
msgstr "متاح"
|
||||
|
||||
#: inventory/models.py:111
|
||||
#: inventory/models.py:112
|
||||
msgid "Sold"
|
||||
msgstr "تم البيع"
|
||||
|
||||
#: inventory/models.py:112
|
||||
#: inventory/models.py:113
|
||||
msgid "Hold"
|
||||
msgstr "في الانتظار"
|
||||
|
||||
#: inventory/models.py:113
|
||||
#: inventory/models.py:114
|
||||
msgid "Damaged"
|
||||
msgstr "تالف"
|
||||
|
||||
#: inventory/models.py:117
|
||||
#: inventory/models.py:118
|
||||
msgid "New"
|
||||
msgstr "جديد"
|
||||
|
||||
#: inventory/models.py:118
|
||||
#: inventory/models.py:119
|
||||
msgid "Used"
|
||||
msgstr "مستعمل"
|
||||
|
||||
#: inventory/models.py:127 inventory/models.py:332
|
||||
#: inventory/models.py:128 inventory/models.py:405
|
||||
msgid "Dealer"
|
||||
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_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/vendor.py:191
|
||||
#: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/bills/tags/bill_table.html:12
|
||||
@ -82,28 +86,28 @@ msgstr "المعرض"
|
||||
msgid "Vendor"
|
||||
msgstr "المورد"
|
||||
|
||||
#: inventory/models.py:144 templates/inventory/car_inventory.html:42
|
||||
#: inventory/models.py:145 templates/inventory/car_inventory.html:55
|
||||
msgid "Make"
|
||||
msgstr "الصانع"
|
||||
|
||||
#: inventory/models.py:152 templates/inventory/car_inventory.html:43
|
||||
#: inventory/models.py:153 templates/inventory/car_inventory.html:56
|
||||
msgid "Model"
|
||||
msgstr "الموديل"
|
||||
|
||||
#: inventory/models.py:154 templates/inventory/car_form.html:117
|
||||
#: templates/inventory/car_inventory.html:41
|
||||
#: inventory/models.py:155 templates/inventory/car_form.html:118
|
||||
#: templates/inventory/car_inventory.html:54
|
||||
msgid "Year"
|
||||
msgstr "السنة"
|
||||
|
||||
#: inventory/models.py:161 templates/inventory/car_form.html:177
|
||||
#: inventory/models.py:162 templates/inventory/car_form.html:182
|
||||
msgid "Series"
|
||||
msgstr "السلسلة"
|
||||
|
||||
#: inventory/models.py:169
|
||||
#: inventory/models.py:170
|
||||
msgid "Trim"
|
||||
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
|
||||
#: 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
|
||||
@ -114,171 +118,201 @@ msgstr "الفئة"
|
||||
msgid "Status"
|
||||
msgstr "الحالة"
|
||||
|
||||
#: inventory/models.py:181 templates/inventory/car_detail.html:90
|
||||
#: templates/inventory/car_form.html:243 templates/inventory/car_list.html:177
|
||||
#: inventory/models.py:182 templates/inventory/car_detail.html:90
|
||||
#: templates/inventory/car_form.html:248 templates/inventory/car_list.html:177
|
||||
msgid "Stock Type"
|
||||
msgstr "نوع المخزون"
|
||||
|
||||
#: inventory/models.py:183 templates/inventory/car_detail.html:113
|
||||
#: templates/inventory/car_form.html:296 templates/inventory/car_list.html:200
|
||||
#: inventory/models.py:184 templates/inventory/car_detail.html:113
|
||||
#: templates/inventory/car_form.html:301 templates/inventory/car_list.html:200
|
||||
msgid "Remarks"
|
||||
msgstr "ملاحظات"
|
||||
|
||||
#: inventory/models.py:184 templates/inventory/car_detail.html:94
|
||||
#: templates/inventory/car_form.html:260 templates/inventory/car_list.html:191
|
||||
#: inventory/models.py:185 templates/inventory/car_detail.html:94
|
||||
#: templates/inventory/car_form.html:265 templates/inventory/car_list.html:191
|
||||
#: templates/inventory/car_list.html:192
|
||||
msgid "Mileage"
|
||||
msgstr "عدد الكيلومترات"
|
||||
|
||||
#: inventory/models.py:185 templates/inventory/car_detail.html:98
|
||||
#: templates/inventory/car_form.html:278
|
||||
#: inventory/models.py:186 templates/inventory/car_detail.html:98
|
||||
#: templates/inventory/car_form.html:283
|
||||
msgid "Receiving Date"
|
||||
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"
|
||||
msgstr "السيارة"
|
||||
|
||||
#: inventory/models.py:189
|
||||
#: inventory/models.py:190
|
||||
msgid "Cars"
|
||||
msgstr "السيارات"
|
||||
|
||||
#: inventory/models.py:234 templates/inventory/car_detail.html:160
|
||||
#: templates/inventory/car_finance_form.html:33
|
||||
#: inventory/models.py:237 templates/inventory/car_detail.html:160
|
||||
msgid "Cost Price"
|
||||
msgstr "سعر التكلفة"
|
||||
|
||||
#: inventory/models.py:235 templates/inventory/car_detail.html:164
|
||||
#: templates/inventory/car_finance_form.html:40
|
||||
#: inventory/models.py:240
|
||||
msgid "Profit Margin"
|
||||
msgstr "هامش الربح"
|
||||
|
||||
#: inventory/models.py:236 templates/inventory/car_detail.html:168
|
||||
#: templates/inventory/car_finance_form.html:54
|
||||
#: inventory/models.py:244 templates/inventory/car_detail.html:165
|
||||
msgid "Selling Price"
|
||||
msgstr "سعر البيع"
|
||||
|
||||
#: inventory/models.py:237 templates/inventory/car_detail.html:172
|
||||
#: 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
|
||||
#: inventory/models.py:247 templates/inventory/car_detail.html:189
|
||||
msgid "VAT Amount"
|
||||
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"
|
||||
msgstr "المبلغ الإجمالي"
|
||||
|
||||
#: inventory/models.py:242
|
||||
#: inventory/models.py:275
|
||||
msgid "Car Financial Details"
|
||||
msgstr "التفاصيل المالية السيارة"
|
||||
msgstr "تفاصيل المالية للسيارة"
|
||||
|
||||
#: inventory/models.py:258
|
||||
msgid "Exterior"
|
||||
msgstr "الخارجي"
|
||||
#: inventory/models.py:280
|
||||
msgid "Selling price cannot be negative."
|
||||
msgstr "لا يمكن أن يكون سعر البيع سالبًا."
|
||||
|
||||
#: inventory/models.py:259
|
||||
msgid "Interior"
|
||||
msgstr "الداخلي"
|
||||
#: inventory/models.py:282
|
||||
msgid "Discount amount cannot be negative."
|
||||
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
|
||||
#: venv/lib/python3.11/site-packages/django_ledger/forms/coa.py:16
|
||||
#: venv/lib/python3.11/site-packages/django_ledger/forms/coa.py:37
|
||||
msgid "Name"
|
||||
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"
|
||||
msgstr "الاسم بالعربية"
|
||||
|
||||
#: inventory/models.py:264
|
||||
#: inventory/models.py:315 inventory/models.py:328
|
||||
msgid "RGB"
|
||||
msgstr "آر جي بي"
|
||||
|
||||
#: inventory/models.py:269 templates/inventory/color_palette.html:83
|
||||
msgid "Color Type"
|
||||
msgstr "نوع اللون"
|
||||
#: inventory/models.py:318 inventory/models.py:319
|
||||
msgid "Exterior Colors"
|
||||
msgstr "الألوان الخارجية"
|
||||
|
||||
#: inventory/models.py:273
|
||||
#: inventory/models.py:331 inventory/models.py:332
|
||||
msgid "Interior Colors"
|
||||
msgstr "الألوان الداخلية"
|
||||
|
||||
#: inventory/models.py:345
|
||||
msgid "Color"
|
||||
msgstr "اللون"
|
||||
|
||||
#: inventory/models.py:274
|
||||
#: inventory/models.py:346
|
||||
msgid "Colors"
|
||||
msgstr "الألوان"
|
||||
|
||||
#: inventory/models.py:283 templates/inventory/car_detail.html:131
|
||||
#: inventory/models.py:356 templates/inventory/car_detail.html:131
|
||||
msgid "Custom Number"
|
||||
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
|
||||
msgid "Custom Card"
|
||||
msgstr "البطاقة الجمركية"
|
||||
|
||||
#: inventory/models.py:288
|
||||
#: inventory/models.py:361
|
||||
msgid "Custom Cards"
|
||||
msgstr "البطاقات الجمركية"
|
||||
|
||||
#: inventory/models.py:297
|
||||
#: inventory/models.py:370
|
||||
msgid "Plate Number"
|
||||
msgstr "رقم اللوحة"
|
||||
|
||||
#: inventory/models.py:298
|
||||
#: inventory/models.py:371
|
||||
msgid "Text 1"
|
||||
msgstr "النص 1"
|
||||
|
||||
#: inventory/models.py:299
|
||||
#: inventory/models.py:372
|
||||
msgid "Text 2"
|
||||
msgstr "النص 2"
|
||||
|
||||
#: inventory/models.py:300
|
||||
#: inventory/models.py:373
|
||||
msgid "Text 3"
|
||||
msgstr "النص 3"
|
||||
|
||||
#: inventory/models.py:301
|
||||
#: inventory/models.py:374
|
||||
msgid "Registration Date"
|
||||
msgstr "تاريخ التسجيل"
|
||||
|
||||
#: inventory/models.py:304
|
||||
#: inventory/models.py:377
|
||||
msgid "Registration"
|
||||
msgstr "التسجيل"
|
||||
|
||||
#: inventory/models.py:305
|
||||
#: inventory/models.py:378
|
||||
msgid "Registrations"
|
||||
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/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
|
||||
msgid "Created"
|
||||
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
|
||||
msgid "Updated"
|
||||
msgstr "تم التحديث"
|
||||
|
||||
#: inventory/models.py:323 inventory/models.py:342
|
||||
#: inventory/models.py:396 inventory/models.py:415
|
||||
#: templates/dealers/dealer_detail.html:30
|
||||
msgid "Commercial Registration Number"
|
||||
msgstr "رقم السجل التجاري"
|
||||
|
||||
#: inventory/models.py:324 inventory/models.py:343
|
||||
#: inventory/models.py:397 inventory/models.py:416
|
||||
#: templates/dealers/dealer_detail.html:34
|
||||
msgid "VAT Registration Number"
|
||||
msgstr "رقم التسجيل في ضريبة القيمة المضافة"
|
||||
|
||||
#: inventory/models.py:326 inventory/models.py:345
|
||||
#: inventory/models.py:399 inventory/models.py:418
|
||||
msgid "English Name"
|
||||
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/dealers/dealer_detail.html:38
|
||||
#: templates/vendors/view_vendor.html:44
|
||||
@ -286,7 +320,7 @@ msgstr "الاسم بالإنجليزية"
|
||||
msgid "Phone Number"
|
||||
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/dealers/dealer_detail.html:42
|
||||
#: templates/vendors/view_vendor.html:46
|
||||
@ -295,47 +329,47 @@ msgstr "رقم الهاتف"
|
||||
msgid "Address"
|
||||
msgstr "العنوان"
|
||||
|
||||
#: inventory/models.py:329
|
||||
#: inventory/models.py:402
|
||||
msgid "Logo"
|
||||
msgstr "الشعار"
|
||||
|
||||
#: inventory/models.py:333
|
||||
#: inventory/models.py:406
|
||||
msgid "Dealers"
|
||||
msgstr "المعارض"
|
||||
|
||||
#: inventory/models.py:346 templates/vendors/view_vendor.html:43
|
||||
#: inventory/models.py:419 templates/vendors/view_vendor.html:43
|
||||
msgid "Contact Person"
|
||||
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/vendors_list.html:5
|
||||
msgid "Vendors"
|
||||
msgstr "الموردين"
|
||||
|
||||
#: inventory/models.py:361 templates/customers/view_customer.html:46
|
||||
#: inventory/models.py:434 templates/customers/view_customer.html:46
|
||||
msgid "First Name"
|
||||
msgstr "الاسم الأول"
|
||||
|
||||
#: inventory/models.py:362 templates/customers/view_customer.html:47
|
||||
#: inventory/models.py:435 templates/customers/view_customer.html:47
|
||||
msgid "Middle Name"
|
||||
msgstr "اسم الأب"
|
||||
|
||||
#: inventory/models.py:363 templates/customers/view_customer.html:48
|
||||
#: inventory/models.py:436 templates/customers/view_customer.html:48
|
||||
msgid "Last Name"
|
||||
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
|
||||
#: venv/lib/python3.11/site-packages/django_ledger/models/mixins.py:111
|
||||
msgid "Email"
|
||||
msgstr "البريد الإلكتروني"
|
||||
|
||||
#: inventory/models.py:365 templates/customers/view_customer.html:52
|
||||
#: inventory/models.py:438 templates/customers/view_customer.html:52
|
||||
msgid "National ID"
|
||||
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/estimate.py:252
|
||||
#: venv/lib/python3.11/site-packages/django_ledger/models/invoice.py:318
|
||||
@ -344,11 +378,11 @@ msgstr "رقم الهوية الوطنية"
|
||||
msgid "Customer"
|
||||
msgstr "العميل"
|
||||
|
||||
#: inventory/models.py:372
|
||||
#: inventory/models.py:445
|
||||
msgid "Customers"
|
||||
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/coa.py:152
|
||||
msgid "Chart of Accounts"
|
||||
@ -370,116 +404,96 @@ msgstr "نسيت كلمة المرور؟"
|
||||
msgid "You are not associated with any dealer."
|
||||
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:7
|
||||
msgid "inventory"
|
||||
msgstr "المخزون"
|
||||
|
||||
#: inventory/views.py:370
|
||||
#: inventory/views.py:401
|
||||
msgid "Car finance details saved successfully."
|
||||
msgstr "تم حفظ تفاصيل المالية للسيارة بنجاح."
|
||||
|
||||
#: inventory/views.py:388
|
||||
#: inventory/views.py:419
|
||||
msgid "Car finance updated successfully."
|
||||
msgstr "تم تحديث التفاصيل المالية للسيارة بنجاح."
|
||||
|
||||
#: inventory/views.py:401
|
||||
#: inventory/views.py:432
|
||||
msgid "Car updated successfully."
|
||||
msgstr "تم تحديث السيارة بنجاح"
|
||||
|
||||
#: inventory/views.py:414
|
||||
#: inventory/views.py:445
|
||||
msgid "Car deleted successfully."
|
||||
msgstr "تم حذف السيارة بنجاح."
|
||||
|
||||
#: inventory/views.py:434
|
||||
#: inventory/views.py:465
|
||||
msgid "Custom Card added successfully."
|
||||
msgstr "تم إضافة البطاقة الجمركية بنجاح."
|
||||
|
||||
#: inventory/views.py:452 inventory/views.py:521
|
||||
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
|
||||
#: inventory/views.py:474
|
||||
msgid "This car is already reserved."
|
||||
msgstr "هذه السيارة محجوزة بالفعل."
|
||||
|
||||
#: inventory/views.py:582
|
||||
#: inventory/views.py:484
|
||||
msgid "Car reserved successfully."
|
||||
msgstr "تم حجز السيارة بنجاح."
|
||||
|
||||
#: inventory/views.py:599
|
||||
#: inventory/views.py:501
|
||||
msgid "Reservation renewed successfully."
|
||||
msgstr "تم تجديد الحجز بنجاح"
|
||||
|
||||
#: inventory/views.py:604
|
||||
#: inventory/views.py:506
|
||||
msgid "Reservation canceled successfully."
|
||||
msgstr "تم إلغاء الحجز بنجاح."
|
||||
|
||||
#: inventory/views.py:608
|
||||
#: inventory/views.py:510
|
||||
msgid "Invalid action."
|
||||
msgstr "إجراء غير صالح."
|
||||
|
||||
#: inventory/views.py:610
|
||||
#: inventory/views.py:512
|
||||
msgid "Invalid request method."
|
||||
msgstr "طريقة الطلب غير صالحة"
|
||||
|
||||
#: inventory/views.py:632
|
||||
#: inventory/views.py:534
|
||||
msgid "Dealer created successfully."
|
||||
msgstr "تم إنشاء المعرض بنجاح."
|
||||
|
||||
#: inventory/views.py:643
|
||||
#: inventory/views.py:545
|
||||
msgid "Dealer updated successfully."
|
||||
msgstr "تم تحديث المعرض بنجاح."
|
||||
|
||||
#: inventory/views.py:653
|
||||
#: inventory/views.py:555
|
||||
msgid "Dealer deleted successfully."
|
||||
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:6 templates/header.html:59
|
||||
#: templates/header.html:74
|
||||
msgid "customers"
|
||||
msgstr "العملاء"
|
||||
|
||||
#: inventory/views.py:698
|
||||
#: inventory/views.py:600
|
||||
msgid "Customer created successfully."
|
||||
msgstr "تم إنشاء العميل بنجاح."
|
||||
|
||||
#: inventory/views.py:714
|
||||
#: inventory/views.py:616
|
||||
msgid "Customer updated successfully."
|
||||
msgstr "تم تحديث العميل بنجاح."
|
||||
|
||||
#: inventory/views.py:724
|
||||
#: inventory/views.py:626
|
||||
msgid "Customer deleted successfully."
|
||||
msgstr "تم حذف العميل بنجاح."
|
||||
|
||||
#: inventory/views.py:750
|
||||
#: inventory/views.py:652
|
||||
msgid "Vendor created successfully."
|
||||
msgstr "تم إنشاء المورد بنجاح."
|
||||
|
||||
#: inventory/views.py:766
|
||||
#: inventory/views.py:668
|
||||
msgid "Vendor updated successfully."
|
||||
msgstr "تم تحديث المورد بنجاح"
|
||||
|
||||
#: inventory/views.py:776
|
||||
#: inventory/views.py:678
|
||||
msgid "Vendor deleted successfully."
|
||||
msgstr "تم حذف المورد بنجاح."
|
||||
|
||||
@ -583,8 +597,10 @@ msgid "Add Customer"
|
||||
msgstr "إضافة عميل"
|
||||
|
||||
#: templates/customers/customer_form.html:31
|
||||
#: templates/inventory/add_colors.html:54
|
||||
#: 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/vendors/vendor_form.html:31
|
||||
#: 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 "حفظ"
|
||||
|
||||
#: templates/customers/customer_form.html:33
|
||||
#: templates/inventory/add_colors.html:55
|
||||
#: templates/inventory/add_custom_card.html:8
|
||||
#: templates/inventory/car_confirm_delete.html:14
|
||||
#: templates/inventory/car_detail.html:266
|
||||
#: templates/inventory/car_finance_form.html:68
|
||||
#: templates/inventory/car_detail.html:282
|
||||
#: templates/inventory/car_finance_form.html:41
|
||||
#: templates/inventory/color_palette.html:107
|
||||
#: templates/inventory/reserve_car.html:30
|
||||
#: templates/vendors/vendor_form.html:33
|
||||
@ -616,7 +633,7 @@ msgid "Cancel"
|
||||
msgstr "إلغاء"
|
||||
|
||||
#: 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
|
||||
msgid "search"
|
||||
msgstr "بحث"
|
||||
@ -644,7 +661,7 @@ msgstr "الإجراءات"
|
||||
|
||||
#: templates/customers/customer_list.html:59
|
||||
#: templates/inventory/car_detail.html:124
|
||||
#: templates/inventory/car_inventory.html:62
|
||||
#: templates/inventory/car_inventory.html:75
|
||||
#: templates/vendors/vendors_list.html:65
|
||||
msgid "view"
|
||||
msgstr "عرض"
|
||||
@ -674,8 +691,7 @@ msgid "Customer Details"
|
||||
msgstr "تفاصيل العميل"
|
||||
|
||||
#: templates/customers/view_customer.html:61
|
||||
#: templates/inventory/car_detail.html:217
|
||||
#: templates/inventory/car_detail.html:291
|
||||
#: templates/inventory/car_detail.html:307
|
||||
#: 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/journal_entry/tags/je_table.html:83
|
||||
@ -717,7 +733,7 @@ msgid "Delete"
|
||||
msgstr "حذف"
|
||||
|
||||
#: templates/customers/view_customer.html:72
|
||||
#: templates/inventory/car_detail.html:293
|
||||
#: templates/inventory/car_detail.html:309
|
||||
msgid "Back to List"
|
||||
msgstr "العودة إلى القائمة"
|
||||
|
||||
@ -941,7 +957,7 @@ msgid "View your best-selling cars."
|
||||
msgstr "عرض السيارات الأكثر مبيعاً."
|
||||
|
||||
#: 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"
|
||||
msgstr "الموديل"
|
||||
|
||||
@ -1000,13 +1016,31 @@ msgstr "الجمعة"
|
||||
msgid "Sat"
|
||||
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
|
||||
msgid "Car Details"
|
||||
msgstr "تفاصيل السيارة"
|
||||
|
||||
#: templates/inventory/car_detail.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
|
||||
msgid "specifications"
|
||||
msgstr "المواصفات"
|
||||
@ -1015,7 +1049,7 @@ msgstr "المواصفات"
|
||||
msgid "year"
|
||||
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
|
||||
msgid "make"
|
||||
msgstr "الصانع"
|
||||
@ -1024,7 +1058,7 @@ msgstr "الصانع"
|
||||
msgid "series"
|
||||
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
|
||||
msgid "trim"
|
||||
msgstr "الفئة"
|
||||
@ -1041,7 +1075,7 @@ msgstr "إضافة"
|
||||
msgid "Financial Details"
|
||||
msgstr "التفاصيل المالية"
|
||||
|
||||
#: templates/inventory/car_detail.html:180
|
||||
#: templates/inventory/car_detail.html:193
|
||||
#: 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:127
|
||||
@ -1060,44 +1094,44 @@ msgstr "التفاصيل المالية"
|
||||
msgid "Total"
|
||||
msgstr "الإجمالي"
|
||||
|
||||
#: templates/inventory/car_detail.html:187
|
||||
#: templates/inventory/car_detail.html:200
|
||||
msgid "Edit Finance Details"
|
||||
msgstr "تعديل التفاصيل المالية"
|
||||
|
||||
#: templates/inventory/car_detail.html:191
|
||||
#: templates/inventory/car_detail.html:204
|
||||
msgid "No finance details available."
|
||||
msgstr "لا توجد تفاصيل مالية متاحة."
|
||||
|
||||
#: templates/inventory/car_detail.html:194
|
||||
#: templates/inventory/car_detail.html:207
|
||||
msgid "Add Finance Details"
|
||||
msgstr "إضافة التفاصيل المالية"
|
||||
|
||||
#: templates/inventory/car_detail.html:200
|
||||
#: templates/inventory/car_detail.html:213
|
||||
msgid "Colors Details"
|
||||
msgstr "تفاصيل الألوان"
|
||||
|
||||
#: templates/inventory/car_detail.html:224
|
||||
#: templates/inventory/car_detail.html:240
|
||||
msgid "No colors available for this car."
|
||||
msgstr "لا تتوفر ألوان لهذه السيارة."
|
||||
|
||||
#: templates/inventory/car_detail.html:231
|
||||
#: templates/inventory/car_detail.html:247
|
||||
msgid "Get Colors"
|
||||
msgstr "الحصول على الألوان"
|
||||
|
||||
#: templates/inventory/car_detail.html:238
|
||||
#: templates/inventory/car_detail.html:254
|
||||
msgid "Reservations Details"
|
||||
msgstr "تفاصيل الحجز"
|
||||
|
||||
#: templates/inventory/car_detail.html:243
|
||||
#: templates/inventory/car_detail.html:259
|
||||
msgid "Reserved By"
|
||||
msgstr "محجوز بواسطة"
|
||||
|
||||
#: templates/inventory/car_detail.html:244
|
||||
#: templates/inventory/car_detail.html:260
|
||||
msgid "Expires At"
|
||||
msgstr "ينتهي في"
|
||||
|
||||
#: templates/inventory/car_detail.html:245
|
||||
#: templates/inventory/car_inventory.html:44
|
||||
#: templates/inventory/car_detail.html:261
|
||||
#: 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/accounts_table.html:29
|
||||
#: venv/lib/python3.11/site-packages/django_ledger/templates/django_ledger/account/tags/accounts_table.html:92
|
||||
@ -1124,27 +1158,27 @@ msgstr "ينتهي في"
|
||||
msgid "Actions"
|
||||
msgstr "الإجراءات"
|
||||
|
||||
#: templates/inventory/car_detail.html:260
|
||||
#: templates/inventory/car_detail.html:276
|
||||
msgid "renew"
|
||||
msgstr "تجديد"
|
||||
|
||||
#: templates/inventory/car_detail.html:277
|
||||
#: templates/inventory/car_detail.html:293
|
||||
#: templates/inventory/reserve_car.html:29
|
||||
msgid "Reserve"
|
||||
msgstr "حجز"
|
||||
|
||||
#: templates/inventory/car_detail.html:289
|
||||
#: templates/inventory/car_detail.html:305
|
||||
#: templates/inventory/transfer_car.html:23
|
||||
msgid "transfer"
|
||||
msgstr "نقل"
|
||||
|
||||
#: templates/inventory/car_detail.html:378
|
||||
#: templates/inventory/car_detail.html:394
|
||||
#: templates/inventory/car_list.html:542
|
||||
#: templates/partials/specifications_modal.html:11
|
||||
msgid "No specifications available."
|
||||
msgstr "لا توجد مواصفات متاحة."
|
||||
|
||||
#: templates/inventory/car_detail.html:382
|
||||
#: templates/inventory/car_detail.html:398
|
||||
#: templates/inventory/car_list.html:546
|
||||
msgid "Error loading specifications."
|
||||
msgstr "حدث خطأ أثناء تحميل المواصفات."
|
||||
@ -1153,30 +1187,14 @@ msgstr "حدث خطأ أثناء تحميل المواصفات."
|
||||
msgid "Edit Car"
|
||||
msgstr "تعديل السيارة"
|
||||
|
||||
#: templates/inventory/car_finance_form.html:5
|
||||
#: templates/inventory/car_finance_form.html:6
|
||||
msgid "Car Finance Details"
|
||||
msgstr "التفاصيل المالية السيارة"
|
||||
|
||||
#: templates/inventory/car_finance_form.html:9
|
||||
#: templates/inventory/car_finance_form.html:10
|
||||
msgid "Finance Details for"
|
||||
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/partials/scanner_modal.html:6
|
||||
#: templates/partials/specifications_modal.html:8
|
||||
@ -1190,7 +1208,7 @@ msgstr "الماسح الضوئي"
|
||||
#: templates/inventory/car_form.html:62
|
||||
#: templates/partials/scanner_modal.html:10
|
||||
msgid "VIN will appear here."
|
||||
msgstr ""
|
||||
msgstr "رقم الهيكل سيظهر هنا."
|
||||
|
||||
#: templates/inventory/car_form.html:63
|
||||
#: templates/partials/scanner_modal.html:11
|
||||
@ -1201,34 +1219,42 @@ msgstr "التعرف الآلي على الحروف"
|
||||
msgid "Search"
|
||||
msgstr "بحث"
|
||||
|
||||
#: templates/inventory/car_form.html:160 templates/inventory/car_form.html:182
|
||||
#: templates/inventory/car_form.html:204 templates/inventory/car_form.html:498
|
||||
#: templates/inventory/car_form.html:515 templates/inventory/car_form.html:516
|
||||
#: templates/inventory/car_form.html:534
|
||||
#: templates/inventory/car_form.html:165 templates/inventory/car_form.html:187
|
||||
#: templates/inventory/car_form.html:209 templates/inventory/car_form.html:519
|
||||
#: templates/inventory/car_form.html:536 templates/inventory/car_form.html:537
|
||||
#: templates/inventory/car_form.html:555
|
||||
msgid "Select"
|
||||
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"
|
||||
msgstr "الرجاء الإنتظار"
|
||||
|
||||
#: templates/inventory/car_form.html:382
|
||||
#: templates/inventory/car_form.html:400
|
||||
msgid "Loading"
|
||||
msgstr "تحميل"
|
||||
|
||||
#: templates/inventory/car_form.html:413
|
||||
#: templates/inventory/car_form.html:431
|
||||
msgid "Please enter a valid VIN."
|
||||
msgstr "الرجاء إدخال رقم هيكل صالح مكون من 17 حرفًا."
|
||||
|
||||
#: templates/inventory/car_form.html:429
|
||||
#: templates/inventory/car_form.html:447
|
||||
msgid "Failed to decode VIN."
|
||||
msgstr "فشل في فك تشفير رقم الهيكل"
|
||||
|
||||
#: templates/inventory/car_form.html:434
|
||||
#: templates/inventory/car_form.html:452
|
||||
msgid "An error occurred while decoding the VIN."
|
||||
msgstr "حدث خطأ أثناء فك تشفير الهيكل"
|
||||
|
||||
#: templates/inventory/car_inventory.html:67
|
||||
#: templates/inventory/car_inventory.html:80
|
||||
msgid "No cars available."
|
||||
msgstr "لا توجد سيارات متاحة."
|
||||
|
||||
@ -1294,6 +1320,10 @@ msgstr "تحديث اللون"
|
||||
msgid "Add Color for"
|
||||
msgstr "إضافة لون الى"
|
||||
|
||||
#: templates/inventory/color_palette.html:83
|
||||
msgid "Color Type"
|
||||
msgstr "نوع اللون"
|
||||
|
||||
#: templates/inventory/inventory_stats.html:5
|
||||
msgid "Inventory Statistics"
|
||||
msgstr "إحصائيات المخزون"
|
||||
|
||||
BIN
static/.DS_Store
vendored
BIN
static/images/.DS_Store
vendored
BIN
static/images/logos/.DS_Store
vendored
BIN
static/images/logos/Alamjdouie-Hyundai-Auto.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
static/images/logos/suppliers/.DS_Store
vendored
Normal file
BIN
static/images/logos/suppliers/Alamjdouie-Hyundai-01.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
static/images/logos/suppliers/Alamjdouie-Hyundai-Auto_١.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
static/images/logos/suppliers/Aljomaih-Automotive-Company-2.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
static/images/logos/suppliers/Aljomaih-Automotive-Company-3.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
static/images/logos/users/marwan-company_b9ATueB.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
static/images/logos/vendors/Alamjdouie-Hyundai-01.png
vendored
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
static/images/logos/vendors/Alamjdouie-Hyundai-Auto.png
vendored
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
static/images/logos/vendors/Alamjdouie-Hyundai-Auto_3Z7TlPQ.png
vendored
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
static/images/logos/vendors/Alamjdouie-Hyundai-Auto_IybmIRQ.png
vendored
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
static/images/logos/vendors/Aljomaih-Automotive-Company-2.png
vendored
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
static/images/logos/vendors/Aljomaih-Automotive-Company-3.png
vendored
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
static/images/logos/vendors/Aljomaih-Automotive-Company.png
vendored
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
static/images/logos/vendors/Aljomaih-Automotive-Company_PrrrHZs.png
vendored
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
templates/.DS_Store
vendored
@ -30,7 +30,10 @@
|
||||
min-height: 100vh;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
small, .small {
|
||||
font-size: 0.9rem;
|
||||
line-height: 0;
|
||||
}
|
||||
.btn {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
@ -45,7 +48,7 @@
|
||||
<!-- Main content goes here -->
|
||||
{% endblock %}
|
||||
|
||||
{% include 'footer.html' %}
|
||||
|
||||
|
||||
<!-- JavaScript Files -->
|
||||
|
||||
|
||||
@ -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>© 2024</span> <span>{% trans 'All right reserved' %}:</span> <a class="text-muted" href="https://tenhal.sa">{% trans 'Tenhal' %}</a><span> </span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
61
templates/inventory/add_colors.html
Normal 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 %}
|
||||
@ -31,7 +31,7 @@
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||
<div class="modal-content glossy-modal">
|
||||
<div class="modal-header bg-success">
|
||||
<div class="modal-header bg-primary text-light">
|
||||
<h5 class="modal-title"
|
||||
id="specificationsModalLabel">
|
||||
{% trans 'specifications'|upper %}
|
||||
@ -152,7 +152,6 @@
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-lg-6 col-xl-6">
|
||||
|
||||
<p class="fs-5">{% trans 'Financial Details' %}</p>
|
||||
{% if car.finances.exists %} {% for finance in car.finances.all %}
|
||||
<table class="table table-sm table-responsive mb-3 align-middle">
|
||||
@ -160,17 +159,29 @@
|
||||
<th>{% trans "Cost Price" %}</th>
|
||||
<td>{{ finance.cost_price }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% trans "Profit Margin" %}</th>
|
||||
<td>{{ finance.profit_margin|percentage }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% trans "Selling Price" %}</th>
|
||||
<td>{{ finance.selling_price }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% trans "VAT Rate" %}</th>
|
||||
<td>{{ finance.vat_rate|percentage }}</td>
|
||||
<tr>
|
||||
<td><small class="ms-5">{% trans "Administration Fee" %}</small></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>
|
||||
<th>{% trans "VAT Amount" %}</th>
|
||||
@ -200,25 +211,28 @@
|
||||
<p class="fs-5 mt-2">{% trans 'Colors Details' %}</p>
|
||||
<table class="table table-sm table-responsive align-middle">
|
||||
<tbody class=" align-middle">
|
||||
{% for color in car.colors.all %}
|
||||
{% if car.colors.exists %}
|
||||
{% for color in car.colors.all %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ color.get_color_type_display }}
|
||||
</td>
|
||||
<th>{% trans 'Exterior' %}</th>
|
||||
<td>
|
||||
<span>{{ color.name.get_local_name }}</span>
|
||||
<span>{{ color.exterior.get_local_name }}</span>
|
||||
</td>
|
||||
<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 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>
|
||||
{% 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>
|
||||
<td colspan="2">
|
||||
{% trans "No colors available for this car." %}
|
||||
@ -232,7 +246,7 @@
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="fs-5 mt-2">{% trans 'Reservations Details' %}</p>
|
||||
@ -250,22 +264,26 @@
|
||||
<tr>
|
||||
<td>{{ reservation.reserved_by.username }}</td>
|
||||
<td>{{ reservation.reserved_until }}</td>
|
||||
<td colspan="2">
|
||||
<td>
|
||||
{% if reservation.is_active %}
|
||||
<form method="post" action="{% url 'reservations' reservation.id %}">
|
||||
{% csrf_token %}
|
||||
{% csrf_token %}
|
||||
<button type="submit"
|
||||
name="action"
|
||||
value="renew"
|
||||
class="btn btn-sm btn-primary">
|
||||
<small>{% trans "renew" %}</small>
|
||||
class="btn btn-sm btn-success">
|
||||
<small>{% trans "Renew" %}</small>
|
||||
</button>
|
||||
<button type="submit"
|
||||
name="action"
|
||||
value="cancel"
|
||||
class="btn btn-sm btn-danger">
|
||||
class="btn btn-sm btn-secondary">
|
||||
<small>{% trans "Cancel" %}</small>
|
||||
</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<span class="badge bg-danger" style="width: 120px;">{% trans "Expired" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_filters %}
|
||||
{% load i18n %}
|
||||
{% load custom_filters %}
|
||||
|
||||
@ -25,87 +26,25 @@
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="post" class="needs-validation" novalidate>
|
||||
{% csrf_token %}
|
||||
<div class="row g-1">
|
||||
<div class="col-lg-4 col-xl-2">
|
||||
<label for="id_cost_price" class="form-label">{% trans 'Cost Price' %}</label>
|
||||
{{ form.cost_price|add_class:"form-control form-control-sm" }}
|
||||
<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>
|
||||
<form method="post" class="needs-validation" novalidate>
|
||||
<div class="row g-1">
|
||||
<div class="col-lg-4 col-xl-12">
|
||||
|
||||
{% csrf_token %}
|
||||
|
||||
{{ form|crispy }}
|
||||
</div>
|
||||
<div class="d-flex justify-content-end mt-4">
|
||||
<button type="submit" class="btn btn-sm btn-primary me-1">{% trans "Save Finance Details" %}</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-sm btn-danger">{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
<div class="row g-1">
|
||||
<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>
|
||||
</form>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- 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 %}
|
||||
@ -112,10 +112,12 @@
|
||||
<div class="col-lg-4 col-xl-3">
|
||||
<div class="card h-100 border-1 rounded shadow">
|
||||
<div class="card-body" id="year-container">
|
||||
|
||||
<label class="form-label"
|
||||
for="{{ form.year.id_for_label }}">
|
||||
{% trans 'Year' %}:
|
||||
</label>
|
||||
<span class="text-success fw-bold" id="year-check"></span>
|
||||
<input type="number"
|
||||
class="form-control form-control-sm"
|
||||
id="{{ form.year.id_for_label }}"
|
||||
@ -132,10 +134,12 @@
|
||||
<div class="col-lg-4 col-xl-3">
|
||||
<div class="card h-100 border-1 rounded shadow">
|
||||
<div id="make-container" class="card-body">
|
||||
<div class="status"></div>
|
||||
<label class="form-label"
|
||||
for="{{ form.id_car_make.id_for_label }}">
|
||||
{% trans 'make'|capfirst %}:
|
||||
</label>
|
||||
<span class="text-success fw-bold" id="make-check"></span>
|
||||
{{ form.id_car_make|add_class:"form-select form-select-sm" }}
|
||||
{% if form.id_car_make.errors %}
|
||||
<div class="text-danger small">
|
||||
@ -154,6 +158,7 @@
|
||||
for="{{ form.id_car_model.id_for_label }}">
|
||||
{% trans 'model'|capfirst %}:
|
||||
</label>
|
||||
<span class="text-success fw-bold" id="model-check"></span>
|
||||
<select class="form-select form-select-sm"
|
||||
id="{{ form.id_car_model.id_for_label }}"
|
||||
name="{{ form.id_car_model.html_name }}">
|
||||
@ -307,18 +312,30 @@
|
||||
</div>
|
||||
|
||||
<!-- Specifications Buttons -->
|
||||
<div class="col-lg-4 col-xl-6 justify-content-between">
|
||||
<button type="button"
|
||||
class="btn btn-danger mt-1"
|
||||
<div class="row g-1">
|
||||
<div class="btn-group">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-danger me-1"
|
||||
id="specification-btn"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#specificationsModal"
|
||||
disabled>{% trans 'specifications'|capfirst %}
|
||||
</button>
|
||||
<button type="submit"
|
||||
class="btn btn-primary mt-1"
|
||||
id="saveCarBtn" disabled>{% trans 'Save' %}
|
||||
</button>
|
||||
<!--<div class="form-group">-->
|
||||
<button type="submit"
|
||||
name="add_another"
|
||||
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>
|
||||
</main>
|
||||
</div>
|
||||
@ -364,7 +381,7 @@ document.addEventListener("DOMContentLoaded", function() {
|
||||
const yearBg = document.getElementById('year-container');
|
||||
const serieBg = document.getElementById('serie-container');
|
||||
const trimBg = document.getElementById('trim-container');
|
||||
const saveCarBtn = document.getElementById('saveCarBtn');
|
||||
/*const saveCarBtn = document.getElementById('saveCarBtn');*/
|
||||
|
||||
const ajaxUrl = "{% url 'ajax_handler' %}";
|
||||
|
||||
@ -373,7 +390,8 @@ document.addEventListener("DOMContentLoaded", function() {
|
||||
const videoElement = document.getElementById('video');
|
||||
const resultDisplay = document.getElementById('result');
|
||||
const fallbackButton = document.getElementById('ocr-fallback-btn');
|
||||
const codeReader = new ZXing.BrowserMultiFormatReader();
|
||||
let codeReader;
|
||||
codeReader = new ZXing.BrowserMultiFormatReader();
|
||||
let currentStream = null;
|
||||
|
||||
function showLoading() {
|
||||
@ -439,16 +457,19 @@ async function updateFields(vinData) {
|
||||
console.log(vinData)
|
||||
if (vinData.make_id) {
|
||||
makeSelect.value = vinData.make_id;
|
||||
document.getElementById("make-check").innerHTML = '✓';
|
||||
await loadModels(vinData.make_id);
|
||||
}
|
||||
if (vinData.model_id) {
|
||||
modelSelect.value = vinData.model_id;
|
||||
document.getElementById("model-check").innerHTML = '✓';
|
||||
await loadSeries(vinData.model_id);
|
||||
}
|
||||
if (vinData.year) {
|
||||
yearSelect.value = vinData.year;
|
||||
document.getElementById("year-check").innerHTML = '✓';
|
||||
}
|
||||
checkFormCompletion();
|
||||
/*checkFormCompletion();*/
|
||||
}
|
||||
|
||||
// Start the scanner
|
||||
@ -489,12 +510,12 @@ function stopScanner() {
|
||||
function resetDropdown(dropdown, placeholder) {
|
||||
dropdown.innerHTML = `<option value="">${placeholder}</option>`;
|
||||
}
|
||||
|
||||
/*
|
||||
function checkFormCompletion() {
|
||||
const isFormComplete = vinInput.value.length === 17 && stockTypeSelect.value;
|
||||
saveCarBtn.disabled = !isFormComplete;
|
||||
}
|
||||
|
||||
*/
|
||||
async function loadModels(makeId) {
|
||||
resetDropdown(modelSelect, '{% trans "Select" %}');
|
||||
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){
|
||||
specificationsContent.innerHTML = '';
|
||||
const response = await fetch(`${ajaxUrl}?action=get_specifications&trim_id=${trimId}`, {
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
@ -592,9 +614,9 @@ async function loadSpecifications(trimId){
|
||||
});
|
||||
|
||||
closeButton.addEventListener('click', closeModal);
|
||||
stockTypeSelect.addEventListener('change', checkFormCompletion);
|
||||
/*stockTypeSelect.addEventListener('change', checkFormCompletion);
|
||||
mileageInput.addEventListener('input', checkFormCompletion);
|
||||
remarksInput.addEventListener('input', checkFormCompletion);
|
||||
remarksInput.addEventListener('input', checkFormCompletion);*/
|
||||
decodeVinBtn.addEventListener('click', decodeVin);
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -91,9 +91,9 @@
|
||||
{% for choice in form.color.field.choices %}
|
||||
<div class="col-lg-3 col-xl-3 text-center">
|
||||
<label class="color-option" id="color-option-{{ forloop.counter }}">
|
||||
<input type="radio" name="color" value="{{ choice.0 }}"
|
||||
{% if choice.0 == form.color.value %}checked{% endif %}
|
||||
style="display: none;"
|
||||
<input type="radio" name="color" value="{{ choice.0 }}"
|
||||
{% if choice.0 == form.color.value %}checked{% endif %}
|
||||
style="display: none;"
|
||||
onchange="highlightSelectedColor({{ forloop.counter }})">
|
||||
<div class="color-box" style="background-color: rgb({{ choice.0 }});"></div>
|
||||
<span><small class="fw-lighter">{{ choice.1 }}</small></span>
|
||||
|
||||
82
templates/sales/quotation_detail.html
Normal 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 %}
|
||||
23
templates/sales/quotation_form.html
Normal 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 %}
|
||||
48
templates/sales/quotation_list.html
Normal 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 %}
|
||||
39
templates/sales/sales_order_detail.html
Normal 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 %}
|
||||
210
templates/vendors/vendors_list.html
vendored
@ -1,134 +1,110 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% block title %}{% trans 'Vendors'|capfirst %}{% endblock title %}
|
||||
{% block vendors %}<a class="nav-link active">{% trans "Vendors"|capfirst %}</a>{% endblock %}
|
||||
{% block title %}{{ _('Vendors')|capfirst }}{% endblock title %}
|
||||
{% block vendors %}<a class="nav-link active">{{ _("Vendors")|capfirst }}</a>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header fw-light mb-0">
|
||||
{% include 'breadcrumbs.html' %}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="container-fluid p-2">
|
||||
<form method="get">
|
||||
<div class="container-fluid p-3">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h6 class="mb-0">{{ _("Vendors")|capfirst }}</h6>
|
||||
<form method="get" class="d-inline-block">
|
||||
<div class="input-group input-group-sm">
|
||||
<button id="inputGroup-sizing-sm"
|
||||
class="btn btn-sm btn-secondary rounded-start" type="submit">
|
||||
{% trans 'search'|capfirst %}
|
||||
<button class="btn btn-secondary" type="submit">
|
||||
{{ _("Search")|capfirst }}
|
||||
</button>
|
||||
<input type="text"
|
||||
name="q"
|
||||
class="form-control form-control-sm rounded-end"
|
||||
value="{{ request.GET.q }}"
|
||||
aria-describedby="inputGroup-sizing-sm"/>
|
||||
<!-- Clear Button -->
|
||||
{% if request.GET.q %}
|
||||
<a href="{% url request.resolver_match.view_name %}"
|
||||
class="btn btn-sm btn-outline-danger ms-1 rounded">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
class="form-control"
|
||||
placeholder="{{ _('Enter vendor name') }}"
|
||||
value="{{ request.GET.q }}">
|
||||
{% if request.GET.q %}
|
||||
<a href="{% url request.resolver_match.view_name %}" class="btn btn-outline-danger ms-1">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<table class="table table-hover table-responsive-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans 'name'|capfirst %}</th>
|
||||
|
||||
<th>{% trans 'logo'|capfirst %}</th>
|
||||
<th>{% trans 'address'|capfirst %}</th>
|
||||
<th>{% trans 'actions'|capfirst %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if page_obj.object_list %}
|
||||
{% for vendor in page_obj.object_list %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ vendor.get_local_name }}
|
||||
</td>
|
||||
<td>
|
||||
{% if vendor.logo %}
|
||||
<img src="{% static 'images/' %}{{ vendor.logo }}" width="100" height="30">
|
||||
<div class="card-body p-0">
|
||||
<table class="table table-hover table-striped mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>{{ _("Name")|capfirst }}</th>
|
||||
<th>{{ _("Logo")|capfirst }}</th>
|
||||
<th>{{ _("Address")|capfirst }}</th>
|
||||
<th>{{ _("Actions")|capfirst }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if page_obj.object_list %}
|
||||
{% for vendor in vendors %}
|
||||
<tr>
|
||||
<td>{{ vendor.get_local_name }}</td>
|
||||
<td>
|
||||
<img src="{{ vendor.logo.url }}"
|
||||
alt="{{ vendor.get_local_name }}"
|
||||
class="img-thumbnail"
|
||||
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 %}
|
||||
<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 %}
|
||||
</td>
|
||||
<td>{{ vendor.address }}</td>
|
||||
<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">««</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">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link" aria-hidden="true">««</span>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link" aria-hidden="true">«</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% for num in page_obj.paginator.page_range %}
|
||||
{% if page_obj.number == num %}
|
||||
<li class="page-item active">
|
||||
<span class="page-link">{{ num }}</span>
|
||||
</li>
|
||||
{% 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">»</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">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link" aria-hidden="true">»</span>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link" aria-hidden="true">»»</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
</tbody>
|
||||
</table>
|
||||
</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">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% for num in page_obj.paginator.page_range %}
|
||||
{% if page_obj.number == num %}
|
||||
<li class="page-item active"><a class="page-link" href="?page={{ num }}">{{ num }}</a></li>
|
||||
{% else %}
|
||||
<li class="page-item"><a class="page-link" href="?page={{ num }}">{{ num }}</a></li>
|
||||
{% endif %}
|
||||
{% endfor %} {% if page_obj.has_next %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
42
testapi.py
@ -1,23 +1,23 @@
|
||||
|
||||
from vin import VIN
|
||||
# from vin import VIN
|
||||
import requests
|
||||
import json
|
||||
|
||||
vin_no = 'VR7ED9HP6SJ522156'
|
||||
|
||||
details = {
|
||||
# 'Description': VIN(vin_no).description,
|
||||
'make': VIN(vin_no).make,
|
||||
'year': VIN(vin_no).model_year,
|
||||
'model': VIN(vin_no).model,
|
||||
'trim': VIN(vin_no).trim,
|
||||
'Series': VIN(vin_no).series,
|
||||
'body_class': VIN(vin_no).body_class,
|
||||
'Type': VIN(vin_no).vehicle_type,
|
||||
'Electrification level': VIN(vin_no).electrification_level,
|
||||
}
|
||||
|
||||
print(details)
|
||||
#
|
||||
# vin_no = 'VR7ED9HP6SJ522156'
|
||||
#
|
||||
# details = {
|
||||
# # 'Description': VIN(vin_no).description,
|
||||
# 'make': VIN(vin_no).make,
|
||||
# 'year': VIN(vin_no).model_year,
|
||||
# 'model': VIN(vin_no).model,
|
||||
# 'trim': VIN(vin_no).trim,
|
||||
# 'Series': VIN(vin_no).series,
|
||||
# 'body_class': VIN(vin_no).body_class,
|
||||
# 'Type': VIN(vin_no).vehicle_type,
|
||||
# 'Electrification level': VIN(vin_no).electrification_level,
|
||||
# }
|
||||
#
|
||||
# print(details)
|
||||
|
||||
# from vininfo import Vin
|
||||
#
|
||||
@ -120,3 +120,11 @@ print(details)
|
||||
#
|
||||
# except Exception as 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)
|
||||
|
||||