This commit is contained in:
Marwan Alwali 2025-01-08 15:21:20 +03:00
parent 7fe829b362
commit bb10d9186e
155 changed files with 193987 additions and 424 deletions

BIN
.DS_Store vendored

Binary file not shown.

0
.idea/sqldialects.xml generated Normal file
View File

View File

@ -1,4 +1,4 @@
# Generated by Django 5.1.4 on 2025-01-06 23:09
# Generated by Django 5.1.4 on 2025-01-07 22:27
from django.db import migrations, models

2881
car_make01.json Normal file

File diff suppressed because it is too large Load Diff

4012
carmake_backup.json Normal file

File diff suppressed because it is too large Load Diff

45641
carmodel_backup.json Normal file

File diff suppressed because it is too large Load Diff

141095
carserie_backup.json Normal file

File diff suppressed because it is too large Load Diff

View File

Binary file not shown.

0
cartrim_backup.json Normal file
View File

29
create_json.py Normal file
View File

@ -0,0 +1,29 @@
import mysql.connector
import json
# Connect to MySQL
mysql_conn = mysql.connector.connect(
host='localhost',
user='root',
password='Kfsh&rc9788',
database='trucks2db'
)
cursor = mysql_conn.cursor()
# Get list of tables
cursor.execute("SHOW TABLES")
tables = cursor.fetchall()
for table in tables:
table_name = table[0]
cursor.execute(f"SELECT * FROM {table_name}")
rows = cursor.fetchall()
cursor.execute(f"DESCRIBE {table_name}")
columns = [column[0] for column in cursor.fetchall()]
# Convert rows to list of dictionaries
data = [dict(zip(columns, row)) for row in rows]
# Write to JSON file
with open(f'{table_name}.json', 'w') as f:
json.dump(data, f)

View File

@ -1,4 +1,4 @@
# Generated by Django 5.1.4 on 2025-01-06 23:09
# Generated by Django 5.1.4 on 2025-01-07 22:27
from django.db import migrations, models

View File

@ -1,4 +1,4 @@
# Generated by Django 5.1.4 on 2025-01-06 23:09
# Generated by Django 5.1.4 on 2025-01-07 22:27
import django.db.models.deletion
from django.db import migrations, models

View File

