additinal services, and estimate + invoice
This commit is contained in:
parent
7fe829b362
commit
9ccb432d19
@ -14,8 +14,8 @@ from inventory.models import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Set up Django environment
|
# Set up Django environment
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "your_project.settings")
|
# os.environ.setdefault("DJANGO_SETTINGS_MODULE", "your_project.settings")
|
||||||
django.setup()
|
# django.setup()
|
||||||
|
|
||||||
# Load the cleaned JSON data
|
# Load the cleaned JSON data
|
||||||
with open("final_car_data.json", "r") as file:
|
with open("final_car_data.json", "r") as file:
|
||||||
|
|||||||
@ -190,7 +190,7 @@ class CarUpdateForm(forms.ModelForm, AddClassMixin):
|
|||||||
|
|
||||||
class CarFinanceForm(AddClassMixin, forms.ModelForm):
|
class CarFinanceForm(AddClassMixin, forms.ModelForm):
|
||||||
additional_finances = forms.ModelMultipleChoiceField(
|
additional_finances = forms.ModelMultipleChoiceField(
|
||||||
queryset=ItemModel.objects.filter(item_role=ItemModel.ITEM_ROLE_SERVICE),
|
queryset=AdditionalServices.objects.all(),
|
||||||
widget=forms.CheckboxSelectMultiple(attrs={"class": "form-check-input"}),
|
widget=forms.CheckboxSelectMultiple(attrs={"class": "form-check-input"}),
|
||||||
required=False,
|
required=False,
|
||||||
)
|
)
|
||||||
@ -202,16 +202,15 @@ class CarFinanceForm(AddClassMixin, forms.ModelForm):
|
|||||||
"profit_margin",
|
"profit_margin",
|
||||||
"vat_amount",
|
"vat_amount",
|
||||||
"total",
|
"total",
|
||||||
"vat_rate",
|
|
||||||
"additional_services",
|
"additional_services",
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
# def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
# super().__init__(*args, **kwargs)
|
||||||
if self.instance.pk:
|
# if self.instance.pk:
|
||||||
self.fields[
|
# self.fields[
|
||||||
"additional_finances"
|
# "additional_finances"
|
||||||
].initial = self.instance.additional_services.all()
|
# ].initial = self.instance.additional_services.all()
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
instance = super().save()
|
instance = super().save()
|
||||||
@ -367,18 +366,6 @@ class CarSelectionTable(tables.Table):
|
|||||||
|
|
||||||
|
|
||||||
class WizardForm1(forms.Form):
|
class WizardForm1(forms.Form):
|
||||||
username = forms.CharField(
|
|
||||||
widget=forms.TextInput(
|
|
||||||
attrs={
|
|
||||||
"class": "form-control",
|
|
||||||
"placeholder": "Username",
|
|
||||||
"required": "required",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
error_messages={
|
|
||||||
"required": _("You must add a username."),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
email = forms.EmailField(
|
email = forms.EmailField(
|
||||||
widget=forms.EmailInput(
|
widget=forms.EmailInput(
|
||||||
attrs={
|
attrs={
|
||||||
|
|||||||
@ -0,0 +1,26 @@
|
|||||||
|
# Generated by Django 4.2.17 on 2025-01-08 08:42
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
|
||||||
|
('inventory', '0003_alter_caroptionvalue_is_base'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='additionalservices',
|
||||||
|
name='item',
|
||||||
|
field=models.OneToOneField(default='', on_delete=django.db.models.deletion.CASCADE, to='django_ledger.itemmodel', verbose_name='Item'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='additional_services',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='additional_finances', to='inventory.additionalservices'),
|
||||||
|
),
|
||||||
|
]
|
||||||
20
inventory/migrations/0005_alter_additionalservices_item.py
Normal file
20
inventory/migrations/0005_alter_additionalservices_item.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 4.2.17 on 2025-01-08 08:43
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
|
||||||
|
('inventory', '0004_additionalservices_item_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='additionalservices',
|
||||||
|
name='item',
|
||||||
|
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='django_ledger.itemmodel', verbose_name='Item'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -28,7 +28,7 @@ from django.utils.timezone import now
|
|||||||
from .utilities.financials import get_financial_value, get_total, get_total_financials
|
from .utilities.financials import get_financial_value, get_total, get_total_financials
|
||||||
from django.db.models import FloatField
|
from django.db.models import FloatField
|
||||||
from .mixins import LocalizedNameMixin
|
from .mixins import LocalizedNameMixin
|
||||||
from django_ledger.models import EntityModel
|
from django_ledger.models import EntityModel,ItemModel
|
||||||
|
|
||||||
|
|
||||||
class DealerUserManager(UserManager):
|
class DealerUserManager(UserManager):
|
||||||
@ -59,9 +59,6 @@ class VatRate(models.Model):
|
|||||||
is_active = models.BooleanField(default=True)
|
is_active = models.BooleanField(default=True)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
@property
|
|
||||||
def vat_rate(self):
|
|
||||||
return self.rate / 100
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Rate: {self.rate}%"
|
return f"Rate: {self.rate}%"
|
||||||
|
|
||||||
@ -227,6 +224,7 @@ class AdditionalServices(models.Model, LocalizedNameMixin):
|
|||||||
taxable = models.BooleanField(default=False, verbose_name=_("taxable"))
|
taxable = models.BooleanField(default=False, verbose_name=_("taxable"))
|
||||||
uom = models.CharField(max_length=10, choices=UnitOfMeasure.choices, verbose_name=_("Unit of Measurement"))
|
uom = models.CharField(max_length=10, choices=UnitOfMeasure.choices, verbose_name=_("Unit of Measurement"))
|
||||||
dealer = models.ForeignKey("Dealer", on_delete=models.CASCADE, verbose_name=_("Dealer"))
|
dealer = models.ForeignKey("Dealer", on_delete=models.CASCADE, verbose_name=_("Dealer"))
|
||||||
|
item = models.OneToOneField(ItemModel, on_delete=models.CASCADE, verbose_name=_("Item"),null=True, blank=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Additional Services")
|
verbose_name = _("Additional Services")
|
||||||
@ -317,34 +315,6 @@ class Car(models.Model):
|
|||||||
def get_car_group(self):
|
def get_car_group(self):
|
||||||
return f"{self.id_car_make.get_local_name} {self.id_car_model.get_local_name}"
|
return f"{self.id_car_make.get_local_name} {self.id_car_model.get_local_name}"
|
||||||
|
|
||||||
|
|
||||||
# class CarData(models.Model):
|
|
||||||
# vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN"))
|
|
||||||
# make = models.CharField(max_length=255, verbose_name=_("Make"))
|
|
||||||
# make_ar = models.CharField(max_length=255, verbose_name=_("Make Arabic"))
|
|
||||||
# model = models.CharField(max_length=255, verbose_name=_("Model"))
|
|
||||||
# model_ar = models.CharField(max_length=255, verbose_name=_("Model Arabic"))
|
|
||||||
# year = models.IntegerField(verbose_name=_("Year"))
|
|
||||||
# series = models.CharField(max_length=255,verbose_name=_("Series"))
|
|
||||||
# trim = models.CharField(max_length=255,verbose_name=_("Trim"))
|
|
||||||
# specs = models.JsonField
|
|
||||||
# status = models.CharField(
|
|
||||||
# max_length=10,
|
|
||||||
# choices=CarStatusChoices,
|
|
||||||
# default=CarStatusChoices.AVAILABLE,
|
|
||||||
# verbose_name=_("Status")
|
|
||||||
# )
|
|
||||||
# stock_type = models.CharField(
|
|
||||||
# max_length=10,
|
|
||||||
# choices=CarStockTypeChoices,
|
|
||||||
# default=CarStockTypeChoices.NEW,
|
|
||||||
# verbose_name=_("Stock Type")
|
|
||||||
# )
|
|
||||||
# remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks"))
|
|
||||||
# mileage = models.IntegerField(blank=True, null=True, verbose_name=_("Mileage"))
|
|
||||||
# receiving_date = models.DateTimeField(verbose_name=_("Receiving Date"))
|
|
||||||
|
|
||||||
|
|
||||||
class CarReservation(models.Model):
|
class CarReservation(models.Model):
|
||||||
car = models.ForeignKey('Car', on_delete=models.CASCADE, related_name='reservations', verbose_name=_("Car"))
|
car = models.ForeignKey('Car', on_delete=models.CASCADE, related_name='reservations', verbose_name=_("Car"))
|
||||||
reserved_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reservations', verbose_name=_("Reserved By"))
|
reserved_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reservations', verbose_name=_("Reserved By"))
|
||||||
@ -363,7 +333,7 @@ class CarReservation(models.Model):
|
|||||||
|
|
||||||
# Car Finance Model
|
# Car Finance Model
|
||||||
class CarFinance(models.Model):
|
class CarFinance(models.Model):
|
||||||
additional_services = models.ManyToManyField(ItemModel, related_name="additional_finances",blank=True)
|
additional_services = models.ManyToManyField(AdditionalServices, related_name="additional_finances",blank=True)
|
||||||
car = models.OneToOneField(Car, on_delete=models.CASCADE, related_name='finances')
|
car = models.OneToOneField(Car, on_delete=models.CASCADE, related_name='finances')
|
||||||
cost_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Cost Price"))
|
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"))
|
selling_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Selling Price"))
|
||||||
@ -372,18 +342,13 @@ class CarFinance(models.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def total(self):
|
def total(self):
|
||||||
total = self.selling_price
|
if self.additional_services.count() > 0:
|
||||||
|
return self.selling_price + sum(x.price for x in self.additional_services.all())
|
||||||
if self.additional_services.count() != 0:
|
return self.selling_price
|
||||||
total_additional_services = sum(x.default_amount for x in self.additional_services.all())
|
|
||||||
total += total_additional_services
|
|
||||||
|
|
||||||
return total
|
|
||||||
@property
|
@property
|
||||||
def total_discount(self):
|
def total_discount(self):
|
||||||
if self.discount_amount != 0:
|
if self.discount_amount > 0:
|
||||||
total = self.total - self.discount_amount
|
return self.total - self.discount_amount
|
||||||
return total
|
|
||||||
return self.total
|
return self.total
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -395,7 +360,7 @@ class CarFinance(models.Model):
|
|||||||
def vat_amount(self):
|
def vat_amount(self):
|
||||||
vat = VatRate.objects.filter(is_active=True).first()
|
vat = VatRate.objects.filter(is_active=True).first()
|
||||||
if vat:
|
if vat:
|
||||||
return (self.total_discount * Decimal(vat.vat_rate)).quantize(Decimal('0.01'))
|
return (self.total_discount * Decimal(vat.rate)).quantize(Decimal('0.01'))
|
||||||
return Decimal('0.00')
|
return Decimal('0.00')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,14 @@ from django.dispatch import receiver
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django_ledger.io import roles
|
from django_ledger.io import roles
|
||||||
from django_ledger.models import EntityModel,AccountModel,ItemModel,ItemModelAbstract,UnitOfMeasureModel, VendorModel
|
from django_ledger.models import (
|
||||||
|
EntityModel,
|
||||||
|
AccountModel,
|
||||||
|
ItemModel,
|
||||||
|
ItemModelAbstract,
|
||||||
|
UnitOfMeasureModel,
|
||||||
|
VendorModel,
|
||||||
|
)
|
||||||
from . import models
|
from . import models
|
||||||
from .models import OpportunityLog
|
from .models import OpportunityLog
|
||||||
|
|
||||||
@ -43,23 +50,24 @@ User = get_user_model()
|
|||||||
# instance.save()
|
# instance.save()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# check with marwan
|
# check with marwan
|
||||||
@receiver(post_save, sender=models.Car)
|
@receiver(post_save, sender=models.Car)
|
||||||
def create_car_location(sender, instance, created, **kwargs):
|
def create_car_location(sender, instance, created, **kwargs):
|
||||||
"""
|
"""
|
||||||
Signal to create or update the car's location when a car instance is saved.
|
Signal to create or update the car's location when a car instance is saved.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if created:
|
if created:
|
||||||
if instance.dealer is None:
|
if instance.dealer is None:
|
||||||
raise ValueError(f"Cannot create CarLocation for car {instance.vin}: dealer is missing.")
|
raise ValueError(
|
||||||
|
f"Cannot create CarLocation for car {instance.vin}: dealer is missing."
|
||||||
|
)
|
||||||
|
|
||||||
models.CarLocation.objects.create(
|
models.CarLocation.objects.create(
|
||||||
car=instance,
|
car=instance,
|
||||||
owner=instance.dealer,
|
owner=instance.dealer,
|
||||||
showroom=instance.dealer,
|
showroom=instance.dealer,
|
||||||
description=f"Initial location set for car {instance.vin}."
|
description=f"Initial location set for car {instance.vin}.",
|
||||||
)
|
)
|
||||||
print("Car Location created")
|
print("Car Location created")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -85,7 +93,6 @@ def create_car_location(sender, instance, created, **kwargs):
|
|||||||
# Create Entity
|
# Create Entity
|
||||||
@receiver(post_save, sender=models.Dealer)
|
@receiver(post_save, sender=models.Dealer)
|
||||||
def create_ledger_entity(sender, instance, created, **kwargs):
|
def create_ledger_entity(sender, instance, created, **kwargs):
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
entity_name = instance.user.dealer.name
|
entity_name = instance.user.dealer.name
|
||||||
entity = EntityModel.create_entity(
|
entity = EntityModel.create_entity(
|
||||||
@ -105,13 +112,10 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
|||||||
# entity.populate_default_coa(activate_accounts=True, coa_model=coa)
|
# entity.populate_default_coa(activate_accounts=True, coa_model=coa)
|
||||||
print(f"Ledger entity created for Dealer: {instance.name}")
|
print(f"Ledger entity created for Dealer: {instance.name}")
|
||||||
|
|
||||||
|
# Create unit of measures
|
||||||
|
entity.create_uom(name="Unit", unit_abbr="unit")
|
||||||
# Create unit of measures
|
for u in models.UnitOfMeasure.choices:
|
||||||
|
entity.create_uom(name=u[1], unit_abbr=u[0])
|
||||||
entity.create_uom(
|
|
||||||
name="Unit",
|
|
||||||
unit_abbr="unit")
|
|
||||||
|
|
||||||
# Create Cash Account
|
# Create Cash Account
|
||||||
asset_ca_cash = entity.create_account(
|
asset_ca_cash = entity.create_account(
|
||||||
@ -157,6 +161,17 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
|||||||
balance_type="credit",
|
balance_type="credit",
|
||||||
active=True,
|
active=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# add Bank
|
||||||
|
|
||||||
|
# asset_ca_accounts_payable = entity.create_account(
|
||||||
|
# coa_model=coa,
|
||||||
|
# code="2101",
|
||||||
|
# role=roles.LIABILITY_CL_ACC_PAYABLE,
|
||||||
|
# name=_("Accounts Payable"),
|
||||||
|
# balance_type="credit",
|
||||||
|
# active=True,
|
||||||
|
# )
|
||||||
asset_ca_accounts_payable.role_default = True
|
asset_ca_accounts_payable.role_default = True
|
||||||
asset_ca_accounts_payable.save()
|
asset_ca_accounts_payable.save()
|
||||||
# Create Equity Account
|
# Create Equity Account
|
||||||
@ -216,8 +231,8 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
|||||||
balance_type="debit",
|
balance_type="debit",
|
||||||
active=True,
|
active=True,
|
||||||
)
|
)
|
||||||
#Create Deferred Revenue Account
|
# Create Deferred Revenue Account
|
||||||
entity.create_account(
|
unearned_account = entity.create_account(
|
||||||
coa_model=coa,
|
coa_model=coa,
|
||||||
code="2060",
|
code="2060",
|
||||||
role=roles.LIABILITY_CL_DEFERRED_REVENUE,
|
role=roles.LIABILITY_CL_DEFERRED_REVENUE,
|
||||||
@ -225,11 +240,13 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
|||||||
balance_type="credit",
|
balance_type="credit",
|
||||||
active=True,
|
active=True,
|
||||||
)
|
)
|
||||||
|
unearned_account.role_default = True
|
||||||
|
unearned_account.save()
|
||||||
|
|
||||||
|
|
||||||
# Create Vendor
|
# Create Vendor
|
||||||
@receiver(post_save, sender=models.Vendor)
|
@receiver(post_save, sender=models.Vendor)
|
||||||
def create_ledger_vendor(sender, instance, created, **kwargs):
|
def create_ledger_vendor(sender, instance, created, **kwargs):
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
entity = EntityModel.objects.filter(name=instance.dealer.name).first()
|
entity = EntityModel.objects.filter(name=instance.dealer.name).first()
|
||||||
|
|
||||||
@ -245,7 +262,7 @@ def create_ledger_vendor(sender, instance, created, **kwargs):
|
|||||||
"additional_info": {
|
"additional_info": {
|
||||||
"arabic_name": instance.arabic_name,
|
"arabic_name": instance.arabic_name,
|
||||||
"contact_person": instance.contact_person,
|
"contact_person": instance.contact_person,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -289,21 +306,19 @@ def create_item_model(sender, instance, created, **kwargs):
|
|||||||
name=f"{instance.vin}",
|
name=f"{instance.vin}",
|
||||||
item_type=ItemModel.ITEM_TYPE_MATERIAL,
|
item_type=ItemModel.ITEM_TYPE_MATERIAL,
|
||||||
uom_model=uom,
|
uom_model=uom,
|
||||||
coa_model=coa
|
coa_model=coa,
|
||||||
)
|
)
|
||||||
entity.create_item_inventory(
|
entity.create_item_inventory(
|
||||||
name=f"{instance.vin}",
|
name=f"{instance.vin}",
|
||||||
item_type=ItemModel.ITEM_TYPE_MATERIAL,
|
item_type=ItemModel.ITEM_TYPE_MATERIAL,
|
||||||
uom_model=uom,
|
uom_model=uom,
|
||||||
coa_model=coa
|
coa_model=coa,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# # update price - CarFinance
|
# # update price - CarFinance
|
||||||
@receiver(post_save, sender=models.CarFinance)
|
@receiver(post_save, sender=models.CarFinance)
|
||||||
def update_item_model_cost(sender, instance, created, **kwargs):
|
def update_item_model_cost(sender, instance, created, **kwargs):
|
||||||
|
|
||||||
ItemModel.objects.filter(item_id=instance.car.vin).update(
|
ItemModel.objects.filter(item_id=instance.car.vin).update(
|
||||||
inventory_received_value=instance.cost_price,
|
inventory_received_value=instance.cost_price,
|
||||||
default_amount=instance.cost_price,
|
default_amount=instance.cost_price,
|
||||||
@ -311,7 +326,6 @@ def update_item_model_cost(sender, instance, created, **kwargs):
|
|||||||
print(f"Inventory item updated with CarFinance data for Car: {instance.car}")
|
print(f"Inventory item updated with CarFinance data for Car: {instance.car}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# @receiver(pre_save, sender=models.SaleQuotation)
|
# @receiver(pre_save, sender=models.SaleQuotation)
|
||||||
# def update_quotation_status(sender, instance, **kwargs):
|
# def update_quotation_status(sender, instance, **kwargs):
|
||||||
# if instance.valid_until and timezone.now() > instance.valid_until:
|
# if instance.valid_until and timezone.now() > instance.valid_until:
|
||||||
@ -333,13 +347,16 @@ def update_item_model_cost(sender, instance, created, **kwargs):
|
|||||||
# quotation.status = 'pending'
|
# quotation.status = 'pending'
|
||||||
# quotation.save()
|
# quotation.save()
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=models.Opportunity)
|
@receiver(post_save, sender=models.Opportunity)
|
||||||
def notify_staff_on_deal_status_change(sender, instance, **kwargs):
|
def notify_staff_on_deal_status_change(sender, instance, **kwargs):
|
||||||
if instance.pk:
|
if instance.pk:
|
||||||
previous = models.Opportunity.objects.get(pk=instance.pk)
|
previous = models.Opportunity.objects.get(pk=instance.pk)
|
||||||
if previous.deal_status != instance.deal_status:
|
if previous.deal_status != instance.deal_status:
|
||||||
message = f"Deal '{instance.deal_name}' status changed from {previous.deal_status} to {instance.deal_status}."
|
message = f"Deal '{instance.deal_name}' status changed from {previous.deal_status} to {instance.deal_status}."
|
||||||
models.Notification.objects.create(staff=instance.created_by, message=message)
|
models.Notification.objects.create(
|
||||||
|
staff=instance.created_by, message=message
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=models.Opportunity)
|
@receiver(post_save, sender=models.Opportunity)
|
||||||
@ -347,9 +364,9 @@ def log_opportunity_creation(sender, instance, created, **kwargs):
|
|||||||
if created:
|
if created:
|
||||||
models.OpportunityLog.objects.create(
|
models.OpportunityLog.objects.create(
|
||||||
opportunity=instance,
|
opportunity=instance,
|
||||||
action='create',
|
action="create",
|
||||||
user=instance.created_by,
|
user=instance.created_by,
|
||||||
details=f"Opportunity '{instance.deal_name}' was created."
|
details=f"Opportunity '{instance.deal_name}' was created.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -360,16 +377,41 @@ def log_opportunity_update(sender, instance, **kwargs):
|
|||||||
if previous.deal_status != instance.deal_status:
|
if previous.deal_status != instance.deal_status:
|
||||||
models.OpportunityLog.objects.create(
|
models.OpportunityLog.objects.create(
|
||||||
opportunity=instance,
|
opportunity=instance,
|
||||||
action='status_change',
|
action="status_change",
|
||||||
user=instance.created_by,
|
user=instance.created_by,
|
||||||
old_status=previous.deal_status,
|
old_status=previous.deal_status,
|
||||||
new_status=instance.deal_status,
|
new_status=instance.deal_status,
|
||||||
details=f"Status changed from {previous.deal_status} to {instance.deal_status}."
|
details=f"Status changed from {previous.deal_status} to {instance.deal_status}.",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
models.OpportunityLog.objects.create(
|
models.OpportunityLog.objects.create(
|
||||||
opportunity=instance,
|
opportunity=instance,
|
||||||
action='update',
|
action="update",
|
||||||
user=instance.created_by,
|
user=instance.created_by,
|
||||||
details=f"Opportunity '{instance.deal_name}' was updated."
|
details=f"Opportunity '{instance.deal_name}' was updated.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender=models.AdditionalServices)
|
||||||
|
def create_item_service(sender, instance, created, **kwargs):
|
||||||
|
if created:
|
||||||
|
|
||||||
|
entity = instance.dealer.entity
|
||||||
|
uom = entity.get_uom_all().get(unit_abbr=instance.uom)
|
||||||
|
cogs = entity.get_all_accounts().get(role=roles.COGS)
|
||||||
|
|
||||||
|
# price = (float(instance.price) * float(vat.rate)) + float(instance.price) if instance.taxable else instance.price
|
||||||
|
service_model = ItemModel.objects.create(
|
||||||
|
name=instance.name,
|
||||||
|
uom=uom,
|
||||||
|
default_amount=instance.price,
|
||||||
|
entity=entity,
|
||||||
|
is_product_or_service=True,
|
||||||
|
sold_as_unit=True,
|
||||||
|
is_active=True,
|
||||||
|
item_role="service",
|
||||||
|
for_inventory=False,
|
||||||
|
cogs_account=cogs,
|
||||||
|
)
|
||||||
|
instance.item = service_model
|
||||||
|
instance.save()
|
||||||
|
|||||||
@ -97,5 +97,5 @@ def reserve_car(car,request):
|
|||||||
def calculate_vat_amount(amount):
|
def calculate_vat_amount(amount):
|
||||||
vat = models.VatRate.objects.filter(is_active=True).first()
|
vat = models.VatRate.objects.filter(is_active=True).first()
|
||||||
if vat:
|
if vat:
|
||||||
return ((amount * Decimal(vat.vat_rate)).quantize(Decimal('0.01')),vat.vat_rate)
|
return ((amount * Decimal(vat.rate)).quantize(Decimal('0.01')),vat.rate)
|
||||||
return amount
|
return amount
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
from decimal import Decimal
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
from django_ledger.models import (
|
from django_ledger.models import (
|
||||||
EntityModel,
|
EntityModel,
|
||||||
@ -118,7 +119,7 @@ def dealer_signup(request, *args, **kwargs):
|
|||||||
wf1 = data.get("wizardValidationForm1")
|
wf1 = data.get("wizardValidationForm1")
|
||||||
wf2 = data.get("wizardValidationForm2")
|
wf2 = data.get("wizardValidationForm2")
|
||||||
wf3 = data.get("wizardValidationForm3")
|
wf3 = data.get("wizardValidationForm3")
|
||||||
username = wf1.get("username")
|
# username = wf1.get("username")
|
||||||
email = wf1.get("email")
|
email = wf1.get("email")
|
||||||
password = wf1.get("password")
|
password = wf1.get("password")
|
||||||
password_confirm = wf1.get("confirm_password")
|
password_confirm = wf1.get("confirm_password")
|
||||||
@ -134,7 +135,8 @@ def dealer_signup(request, *args, **kwargs):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
user = User.objects.create(username=username, email=email)
|
# user = User.objects.create(username=username, email=email)
|
||||||
|
user = User.objects.create(username=email, email=email)
|
||||||
user.set_password(password)
|
user.set_password(password)
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
@ -147,7 +149,7 @@ def dealer_signup(request, *args, **kwargs):
|
|||||||
phone_number=phone,
|
phone_number=phone,
|
||||||
address=address,
|
address=address,
|
||||||
)
|
)
|
||||||
user = authenticate(request, username=username, password=password)
|
user = authenticate(request, email=email, password=password)
|
||||||
if user is not None:
|
if user is not None:
|
||||||
return JsonResponse(
|
return JsonResponse(
|
||||||
{"message": "User created successfully."}, status=200
|
{"message": "User created successfully."}, status=200
|
||||||
@ -544,6 +546,22 @@ class CarFinanceCreateView(LoginRequiredMixin, CreateView):
|
|||||||
context["car"] = self.car
|
context["car"] = self.car
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
def get_form(self, form_class=None):
|
||||||
|
form = super().get_form(form_class)
|
||||||
|
dealer = get_user_type(self.request.user.dealer)
|
||||||
|
form.fields[
|
||||||
|
"additional_finances"
|
||||||
|
].queryset = models.AdditionalServices.objects.filter(dealer=dealer)
|
||||||
|
return form
|
||||||
|
|
||||||
|
# def get_initial(self):
|
||||||
|
# initial = super().get_initial()
|
||||||
|
# instance = self.get_object()
|
||||||
|
# dealer = get_user_type(self.request.user.dealer)
|
||||||
|
# selected_items = instance.additional_services.filter(dealer=dealer)
|
||||||
|
# initial["additional_finances"] = selected_items
|
||||||
|
# return initial
|
||||||
|
|
||||||
|
|
||||||
class CarFinanceUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
class CarFinanceUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||||
model = models.CarFinance
|
model = models.CarFinance
|
||||||
@ -562,10 +580,19 @@ class CarFinanceUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
|||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
initial = super().get_initial()
|
initial = super().get_initial()
|
||||||
instance = self.get_object()
|
instance = self.get_object()
|
||||||
selected_items = instance.additional_services.all()
|
dealer = get_user_type(self.request.user.dealer)
|
||||||
|
selected_items = instance.additional_services.filter(dealer=dealer)
|
||||||
initial["additional_finances"] = selected_items
|
initial["additional_finances"] = selected_items
|
||||||
return initial
|
return initial
|
||||||
|
|
||||||
|
def get_form(self, form_class=None):
|
||||||
|
form = super().get_form(form_class)
|
||||||
|
dealer = get_user_type(self.request.user.dealer)
|
||||||
|
form.fields[
|
||||||
|
"additional_finances"
|
||||||
|
].queryset = models.AdditionalServices.objects.filter(dealer=dealer)
|
||||||
|
return form
|
||||||
|
|
||||||
|
|
||||||
class CarUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
class CarUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||||
model = models.Car
|
model = models.Car
|
||||||
@ -1634,7 +1661,6 @@ def bank_account_delete(request, pk):
|
|||||||
# Accounts
|
# Accounts
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AccountListView(LoginRequiredMixin, ListView):
|
class AccountListView(LoginRequiredMixin, ListView):
|
||||||
model = AccountModel
|
model = AccountModel
|
||||||
template_name = "ledger/coa_accounts/account_list.html"
|
template_name = "ledger/coa_accounts/account_list.html"
|
||||||
@ -1822,8 +1848,7 @@ def create_estimate(request):
|
|||||||
"unit_cost": instance.finances.cost_price,
|
"unit_cost": instance.finances.cost_price,
|
||||||
"unit_revenue": instance.finances.selling_price,
|
"unit_revenue": instance.finances.selling_price,
|
||||||
"quantity": float(quantities),
|
"quantity": float(quantities),
|
||||||
"total_amount": instance.finances.total_vat
|
"total_amount": instance.finances.total_vat * int(quantities),
|
||||||
* int(quantities),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1879,13 +1904,22 @@ class EstimateDetailView(LoginRequiredMixin, DetailView):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
estimate = kwargs.get("object")
|
estimate = kwargs.get("object")
|
||||||
|
vat = models.VatRate.objects.filter(is_active=True).first()
|
||||||
|
|
||||||
if estimate.get_itemtxs_data():
|
if estimate.get_itemtxs_data():
|
||||||
total = sum(
|
car_and_item_info = [
|
||||||
float(
|
{
|
||||||
models.Car.objects.get(
|
"car": models.Car.objects.get(vin=x.item_model.name),
|
||||||
|
"total": models.Car.objects.get(
|
||||||
vin=x.item_model.name
|
vin=x.item_model.name
|
||||||
).finances.total
|
).finances.selling_price
|
||||||
)
|
* Decimal(x.ce_quantity),
|
||||||
|
"itemmodel": x,
|
||||||
|
}
|
||||||
|
for x in estimate.get_itemtxs_data()[0].all()
|
||||||
|
]
|
||||||
|
total = sum(
|
||||||
|
float(models.Car.objects.get(vin=x.item_model.name).finances.total)
|
||||||
* float(x.ce_quantity)
|
* float(x.ce_quantity)
|
||||||
for x in estimate.get_itemtxs_data()[0].all()
|
for x in estimate.get_itemtxs_data()[0].all()
|
||||||
)
|
)
|
||||||
@ -1895,14 +1929,26 @@ class EstimateDetailView(LoginRequiredMixin, DetailView):
|
|||||||
).discount_amount
|
).discount_amount
|
||||||
for i in estimate.get_itemtxs_data()[0].all()
|
for i in estimate.get_itemtxs_data()[0].all()
|
||||||
)
|
)
|
||||||
vat = models.VatRate.objects.filter(is_active=True).first()
|
additional_services = []
|
||||||
|
for i in estimate.get_itemtxs_data()[0].all():
|
||||||
|
cf = models.CarFinance.objects.get(car__vin=i.item_model.name)
|
||||||
|
if cf.additional_services.exists():
|
||||||
|
additional_services.extend(
|
||||||
|
[
|
||||||
|
{"name": x.name, "price": x.price}
|
||||||
|
for x in cf.additional_services.all()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
grand_total = float(total) - float(discount_amount)
|
grand_total = float(total) - float(discount_amount)
|
||||||
vat_amount = round(float(grand_total) * float(vat.vat_rate), 2)
|
vat_amount = round(float(grand_total) * float(vat.rate), 2)
|
||||||
|
|
||||||
kwargs["vat_amount"] = vat_amount
|
kwargs["vat_amount"] = vat_amount
|
||||||
|
kwargs["car_and_item_info"] = car_and_item_info
|
||||||
kwargs["total"] = grand_total + vat_amount
|
kwargs["total"] = grand_total + vat_amount
|
||||||
kwargs["discount_amount"] = discount_amount
|
kwargs["discount_amount"] = discount_amount
|
||||||
kwargs["vat"] = vat.rate
|
kwargs["vat"] = vat.rate
|
||||||
|
kwargs["additional_services"] = additional_services
|
||||||
kwargs["invoice"] = (
|
kwargs["invoice"] = (
|
||||||
InvoiceModel.objects.all().filter(ce_model=estimate).first()
|
InvoiceModel.objects.all().filter(ce_model=estimate).first()
|
||||||
)
|
)
|
||||||
@ -1930,14 +1976,51 @@ class EstimatePreviewView(LoginRequiredMixin, DetailView):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
estimate = kwargs.get("object")
|
estimate = kwargs.get("object")
|
||||||
|
vat = models.VatRate.objects.filter(is_active=True).first()
|
||||||
if estimate.get_itemtxs_data():
|
if estimate.get_itemtxs_data():
|
||||||
|
car_and_item_info = [
|
||||||
|
{
|
||||||
|
"car": models.Car.objects.get(vin=x.item_model.name),
|
||||||
|
"total": models.Car.objects.get(
|
||||||
|
vin=x.item_model.name
|
||||||
|
).finances.selling_price
|
||||||
|
* Decimal(x.ce_quantity),
|
||||||
|
"itemmodel": x,
|
||||||
|
}
|
||||||
|
for x in estimate.get_itemtxs_data()[0].all()
|
||||||
|
]
|
||||||
total = sum(
|
total = sum(
|
||||||
x.ce_cost_estimate for x in estimate.get_itemtxs_data()[0].all()
|
float(models.Car.objects.get(vin=x.item_model.name).finances.total)
|
||||||
|
* float(x.ce_quantity)
|
||||||
|
for x in estimate.get_itemtxs_data()[0].all()
|
||||||
)
|
)
|
||||||
vat = models.VatRate.objects.filter(is_active=True).first()
|
discount_amount = sum(
|
||||||
kwargs["vat_amount"] = total * vat.vat_rate
|
models.CarFinance.objects.get(
|
||||||
kwargs["total"] = (total * vat.vat_rate) + total
|
car__vin=i.item_model.name
|
||||||
|
).discount_amount
|
||||||
|
for i in estimate.get_itemtxs_data()[0].all()
|
||||||
|
)
|
||||||
|
|
||||||
|
additional_services = []
|
||||||
|
|
||||||
|
for i in estimate.get_itemtxs_data()[0].all():
|
||||||
|
cf = models.CarFinance.objects.get(car__vin=i.item_model.name)
|
||||||
|
if cf.additional_services.exists():
|
||||||
|
additional_services.extend(
|
||||||
|
[
|
||||||
|
{"name": x.name, "price": x.price}
|
||||||
|
for x in cf.additional_services.all()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
grand_total = float(total) - float(discount_amount)
|
||||||
|
vat_amount = round(float(grand_total) * float(vat.rate), 2)
|
||||||
|
|
||||||
|
kwargs["vat_amount"] = vat_amount
|
||||||
|
kwargs["total"] = grand_total + vat_amount
|
||||||
kwargs["vat"] = vat.rate
|
kwargs["vat"] = vat.rate
|
||||||
|
kwargs["car_and_item_info"] = car_and_item_info
|
||||||
|
kwargs["additional_services"] = additional_services
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
@ -1997,9 +2080,21 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView):
|
|||||||
vat = models.VatRate.objects.filter(is_active=True).first()
|
vat = models.VatRate.objects.filter(is_active=True).first()
|
||||||
|
|
||||||
if invoice.get_itemtxs_data():
|
if invoice.get_itemtxs_data():
|
||||||
|
car_and_item_info = [
|
||||||
|
{
|
||||||
|
"car": models.Car.objects.get(vin=x.item_model.name),
|
||||||
|
"total": models.Car.objects.get(
|
||||||
|
vin=x.item_model.name
|
||||||
|
).finances.selling_price
|
||||||
|
* Decimal(x.quantity),
|
||||||
|
"itemmodel": x,
|
||||||
|
}
|
||||||
|
for x in invoice.get_itemtxs_data()[0].all()
|
||||||
|
]
|
||||||
total = sum(
|
total = sum(
|
||||||
float(x.ce_revenue_estimate) * float(x.ce_quantity)
|
float(models.Car.objects.get(vin=x.item_model.name).finances.total)
|
||||||
for x in invoice.ce_model.get_itemtxs_data()[0].all()
|
* float(x.quantity)
|
||||||
|
for x in invoice.get_itemtxs_data()[0].all()
|
||||||
)
|
)
|
||||||
discount_amount = sum(
|
discount_amount = sum(
|
||||||
models.CarFinance.objects.get(
|
models.CarFinance.objects.get(
|
||||||
@ -2008,14 +2103,27 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView):
|
|||||||
for i in invoice.get_itemtxs_data()[0].all()
|
for i in invoice.get_itemtxs_data()[0].all()
|
||||||
)
|
)
|
||||||
|
|
||||||
grand_total = float(total) - float(discount_amount)
|
additional_services = []
|
||||||
vat_amount = round(float(grand_total) * float(vat.vat_rate), 2)
|
|
||||||
|
|
||||||
|
for i in invoice.get_itemtxs_data()[0].all():
|
||||||
|
cf = models.CarFinance.objects.get(car__vin=i.item_model.name)
|
||||||
|
if cf.additional_services.exists():
|
||||||
|
additional_services.extend(
|
||||||
|
[
|
||||||
|
{"name": x.name, "price": x.price}
|
||||||
|
for x in cf.additional_services.all()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
grand_total = float(total) - float(discount_amount)
|
||||||
|
vat_amount = round(float(grand_total) * float(vat.rate), 2)
|
||||||
|
|
||||||
kwargs["vat_amount"] = vat_amount
|
kwargs["vat_amount"] = vat_amount
|
||||||
kwargs["total"] = grand_total + vat_amount
|
kwargs["total"] = grand_total + vat_amount
|
||||||
kwargs["discount_amount"] = discount_amount
|
kwargs["discount_amount"] = discount_amount
|
||||||
kwargs["vat"] = vat.rate
|
kwargs["vat"] = vat.rate
|
||||||
|
kwargs["car_and_item_info"] = car_and_item_info
|
||||||
|
kwargs["additional_services"] = additional_services
|
||||||
kwargs["payments"] = JournalEntryModel.objects.filter(
|
kwargs["payments"] = JournalEntryModel.objects.filter(
|
||||||
ledger=invoice.ledger
|
ledger=invoice.ledger
|
||||||
).all()
|
).all()
|
||||||
@ -2124,15 +2232,51 @@ class InvoicePreviewView(LoginRequiredMixin, DetailView):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
invoice = kwargs.get("object")
|
invoice = kwargs.get("object")
|
||||||
|
vat = models.VatRate.objects.filter(is_active=True).first()
|
||||||
if invoice.get_itemtxs_data():
|
if invoice.get_itemtxs_data():
|
||||||
|
car_and_item_info = [
|
||||||
|
{
|
||||||
|
"car": models.Car.objects.get(vin=x.item_model.name),
|
||||||
|
"total": models.Car.objects.get(
|
||||||
|
vin=x.item_model.name
|
||||||
|
).finances.selling_price
|
||||||
|
* Decimal(x.ce_quantity),
|
||||||
|
"itemmodel": x,
|
||||||
|
}
|
||||||
|
for x in invoice.ce_model.get_itemtxs_data()[0].all()
|
||||||
|
]
|
||||||
total = sum(
|
total = sum(
|
||||||
x.unit_cost * x.quantity for x in invoice.get_itemtxs_data()[0].all()
|
float(models.Car.objects.get(vin=x.item_model.name).finances.total)
|
||||||
|
* float(x.ce_quantity)
|
||||||
|
for x in invoice.ce_model.get_itemtxs_data()[0].all()
|
||||||
)
|
)
|
||||||
total = int(total)
|
discount_amount = sum(
|
||||||
vat = models.VatRate.objects.filter(is_active=True).first()
|
models.CarFinance.objects.get(
|
||||||
kwargs["vat_amount"] = total * vat.vat_rate
|
car__vin=i.item_model.name
|
||||||
kwargs["total"] = (total * vat.vat_rate) + total
|
).discount_amount
|
||||||
|
for i in invoice.ce_model.get_itemtxs_data()[0].all()
|
||||||
|
)
|
||||||
|
|
||||||
|
additional_services = []
|
||||||
|
|
||||||
|
for i in invoice.ce_model.get_itemtxs_data()[0].all():
|
||||||
|
cf = models.CarFinance.objects.get(car__vin=i.item_model.name)
|
||||||
|
if cf.additional_services.exists():
|
||||||
|
additional_services.extend(
|
||||||
|
[
|
||||||
|
{"name": x.name, "price": x.price}
|
||||||
|
for x in cf.additional_services.all()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
grand_total = float(total) - float(discount_amount)
|
||||||
|
vat_amount = round(float(grand_total) * float(vat.rate), 2)
|
||||||
|
|
||||||
|
kwargs["vat_amount"] = vat_amount
|
||||||
|
kwargs["total"] = grand_total + vat_amount
|
||||||
kwargs["vat"] = vat.rate
|
kwargs["vat"] = vat.rate
|
||||||
|
kwargs["car_and_item_info"] = car_and_item_info
|
||||||
|
kwargs["additional_services"] = additional_services
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
@ -2383,19 +2527,22 @@ def fetch_notifications(request):
|
|||||||
|
|
||||||
|
|
||||||
class ItemServiceCreateView(CreateView):
|
class ItemServiceCreateView(CreateView):
|
||||||
model = ItemModel
|
model = models.AdditionalServices
|
||||||
form_class = ServiceCreateForm
|
form_class = forms.AdditionalServiceForm
|
||||||
template_name = "items/service/service_create.html"
|
template_name = "items/service/service_create.html"
|
||||||
success_url = reverse_lazy("item_service_list")
|
success_url = reverse_lazy("item_service_list")
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
# def get_form_kwargs(self):
|
||||||
kwargs = super().get_form_kwargs()
|
# kwargs = super().get_form_kwargs()
|
||||||
kwargs["entity_slug"] = self.request.user.dealer.entity.slug
|
# kwargs["entity_slug"] = self.request.user.dealer.entity.slug
|
||||||
kwargs["user_model"] = self.request.user.dealer.entity.admin
|
# kwargs["user_model"] = self.request.user.dealer.entity.admin
|
||||||
return kwargs
|
# return kwargs
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.entity = self.request.user.dealer.entity
|
vat = models.VatRate.objects.get(is_active=True)
|
||||||
|
form.instance.dealer = get_user_type(self.request.user.dealer)
|
||||||
|
if form.instance.taxable:
|
||||||
|
form.instance.price = (form.instance.price * vat.rate) + form.instance.price
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
154
scripts/loaddata.py
Normal file
154
scripts/loaddata.py
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
import json
|
||||||
|
from tqdm import tqdm
|
||||||
|
from inventory.models import (
|
||||||
|
CarMake, CarModel, CarSerie, CarTrim, CarEquipment,
|
||||||
|
CarSpecification, CarSpecificationValue, CarOption, CarOptionValue
|
||||||
|
)
|
||||||
|
|
||||||
|
def run():
|
||||||
|
with open("final_car_data.json", "r") as file:
|
||||||
|
data = json.load(file)
|
||||||
|
|
||||||
|
# Step 1: Insert CarMake
|
||||||
|
for item in tqdm(data["car_make"], desc="Inserting CarMake"):
|
||||||
|
CarMake.objects.update_or_create(
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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.")
|
||||||
@ -142,7 +142,7 @@
|
|||||||
{% for service in car.finances.additional_services.all %}
|
{% for service in car.finances.additional_services.all %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{service.name}}</td>
|
<td>{{service.name}}</td>
|
||||||
<td>{{ service.default_amount }}</td>
|
<td>{{ service.price }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@ -126,30 +126,38 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for item in estimate.get_itemtxs_data.0 %}
|
{% for item in car_and_item_info %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="">{{forloop.counter}}</td>
|
<td class="">{{forloop.counter}}</td>
|
||||||
<td class="">{{item.item_model.name}}</td>
|
<td class="">{{item.car.id_car_model}}</td>
|
||||||
<td class="align-middle">{{item.ce_quantity}}</td>
|
<td class="align-middle">{{item.itemmodel.ce_quantity}}</td>
|
||||||
<td class="align-middle ps-5">{{item.ce_unit_revenue_estimate}}</td>
|
<td class="align-middle ps-5">{{item.car.finances.selling_price}}</td>
|
||||||
<td class="align-middle text-body-tertiary fw-semibold">{{item.ce_revenue_estimate}}</td>
|
<td class="align-middle text-body-tertiary fw-semibold">{{item.total}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<tr class="bg-body-secondary total-sum">
|
<tr class="bg-body-secondary total-sum">
|
||||||
<td class="align-middle ps-4 fw-bold text-body-highlight" colspan="4">{% trans "Vat" %} ({{vat}}%)</td>
|
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="4">{% trans "Discount Amount" %}</td>
|
||||||
<td class="align-middle text-start fw-bold">
|
<td class="align-middle text-start fw-semibold">
|
||||||
<span id="grand-total">{{vat_amount}}</span>
|
<span id="grand-total">- {{discount_amount}}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="bg-body-secondary total-sum">
|
<tr class="bg-body-secondary total-sum">
|
||||||
<td class="align-middle ps-4 fw-bold text-body-highlight" colspan="4">{% trans "Discount Amount" %}</td>
|
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="4">{% trans "Vat" %} ({{vat}}%)</td>
|
||||||
<td class="align-middle text-start fw-bold">
|
<td class="align-middle text-start fw-semibold">
|
||||||
<span id="grand-total">{{discount_amount}}</span>
|
<span id="grand-total">+ {{vat_amount}}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="bg-body-secondary total-sum">
|
<tr class="bg-body-secondary total-sum">
|
||||||
<td class="align-middle ps-4 fw-bold text-body-highlight" colspan="4">{% trans "Grand Total" %}</td>
|
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="4">{% trans "Additional Services" %}</td>
|
||||||
<td class="align-middle text-start fw-bold">
|
<td class="align-middle text-start fw-semibold">
|
||||||
|
{% for service in additional_services %}
|
||||||
|
<small><span class="fw-semibold">+ {{service.name}} - {{service.price}}</span></small><br>
|
||||||
|
{% endfor %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="bg-body-secondary total-sum">
|
||||||
|
<td class="align-middle ps-4 fw-bolder text-body-highlight" colspan="4">{% trans "Grand Total" %}</td>
|
||||||
|
<td class="align-middle text-start fw-bolder">
|
||||||
<span id="grand-total">{{total}}</span>
|
<span id="grand-total">{{total}}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -277,12 +277,12 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for item in estimate.get_itemtxs_data.0 %}
|
{% for item in car_and_item_info %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ item.item_model.name }}</td>
|
<td>{{ item.car.id_car_model }}</td>
|
||||||
<td class="text-center">{{ item.ce_quantity }}</td>
|
<td class="text-center">{{ item.itemmodel.ce_quantity }}</td>
|
||||||
<td class="text-center">{{ item.ce_unit_cost_estimate }}</td>
|
<td class="text-center">{{ item.car.finances.selling_price }}</td>
|
||||||
<td class="highlight fw-semibold text-center">{{ item.ce_cost_estimate }}</td>
|
<td class="highlight fw-semibold text-center">{{ item.total }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -292,7 +292,12 @@
|
|||||||
<!-- Additional Charges (VAT and Services) -->
|
<!-- Additional Charges (VAT and Services) -->
|
||||||
<div class="additional-charges">
|
<div class="additional-charges">
|
||||||
<p><strong>{% trans "VAT" %} ({{vat}}%):</strong> <span class="highlight">${{vat_amount}}</span></p>
|
<p><strong>{% trans "VAT" %} ({{vat}}%):</strong> <span class="highlight">${{vat_amount}}</span></p>
|
||||||
<p><strong>{% trans "Additional Services" %}:</strong> <span class="highlight">$50.00</span></p>
|
<p><strong>{% trans "Additional Services" %}:</strong>
|
||||||
|
<br>
|
||||||
|
{% for service in additional_services %}
|
||||||
|
<span class="highlight">{{service.name}} - ${{service.price}}</span><br>
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Total -->
|
<!-- Total -->
|
||||||
|
|||||||
@ -144,30 +144,38 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for item in invoice.ce_model.get_itemtxs_data.0 %}
|
{% for item in car_and_item_info %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="">{{forloop.counter}}</td>
|
<td class="">{{forloop.counter}}</td>
|
||||||
<td class="">{{item.item_model.name}}</td>
|
<td class="">{{item.car.id_car_model}}</td>
|
||||||
<td class="align-middle">{{item.ce_quantity}}</td>
|
<td class="align-middle">{{item.itemmodel.quantity}}</td>
|
||||||
<td class="align-middle ps-5">{{item.ce_unit_revenue_estimate}}</td>
|
<td class="align-middle ps-5">{{item.car.finances.selling_price}}</td>
|
||||||
<td class="align-middle text-body-tertiary fw-semibold">{{item.ce_revenue_estimate}}</td>
|
<td class="align-middle text-body-tertiary fw-semibold">{{item.total}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<tr class="bg-body-secondary total-sum">
|
<tr class="bg-body-secondary total-sum">
|
||||||
<td class="align-middle ps-4 fw-bold text-body-highlight" colspan="4">{% trans "VAT" %} ({{vat}}%)</td>
|
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="4">{% trans "Discount Amount" %}</td>
|
||||||
<td class="align-middle text-start fw-bold">
|
<td class="align-middle text-start fw-semibold">
|
||||||
<span id="grand-total">{{vat_amount}}</span>
|
<span id="grand-total">- {{discount_amount}}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="bg-body-secondary total-sum">
|
<tr class="bg-body-secondary total-sum">
|
||||||
<td class="align-middle ps-4 fw-bold text-body-highlight" colspan="4">{% trans "Discount Amount" %}</td>
|
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="4">{% trans "VAT" %} ({{vat}}%)</td>
|
||||||
<td class="align-middle text-start fw-bold">
|
<td class="align-middle text-start fw-semibold">
|
||||||
<span id="grand-total">{{discount_amount}}</span>
|
<span id="grand-total">+ {{vat_amount}}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="bg-body-secondary total-sum">
|
<tr class="bg-body-secondary total-sum">
|
||||||
<td class="align-middle ps-4 fw-bold text-body-highlight" colspan="4">{% trans "Grand Total" %}</td>
|
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="4">{% trans "Additional Services" %}</td>
|
||||||
<td class="align-middle text-start fw-bold">
|
<td class="align-middle text-start fw-bold">
|
||||||
|
{% for service in additional_services %}
|
||||||
|
<small><span class="fw-bold">+ {{service.name}} - {{service.price}}</span></small><br>
|
||||||
|
{% endfor %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="bg-body-secondary total-sum">
|
||||||
|
<td class="align-middle ps-4 fw-bolder text-body-highlight" colspan="4">{% trans "Grand Total" %}</td>
|
||||||
|
<td class="align-middle text-start fw-bolder">
|
||||||
<span id="grand-total">{{total}}</span>
|
<span id="grand-total">{{total}}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -196,12 +196,12 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for item in invoice.get_itemtxs_data.0 %}
|
{% for item in car_and_item_info %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ item.item_model.name }}</td>
|
<td>{{ item.car.id_car_model }}</td>
|
||||||
<td>{{ item.quantity }}</td>
|
<td>{{ item.itemmodel.ce_quantity }}</td>
|
||||||
<td>{{ item.unit_cost }}</td>
|
<td>{{ item.car.finances.selling_price }}</td>
|
||||||
<td>{{ item.total_amount }}</td>
|
<td>{{ item.total }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -211,7 +211,12 @@
|
|||||||
<!-- Additional Charges (VAT and Services) -->
|
<!-- Additional Charges (VAT and Services) -->
|
||||||
<div class="additional-charges">
|
<div class="additional-charges">
|
||||||
<p><strong>VAT ({{vat}}%):</strong> <span class="highlight">${{vat_amount}}</span></p>
|
<p><strong>VAT ({{vat}}%):</strong> <span class="highlight">${{vat_amount}}</span></p>
|
||||||
<p><strong>Additional Services:</strong> <span class="highlight">$50.00</span></p>
|
<p><strong>Additional Services:</strong>
|
||||||
|
<br>
|
||||||
|
{% for service in additional_services %}
|
||||||
|
<span class="highlight">{{service.name}} - ${{service.price}}</span><br>
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Total -->
|
<!-- Total -->
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user