@ -14,153 +14,154 @@ from inventory.models import (
)
# Set up Django environment
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "your_project.settings")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "car_inventory.settings")
django.setup()
# Load the cleaned JSON data
with open("final_car_data.json", "r") as file:
with open("carmake_updated_backup.json", "r") as file:
data = json.load(file)
# Step 1: Insert CarMake
for item in tqdm(data["car_make"], desc="Inserting CarMake"):
for item in tqdm(data["inventory.car_make"], desc="Inserting CarMake"):
CarMake.objects.update_or_create(
id_car_make=item["id_car_make"],
id_car_make=item[id_car_make],
defaults={
"name": item["name"],
"arabic_name": item.get("arabic_name", ""),
"logo": item.get("Logo", ""),
"is_sa_import": item.get("is_sa_import", False),
"car_type": 1,
}
)
# Step 2: Insert CarModel
for item in tqdm(data["car_model"], desc="Inserting CarModel"):
CarMake.objects.get(id_car_make=item["id_car_make"])
CarModel.objects.update_or_create(
id_car_model=item["id_car_model"],
defaults={
"id_car_make_id": item["id_car_make"],
"name": item["name"],
"arabic_name": item.get("arabic_name", ""),
}
)
# Step 3: Insert CarSerie
for item in tqdm(data["car_serie"], desc="Inserting CarSerie"):
CarModel.objects.get(id_car_model=item["id_car_model"])
CarSerie.objects.update_or_create(
id_car_serie=item["id_car_serie"],
defaults={
"id_car_model_id": item["id_car_model"],
"name": item["name"],
"arabic_name": item.get("arabic_name", ""),
"year_begin": item.get("year_begin"),
"year_end": item.get("year_end"),
"generation_name": item.get("generation_name", ""),
}
)
# Step 4: Insert CarTrim
for item in tqdm(data["car_trim"], desc="Inserting CarTrim"):
CarSerie.objects.get(id_car_serie=item["id_car_serie"])
CarTrim.objects.update_or_create(
id_car_trim=item["id_car_trim"],
defaults={
"id_car_serie_id": item["id_car_serie"],
"name": item["name"],
"arabic_name": item.get("arabic_name", ""),
"start_production_year": item["start_production_year"],
"end_production_year": item["end_production_year"],
}
)
# Step 5: Insert CarEquipment
for item in tqdm(data["car_equipment"], desc="Inserting CarEquipment"):
CarTrim.objects.get(id_car_trim=item["id_car_trim"])
CarEquipment.objects.update_or_create(
id_car_equipment=item["id_car_equipment"],
defaults={
"id_car_trim_id": item["id_car_trim"],
"name": item["name"],
"year_begin": item.get("year"),
}
)
# Step 6: Insert CarSpecification (Parent specifications first)
parent_specs = [item for item in data["car_specification"] if item["id_parent"] is None]
child_specs = [item for item in data["car_specification"] if item["id_parent"] is not None]
for item in tqdm(parent_specs, desc="Inserting Parent CarSpecifications"):
CarSpecification.objects.update_or_create(
id_car_specification=item["id_car_specification"],
defaults={
"name": item["name"],
"arabic_name": item.get("arabic_name", ""),
"id_parent_id": None
}
)
for item in tqdm(child_specs, desc="Inserting Child CarSpecifications"):
CarSpecification.objects.get(id_car_specification=item["id_parent"])
CarSpecification.objects.update_or_create(
id_car_specification=item["id_car_specification"],
defaults={
"name": item["name"],
"arabic_name": item.get("arabic_name", ""),
"id_parent_id": item["id_parent"]
}
)
# Step 7: Insert CarSpecificationValue
for item in tqdm(data["car_specification_value"], desc="Inserting CarSpecificationValue"):
CarTrim.objects.get(id_car_trim=item["id_car_trim"])
CarSpecification.objects.get(id_car_specification=item["id_car_specification"])
CarSpecificationValue.objects.update_or_create(
id_car_specification_value=item["id_car_specification_value"],
defaults={
"id_car_trim_id": item["id_car_trim"],
"id_car_specification_id": item["id_car_specification"],
"value": item["value"],
"unit": item.get("unit", ""),
}
)
# Step 8: Insert CarOption (Parent options first)
parent_options = [item for item in data["car_option"] if item["id_parent"] is None]
child_options = [item for item in data["car_option"] if item["id_parent"] is not None]
for item in tqdm(parent_options, desc="Inserting Parent CarOptions"):
CarOption.objects.update_or_create(
id_car_option=item["id_car_option"],
defaults={
"name": item["name"],
"arabic_name": item.get("arabic_name", ""),
"id_parent_id": None
}
)
for item in tqdm(child_options, desc="Inserting Child CarOptions"):
CarOption.objects.get(id_car_option=item["id_parent"])
CarOption.objects.update_or_create(
id_car_option=item["id_car_option"],
defaults={
"name": item["name"],
"arabic_name": item.get("arabic_name", ""),
"id_parent_id": item["id_parent"]
}
)
# Step 9: Insert CarOptionValue
for item in tqdm(data["car_option_value"], desc="Inserting CarOptionValue"):
CarEquipment.objects.get(id_car_equipment=item["id_car_equipment"])
CarOption.objects.get(id_car_option=item["id_car_option"])
CarOptionValue.objects.update_or_create(
id_car_option_value=item["id_car_option_value"],
defaults={
"id_car_option_id": item["id_car_option"],
"id_car_equipment_id": item["id_car_equipment"],
"is_base": item["is_base"],
}
)
# # Step 2: Insert CarModel
# for item in tqdm(data["car_model"], desc="Inserting CarModel"):
# CarMake.objects.get(id_car_make=item["id_car_make"])
# CarModel.objects.update_or_create(
# id_car_model=item["id_car_model"],
# defaults={
# "id_car_make_id": item["id_car_make"],
# "name": item["name"],
# "arabic_name": item.get("arabic_name", ""),
# }
# )
#
# # Step 3: Insert CarSerie
# for item in tqdm(data["car_serie"], desc="Inserting CarSerie"):
# CarModel.objects.get(id_car_model=item["id_car_model"])
# CarSerie.objects.update_or_create(
# id_car_serie=item["id_car_serie"],
# defaults={
# "id_car_model_id": item["id_car_model"],
# "name": item["name"],
# "arabic_name": item.get("arabic_name", ""),
# "year_begin": item.get("year_begin"),
# "year_end": item.get("year_end"),
# "generation_name": item.get("generation_name", ""),
# }
# )
#
# # Step 4: Insert CarTrim
# for item in tqdm(data["car_trim"], desc="Inserting CarTrim"):
# CarSerie.objects.get(id_car_serie=item["id_car_serie"])
# CarTrim.objects.update_or_create(
# id_car_trim=item["id_car_trim"],
# defaults={
# "id_car_serie_id": item["id_car_serie"],
# "name": item["name"],
# "arabic_name": item.get("arabic_name", ""),
# "start_production_year": item["start_production_year"],
# "end_production_year": item["end_production_year"],
# }
# )
#
# # Step 5: Insert CarEquipment
# for item in tqdm(data["car_equipment"], desc="Inserting CarEquipment"):
# CarTrim.objects.get(id_car_trim=item["id_car_trim"])
# CarEquipment.objects.update_or_create(
# id_car_equipment=item["id_car_equipment"],
# defaults={
# "id_car_trim_id": item["id_car_trim"],
# "name": item["name"],
# "year_begin": item.get("year"),
# }
# )
#
# # Step 6: Insert CarSpecification (Parent specifications first)
# parent_specs = [item for item in data["car_specification"] if item["id_parent"] is None]
# child_specs = [item for item in data["car_specification"] if item["id_parent"] is not None]
#
# for item in tqdm(parent_specs, desc="Inserting Parent CarSpecifications"):
# CarSpecification.objects.update_or_create(
# id_car_specification=item["id_car_specification"],
# defaults={
# "name": item["name"],
# "arabic_name": item.get("arabic_name", ""),
# "id_parent_id": None
# }
# )
#
# for item in tqdm(child_specs, desc="Inserting Child CarSpecifications"):
# CarSpecification.objects.get(id_car_specification=item["id_parent"])
# CarSpecification.objects.update_or_create(
# id_car_specification=item["id_car_specification"],
# defaults={
# "name": item["name"],
# "arabic_name": item.get("arabic_name", ""),
# "id_parent_id": item["id_parent"]
# }
# )
#
# # Step 7: Insert CarSpecificationValue
# for item in tqdm(data["car_specification_value"], desc="Inserting CarSpecificationValue"):
# CarTrim.objects.get(id_car_trim=item["id_car_trim"])
# CarSpecification.objects.get(id_car_specification=item["id_car_specification"])
# CarSpecificationValue.objects.update_or_create(
# id_car_specification_value=item["id_car_specification_value"],
# defaults={
# "id_car_trim_id": item["id_car_trim"],
# "id_car_specification_id": item["id_car_specification"],
# "value": item["value"],
# "unit": item.get("unit", ""),
# }
# )
#
# # Step 8: Insert CarOption (Parent options first)
# parent_options = [item for item in data["car_option"] if item["id_parent"] is None]
# child_options = [item for item in data["car_option"] if item["id_parent"] is not None]
#
# for item in tqdm(parent_options, desc="Inserting Parent CarOptions"):
# CarOption.objects.update_or_create(
# id_car_option=item["id_car_option"],
# defaults={
# "name": item["name"],
# "arabic_name": item.get("arabic_name", ""),
# "id_parent_id": None
# }
# )
#
# for item in tqdm(child_options, desc="Inserting Child CarOptions"):
# CarOption.objects.get(id_car_option=item["id_parent"])
# CarOption.objects.update_or_create(
# id_car_option=item["id_car_option"],
# defaults={
# "name": item["name"],
# "arabic_name": item.get("arabic_name", ""),
# "id_parent_id": item["id_parent"]
# }
# )
#
# # Step 9: Insert CarOptionValue
# for item in tqdm(data["car_option_value"], desc="Inserting CarOptionValue"):
# CarEquipment.objects.get(id_car_equipment=item["id_car_equipment"])
# CarOption.objects.get(id_car_option=item["id_car_option"])
# CarOptionValue.objects.update_or_create(
# id_car_option_value=item["id_car_option_value"],
# defaults={
# "id_car_option_id": item["id_car_option"],
# "id_car_equipment_id": item["id_car_equipment"],
# "is_base": item["is_base"],
# }
# )
print("Data population completed successfully.")

BIN
inventory/.DS_Store vendored

Binary file not shown.

BIN
inventory/data/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -9,7 +9,7 @@ class Command(BaseCommand):
def handle(self, *args, **kwargs):
client = OpenAI(api_key=settings.OPENAI_API_KEY)
car_option = CarOption.objects.all()[8820:]
car_option = CarOption.objects.all()[10300:]
total = car_option.count()
print(f'Translating {total} names...')
for index, car_option in enumerate(car_option, start=1):

View File

@ -1,4 +1,4 @@
# Generated by Django 5.1.4 on 2025-01-06 23:09
# Generated by Django 5.1.4 on 2025-01-07 22:27
import django.db.models.deletion
import inventory.mixins
@ -57,6 +57,7 @@ class Migration(migrations.Migration):
('arabic_name', models.CharField(blank=True, max_length=255, null=True)),
('logo', models.ImageField(blank=True, null=True, upload_to='car_make', verbose_name='logo')),
('is_sa_import', models.BooleanField(default=False)),
('car_type', models.SmallIntegerField(choices=[(1, 'Car'), (2, 'light commercial'), (3, 'truck trailer'), (4, 'trailer'), (5, 'truck'), (6, 'bus')])),
],
options={
'verbose_name': 'Make',
@ -166,20 +167,6 @@ class Migration(migrations.Migration):
},
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
),
migrations.CreateModel(
name='CarGeneration',
fields=[
('id_car_generation', models.AutoField(primary_key=True, serialize=False)),
('name', models.CharField(blank=True, max_length=255, null=True)),
('arabic_name', models.CharField(blank=True, max_length=255, null=True)),
('year_begin', models.CharField(blank=True, max_length=255, null=True)),
('year_end', models.CharField(blank=True, max_length=255, null=True)),
('id_car_model', models.ForeignKey(db_column='id_car_model', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel')),
],
options={
'verbose_name': 'Generation',
},
),
migrations.AddField(
model_name='car',
name='id_car_model',
@ -204,6 +191,7 @@ class Migration(migrations.Migration):
('id_car_option_value', models.AutoField(primary_key=True, serialize=False)),
('value', models.CharField(max_length=500)),
('unit', models.CharField(blank=True, max_length=255, null=True)),
('is_base', models.IntegerField()),
('id_car_equipment', models.ForeignKey(db_column='id_car_equipment', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carequipment')),
('id_car_option', models.ForeignKey(db_column='id_car_option', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.caroption')),
],
@ -252,8 +240,8 @@ class Migration(migrations.Migration):
name='CarSpecification',
fields=[
('id_car_specification', models.AutoField(primary_key=True, serialize=False)),
('name', models.CharField(blank=True, max_length=255, null=True)),
('arabic_name', models.CharField(blank=True, max_length=255, null=True)),
('name', models.CharField(max_length=255)),
('arabic_name', models.CharField(max_length=255)),
('id_parent', models.ForeignKey(blank=True, db_column='id_parent', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carspecification')),
],
options={

View File

@ -1,31 +0,0 @@
# Generated by Django 5.1.4 on 2025-01-07 01:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='caroptionvalue',
name='is_base',
field=models.IntegerField(default=1, max_length=1),
preserve_default=False,
),
migrations.AlterField(
model_name='carspecification',
name='arabic_name',
field=models.CharField(default='-', max_length=255),
preserve_default=False,
),
migrations.AlterField(
model_name='carspecification',
name='name',
field=models.CharField(default='-', max_length=255),
preserve_default=False,
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 5.1.4 on 2025-01-07 01:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0002_caroptionvalue_is_base_and_more'),
]
operations = [
migrations.AlterField(
model_name='caroptionvalue',
name='is_base',
field=models.IntegerField(),
),
]

View File

@ -54,6 +54,8 @@ class UnitOfMeasure(models.TextChoices):
SQUARE_METER = 'SQ_M', 'Square Meter'
PIECE = 'PC', 'Piece'
BUNDLE = 'BDL', 'Bundle'
class VatRate(models.Model):
rate = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal('0.15'))
is_active = models.BooleanField(default=True)
@ -65,12 +67,23 @@ class VatRate(models.Model):
def __str__(self):
return f"Rate: {self.rate}%"
class CarType(models.IntegerChoices):
CAR = 1, _('Car')
LIGHT_COMMERCIAL = 2, _('light commercial')
TRUCK_TRAILER = 3, _('truck trailer')
TRAILER = 4, _('trailer')
TRUCK = 5, _('truck')
BUS = 6, _('bus')
class CarMake(models.Model, LocalizedNameMixin):
id_car_make = models.AutoField(primary_key=True)
name = models.CharField(max_length=255, blank=True, null=True)
arabic_name = models.CharField(max_length=255, blank=True, null=True)
logo = models.ImageField(_("logo"), upload_to="car_make", blank=True, null=True)
is_sa_import = models.BooleanField(default=False)
car_type = models.SmallIntegerField(choices=CarType.choices)
def __str__(self):
return self.name
@ -92,21 +105,6 @@ class CarModel(models.Model, LocalizedNameMixin):
verbose_name = "Model"
class CarGeneration(models.Model):
id_car_generation = models.AutoField(primary_key=True)
id_car_model = models.ForeignKey(CarModel, models.DO_NOTHING, db_column='id_car_model')
name = models.CharField(max_length=255, blank=True, null=True)
arabic_name = models.CharField(max_length=255, blank=True, null=True)
year_begin = models.CharField(max_length=255, blank=True, null=True)
year_end = models.CharField(max_length=255, blank=True, null=True)
def __str__(self):
return self.name
class Meta:
verbose_name = "Generation"
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")
@ -369,34 +367,42 @@ class CarFinance(models.Model):
selling_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Selling Price"))
discount_amount = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Discount Amount"),
default=Decimal('0.00'))
# profit_margin = models.DecimalField(max_digits=14,
# decimal_places=2,
# verbose_name=_("Profit Margin"),
# editable=False)
# vat_amount = models.DecimalField(max_digits=14,
# decimal_places=2,
# verbose_name=_("Vat Amount"),
# editable=False,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'))
@property
def total(self):
total = self.selling_price
def total(self):
total = 0
if self.additional_services.count() != 0:
total_additional_services = sum(x.default_amount for x in self.additional_services.all())
total += total_additional_services
return total
@property
def total_discount(self):
total = self.selling_price + total_additional_services
else:
total = self.selling_price
if self.discount_amount != 0:
total = self.total - self.discount_amount
return total
return self.total
@property
def total_vat(self):
return self.total_discount + self.vat_amount
total = total - self.discount_amount
return total
@property
def vat_amount(self):
vat = VatRate.objects.filter(is_active=True).first()
if vat:
return (self.total_discount * Decimal(vat.vat_rate)).quantize(Decimal('0.01'))
return Decimal('0.00')
return (self.total * vat.vat_rate).quantize(Decimal('0.01'))
@property
def total_vat(self):
return self.total + self.vat_amount

View File

@ -1,4 +1,3 @@
from decimal import Decimal
from django.shortcuts import redirect
from django.contrib import messages
from django.utils import timezone
@ -91,11 +90,4 @@ def reserve_car(car,request):
except Exception as e:
messages.error(request, f"Error reserving car: {e}")
return redirect("car_detail", pk=car.pk)
def calculate_vat_amount(amount):
vat = models.VatRate.objects.filter(is_active=True).first()
if vat:
return ((amount * Decimal(vat.vat_rate)).quantize(Decimal('0.01')),vat.vat_rate)
return amount
return redirect("car_detail", pk=car.pk)

View File

@ -59,13 +59,7 @@ from . import models, forms
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin
from django.contrib.auth.models import Group
from .utils import (
calculate_vat_amount,
get_calculations,
reserve_car,
send_email,
get_user_type,
)
from .utils import get_calculations, reserve_car, send_email, get_user_type
from django.contrib.auth.models import User
from allauth.account import views
from django.db.models import Count, F, Value
@ -635,8 +629,8 @@ class CustomCardCreateView(LoginRequiredMixin, CreateView):
@login_required()
def reserve_car_view(request, car_id):
if request.method == "POST":
car = get_object_or_404(models.Car, pk=car_id)
if car.is_reserved():
car = get_object_or_404(models.Car, pk=car_id)
if car.is_reserved():
messages.error(request, _("This car is already reserved."))
return redirect("car_detail", pk=car.pk)
response = reserve_car(car, request)
@ -746,14 +740,6 @@ class CustomerDetailView(LoginRequiredMixin, DetailView):
template_name = "customers/view_customer.html"
context_object_name = "customer"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
name = f"{context['customer'].first_name} {context['customer'].middle_name} {context['customer'].last_name}"
context["estimates"] = self.request.entity.get_estimates().filter(
customer__customer_name=name
)
return context
class CustomerCreateView(
LoginRequiredMixin,
@ -1634,7 +1620,6 @@ def bank_account_delete(request, pk):
# Accounts
class AccountListView(LoginRequiredMixin, ListView):
model = AccountModel
template_name = "ledger/coa_accounts/account_list.html"
@ -1800,7 +1785,7 @@ def create_estimate(request):
"quantity": float(item.get("quantity")),
"unit_cost": car_instance.finances.cost_price,
"unit_revenue": car_instance.finances.selling_price,
"total_amount": (car_instance.finances.total_vat)
"total_amount": car_instance.finances.cost_price
* int(item.get("quantity")),
}
)
@ -1822,8 +1807,7 @@ def create_estimate(request):
"unit_cost": instance.finances.cost_price,
"unit_revenue": instance.finances.selling_price,
"quantity": float(quantities),
"total_amount": instance.finances.total_vat
* int(quantities),
"total_amount": instance.finances.total * int(quantities),
}
}
@ -1836,9 +1820,9 @@ def create_estimate(request):
if isinstance(items, list):
for item in items:
item_instance = ItemModel.objects.get(pk=item)
instance = models.Car.objects.get(vin=item_instance.name)
reserve_car(instance, request)
instance = models.Car.objects.get(vin=item_instance.name)
response = reserve_car(instance, request)
else:
item_instance = ItemModel.objects.get(pk=items)
instance = models.Car.objects.get(vin=item_instance.name)
@ -1853,6 +1837,15 @@ def create_estimate(request):
}
)
# except Exception as e:
# return JsonResponse(
# {
# "status": "error",
# "message": f"An error occurred while processing the request: {str(e)}",
# },
# status=400,
# )
form = EstimateModelCreateForm(entity_slug=entity.slug, user_model=entity.admin)
car_list = models.Car.objects.filter(
dealer=dealer, finances__selling_price__gt=0
@ -1881,27 +1874,25 @@ class EstimateDetailView(LoginRequiredMixin, DetailView):
estimate = kwargs.get("object")
if estimate.get_itemtxs_data():
total = sum(
float(
models.Car.objects.get(
(
x.ce_revenue_estimate
- models.Car.objects.get(
vin=x.item_model.name
).finances.total
).finances.discount_amount
)
* float(x.ce_quantity)
for x in estimate.get_itemtxs_data()[0].all()
)
discount_amount = sum(
models.CarFinance.objects.get(
car__vin=i.item_model.name
).discount_amount
for i in estimate.get_itemtxs_data()[0].all()
)
vat = models.VatRate.objects.filter(is_active=True).first()
grand_total = float(total) - float(discount_amount)
vat_amount = round(float(grand_total) * float(vat.vat_rate), 2)
# Calculate VAT and total with 2 decimal places
vat_amount = round(total * vat.vat_rate, 2) # Round to 2 decimal places
grand_total = round(
(total * vat.vat_rate) + total, 2
) # Round to 2 decimal places
# Add values to the context
kwargs["vat_amount"] = vat_amount
kwargs["total"] = grand_total + vat_amount
kwargs["discount_amount"] = discount_amount
kwargs["total"] = grand_total
kwargs["vat"] = vat.rate
kwargs["invoice"] = (
InvoiceModel.objects.all().filter(ce_model=estimate).first()
@ -1950,25 +1941,52 @@ def estimate_mark_as(request, pk):
if mark == "review":
if not estimate.can_review():
messages.error(request, "Estimate is not ready for review")
return redirect("estimate_detail", pk=estimate.pk)
return redirect("estimate_detail", pk=estimate.pk)
estimate.mark_as_review()
elif mark == "approved":
if not estimate.can_approve():
messages.error(request, "Estimate is not ready for approval")
return redirect("estimate_detail", pk=estimate.pk)
estimate.mark_as_approved()
messages.success(request, "Estimate approved successfully.")
estimate.mark_as_approved()
messages.success(request, "Estimate approved successfully.")
elif mark == "rejected":
if not estimate.can_cancel():
messages.error(request, "Estimate is not ready for rejection")
return redirect("estimate_detail", pk=estimate.pk)
estimate.mark_as_canceled()
messages.success(request, "Estimate canceled successfully.")
messages.success(request, "Estimate canceled successfully.")
elif mark == "completed":
if not estimate.can_complete():
messages.error(request, "Estimate is not ready for completion")
return redirect("estimate_detail", pk=estimate.pk)
# invoice = entity.create_invoice(customer_model=estimate.customer,
# terms=estimate.terms,
# cash_account=entity.get_default_coa_accounts().get(name="Cash"),
# prepaid_account=entity.get_default_coa_accounts().get(name="Accounts Receivable"),
# coa_model=entity.get_default_coa()
# )
# unit_items = estimate.get_itemtxs_data()[0]
# invoice_itemtxs = {
# i.item_model.item_number: {
# 'unit_cost': i.ce_unit_cost_estimate,
# 'quantity': i.ce_quantity,
# 'total_amount': i.ce_cost_estimate
# } for i in unit_items
# }
# invoice_itemtxs = invoice.migrate_itemtxs(itemtxs=invoice_itemtxs,
# commit=True,
# operation=InvoiceModel.ITEMIZE_APPEND)
# invoice.bind_estimate(estimate)
# invoice.mark_as_review()
# estimate.mark_as_completed()
# estimate.save()
# invoice.save()
estimate.save()
messages.success(request, "Estimate marked as " + mark.upper())
@ -1994,27 +2012,15 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView):
def get_context_data(self, **kwargs):
invoice = kwargs.get("object")
vat = models.VatRate.objects.filter(is_active=True).first()
if invoice.get_itemtxs_data():
total = sum(
float(x.ce_revenue_estimate) * float(x.ce_quantity)
for x in invoice.ce_model.get_itemtxs_data()[0].all()
x.unit_cost * x.quantity for x in invoice.get_itemtxs_data()[0].all()
)
discount_amount = sum(
models.CarFinance.objects.get(
car__vin=i.item_model.name
).discount_amount
for i in invoice.get_itemtxs_data()[0].all()
)
grand_total = float(total) - float(discount_amount)
vat_amount = round(float(grand_total) * float(vat.vat_rate), 2)
kwargs["vat_amount"] = vat_amount
kwargs["total"] = grand_total + vat_amount
kwargs["discount_amount"] = discount_amount
total = int(total)
vat = models.VatRate.objects.filter(is_active=True).first()
kwargs["vat_amount"] = total * vat.vat_rate
kwargs["total"] = (total * vat.vat_rate) + total
kwargs["vat"] = vat.rate
kwargs["payments"] = JournalEntryModel.objects.filter(
ledger=invoice.ledger
@ -2074,27 +2080,13 @@ def invoice_create(request, pk):
invoice_model.save()
unit_items = estimate.get_itemtxs_data()[0]
itemtxs = []
for item in unit_items:
car = models.Car.objects.get(vin=item.item_model.name)
itemtxs.append(
{
"item_number": item.item_model.item_number,
"unit_cost": car.finances.total_vat,
"unit_revenue": car.finances.total_vat,
"quantity": item.ce_quantity,
"total_amount": float(car.finances.total_vat)
* float(item.ce_quantity),
}
)
invoice_itemtxs = {
i.get("item_number"): {
"unit_cost": i.get("unit_cost"),
"quantity": i.get("quantity"),
"total_amount": i.get("total_amount"),
i.item_model.item_number: {
"unit_cost": i.ce_unit_cost_estimate,
"quantity": i.ce_quantity,
"total_amount": i.ce_cost_estimate,
}
for i in itemtxs
for i in unit_items
}
invoice_itemtxs = invoice_model.migrate_itemtxs(
@ -2240,6 +2232,59 @@ class UserActivityLogListView(ListView):
return queryset
# email
def send_email_view(request, pk):
estimate = get_object_or_404(EstimateModel, pk=pk)
if request.method == "POST":
# if not estimate.can_review():
# messages.error(request, "Estimate is not ready for review")
# return redirect("estimate_detail", pk=estimate.pk)
if not estimate.get_itemtxs_data()[0]:
messages.error(request, "Estimate has no items")
return redirect("estimate_detail", pk=estimate.pk)
send_email(
"manager@tenhal.com",
request.POST.get("to"),
request.POST.get("subject"),
request.POST.get("message"),
)
# estimate.mark_as_review()
messages.success(request, "Email sent successfully!")
return redirect("estimate_detail", pk=estimate.pk)
link = reverse_lazy("estimate_preview", kwargs={"pk": estimate.pk})
msg = f"""
السلام عليكم
Dear {estimate.customer.customer_name},
أود أن أشارككم تقدير المشروع الذي ناقشناه. يرجى العثور على الوثيقة التفصيلية للمقترح المرفقة.
I hope this email finds you well. I wanted to share with you the estimate for the project we discussed. Please find the detailed estimate document attached.
يرجى مراجعة المقترح وإعلامي إذا كانت لديك أي أسئلة أو مخاوف. إذا كانت كل شيء يبدو جيدًا، يمكننا المضي قدمًا في المشروع.
Please review the estimate and let me know if you have any questions or concerns. If everything looks good, we can proceed with the project.
Estimate Link:
{link}
شكراً لاهتمامكم بهذا الأمر.
Thank you for your attention to this matter.
تحياتي,
Best regards,
[Your Name]
[Your Position]
[Your Company]
[Your Contact Information]
"""
return render(
request,
"sales/estimates/estimate_send.html",
{"estimate": estimate, "message": msg},
)
# CRM RELATED VIEWS
def create_lead(request, pk):
customer = get_object_or_404(models.Customer, pk=pk)
@ -2414,73 +2459,3 @@ class SubscriptionPlans(ListView):
model = models.SubscriptionPlan
template_name = "subscriptions/subscription_plan.html"
context_object_name = "plans"
# email
def send_email_view(request, pk):
estimate = get_object_or_404(EstimateModel, pk=pk)
if request.method == "POST":
# if not estimate.can_review():
# messages.error(request, "Estimate is not ready for review")
# return redirect("estimate_detail", pk=estimate.pk)
if not estimate.get_itemtxs_data()[0]:
messages.error(request, "Estimate has no items")
return redirect("estimate_detail", pk=estimate.pk)
send_email(
"manager@tenhal.com",
request.POST.get("to"),
request.POST.get("subject"),
request.POST.get("message"),
)
estimate.mark_as_review()
messages.success(request, "Email sent successfully!")
return redirect("estimate_detail", pk=estimate.pk)
link = reverse_lazy("estimate_preview", kwargs={"pk": estimate.pk})
msg = f"""
السلام عليكم
Dear {estimate.customer.customer_name},
أود أن أشارككم تقدير المشروع الذي ناقشناه. يرجى العثور على الوثيقة التفصيلية للمقترح المرفقة.
I hope this email finds you well. I wanted to share with you the estimate for the project we discussed. Please find the detailed estimate document attached.
يرجى مراجعة المقترح وإعلامي إذا كانت لديك أي أسئلة أو مخاوف. إذا كانت كل شيء يبدو جيدًا، يمكننا المضي قدمًا في المشروع.
Please review the estimate and let me know if you have any questions or concerns. If everything looks good, we can proceed with the project.
Estimate Link:
{link}
شكراً لاهتمامكم بهذا الأمر.
Thank you for your attention to this matter.
تحياتي,
Best regards,
[Your Name]
[Your Position]
[Your Company]
[Your Contact Information]
"""
return render(
request,
"sales/estimates/estimate_send.html",
{"estimate": estimate, "message": msg},
)
# errors
def custom_page_not_found_view(request, exception):
return render(request, "errors/404.html", {})
def custom_error_view(request, exception=None):
return render(request, "errors/500.html", {})
def custom_permission_denied_view(request, exception=None):
return render(request, "errors/403.html", {})
def custom_bad_request_view(request, exception=None):
return render(request, "errors/400.html", {})

View File

@ -1,5 +1,9 @@
import os
import pymysql
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "car_inventory.settings")
django.setup()
import json
from datetime import datetime
from tqdm import tqdm
@ -11,10 +15,10 @@ from inventory.models import (
# Step 1: Perform MySQL Dump
def dump_mysql_database():
print("Starting MySQL dump...")
db_user = "username"
db_password = "password"
db_name = "haikaldb"
dump_file = f"haikaldb_{datetime.now().strftime('%Y%m%d')}.sql"
db_user = "root"
db_password = "Kfsh&rc9788"
db_name = "trucks2db"
dump_file = f"trucks2db_{datetime.now().strftime('%Y%m%d')}.sql"
os.system(f"mysqldump -u {db_user} -p{db_password} {db_name} > {dump_file}")
print(f"MySQL dump completed: {dump_file}")
@ -23,9 +27,9 @@ def export_database_to_json():
print("Starting export to JSON...")
db_config = {
'host': 'localhost',
'user': 'username',
'password': 'password',
'database': 'haikaldb'
'user': 'root',
'password': 'Kfsh&rc9788',
'database': 'trucks2db',
}
connection = pymysql.connect(**db_config)
@ -41,9 +45,9 @@ def export_database_to_json():
columns = [col[0] for col in cursor.description]
database_json[table_name] = [dict(zip(columns, row)) for row in rows]
json_file_name = "database_export.json"
with open(json_file_name, "w") as json_file:
json.dump(database_json, json_file, indent=4)
json_file_name = "trucks20250101.json"
with open(json_file_name, "w") as file:
json.dump(database_json, file, indent=4, ensure_ascii=False)
connection.close()
print(f"Database exported to JSON successfully: {json_file_name}")
@ -182,7 +186,7 @@ def process_json_data(json_file_name):
def main():
dump_mysql_database() # Step 1: Dump the database
json_file_name = export_database_to_json() # Step 2: Export to JSON
process_json_data(json_file_name) # Step 3: Process the JSON
# process_json_data(json_file_name) # Step 3: Process the JSON
if __name__ == "__main__":
main()

BIN
static/.DS_Store vendored

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

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