This commit is contained in:
Marwan Alwali 2025-01-06 15:59:11 +03:00
commit 0f9b2cff5f
10 changed files with 360 additions and 275 deletions

View File

@ -367,6 +367,18 @@ 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={

View File

@ -38,18 +38,22 @@ class DealerUserManager(UserManager):
return user return user
class UnitOfMeasure(models.TextChoices):
UNIT_CHOICES = ( EACH = 'EA', 'Each'
("Unit", _("Unit")), PAIR = 'PR', 'Pair'
("Kg", _("Kg")), SET = 'SET', 'Set'
("L", _("L")), GALLON = 'GAL', 'Gallon'
("m", _("m")), LITER = 'L', 'Liter'
("cm", _("cm")), METER = 'M', 'Meter'
("m2", _("m2")), KILOGRAM = 'KG', 'Kilogram'
("m3", _("m3")), HOUR = 'HR', 'Hour'
("m3", _("m3")), BOX = 'BX', 'Box'
) ROLL = 'RL', 'Roll'
PACKAGE = 'PKG', 'Package'
DOZEN = 'DZ', 'Dozen'
SQUARE_METER = 'SQ_M', 'Square Meter'
PIECE = 'PC', 'Piece'
BUNDLE = 'BDL', 'Bundle'
class VatRate(models.Model): class VatRate(models.Model):
rate = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal('0.15')) rate = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal('0.15'))
is_active = models.BooleanField(default=True) is_active = models.BooleanField(default=True)
@ -173,7 +177,7 @@ class AdditionalServices(models.Model, LocalizedNameMixin):
description = models.TextField(verbose_name=_("Description")) description = models.TextField(verbose_name=_("Description"))
price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Price")) price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Price"))
taxable = models.BooleanField(default=False, verbose_name=_("taxable")) taxable = models.BooleanField(default=False, verbose_name=_("taxable"))
uom = models.CharField(max_length=10, choices=UNIT_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"))
class Meta: class Meta:
@ -334,8 +338,7 @@ class CarFinance(models.Model):
# custom_card_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Custom Card Fee"), # custom_card_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Custom Card Fee"),
# default=Decimal('0.00')) # default=Decimal('0.00'))
@property @property
def total(self): def total(self):
vat = VatRate.objects.filter(is_active=True).first()
total = 0 total = 0
if self.additional_services.count() != 0: if self.additional_services.count() != 0:
total_additional_services = sum(x.default_amount for x in self.additional_services.all()) total_additional_services = sum(x.default_amount for x in self.additional_services.all())
@ -344,9 +347,19 @@ class CarFinance(models.Model):
total = self.selling_price total = self.selling_price
if self.discount_amount != 0: if self.discount_amount != 0:
total = total - self.discount_amount total = total - self.discount_amount
total = (total * vat.vat_rate).quantize(Decimal('0.01')) + total
return total return total
@property
def vat_amount(self):
vat = VatRate.objects.filter(is_active=True).first()
return (self.total * vat.vat_rate).quantize(Decimal('0.01'))
@property
def total_vat(self):
return self.total + self.vat_amount
def __str__(self): def __str__(self):
return f"Car: {self.car}, Selling Price: {self.selling_price}" return f"Car: {self.car}, Selling Price: {self.selling_price}"

View File

@ -3,11 +3,10 @@ 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, \ from django_ledger.models import EntityModel,AccountModel,ItemModel,ItemModelAbstract,UnitOfMeasureModel, VendorModel
VendorModel, ChartOfAccountModel
from . import models from . import models
from .models import OpportunityLog from .models import OpportunityLog
from .utils import get_dealer_from_instance
User = get_user_model() User = get_user_model()
@ -45,42 +44,49 @@ User = get_user_model()
# instance.save() # instance.save()
@receiver(post_save, sender=models.Car)
# check with marwan
@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.
""" """
if created: try:
print(instance) if created:
models.CarLocation.objects.create( if instance.dealer is None:
car=instance, raise ValueError(f"Cannot create CarLocation for car {instance.vin}: dealer is missing.")
owner=instance.dealer,
showroom=instance.dealer, models.CarLocation.objects.create(
description=f"Initial location set for car {instance.vin}." car=instance,
) owner=instance.dealer,
print("Car Location created") showroom=instance.dealer,
description=f"Initial location set for car {instance.vin}."
)
print("Car Location created")
except Exception as e:
print(f"Failed to create CarLocation for car {instance.vin}: {e}")
# @receiver(post_save, sender=models.CarReservation)
@receiver(post_save, sender=models.CarReservation) # def update_car_status_on_reservation(sender, instance, created, **kwargs):
def update_car_status_on_reservation(sender, instance, created, **kwargs): # if created:
if created: # car = instance.car
car = instance.car # car.status = models.CarStatusChoices.RESERVED
car.status = models.CarStatusChoices.RESERVED # car.save()
car.save()
@receiver(post_delete, sender=models.CarReservation) # @receiver(post_delete, sender=models.CarReservation)
def update_car_status_on_reservation_delete(sender, instance, **kwargs): # def update_car_status_on_reservation_delete(sender, instance, **kwargs):
car = instance.car # car = instance.car
if not car.is_reserved(): # if not car.is_reserved():
car.status = models.CarStatusChoices.AVAILABLE # car.status = models.CarStatusChoices.AVAILABLE
car.save() # car.save()
# 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(
@ -100,18 +106,27 @@ 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 Cash Account
entity.create_account(
# Create unit of measures
entity.create_uom(
name="Unit",
unit_abbr="unit")
# Create Cash Account
asset_ca_cash = entity.create_account(
coa_model=coa, coa_model=coa,
code="1101", code="1010",
role=roles.ASSET_CA_CASH, role=roles.ASSET_CA_CASH,
name=_("Cash"), name=_("Cash"),
balance_type="debit", balance_type="debit",
active=True, active=True,
) )
asset_ca_cash.role_default = True
asset_ca_cash.save()
# Create Accounts Receivable Account # Create Accounts Receivable Account
entity.create_account( asset_ca_receivables = entity.create_account(
coa_model=coa, coa_model=coa,
code="1102", code="1102",
role=roles.ASSET_CA_RECEIVABLES, role=roles.ASSET_CA_RECEIVABLES,
@ -119,9 +134,11 @@ def create_ledger_entity(sender, instance, created, **kwargs):
balance_type="debit", balance_type="debit",
active=True, active=True,
) )
asset_ca_receivables.role_default = True
asset_ca_receivables.save()
# Create Inventory Account # Create Inventory Account
entity.create_account( asset_ca_inventory = entity.create_account(
coa_model=coa, coa_model=coa,
code="1103", code="1103",
role=roles.ASSET_CA_INVENTORY, role=roles.ASSET_CA_INVENTORY,
@ -130,8 +147,10 @@ def create_ledger_entity(sender, instance, created, **kwargs):
active=True, active=True,
) )
asset_ca_inventory.role_default = True
asset_ca_inventory.save()
# Create Accounts Payable Account # Create Accounts Payable Account
entity.create_account( asset_ca_accounts_payable = entity.create_account(
coa_model=coa, coa_model=coa,
code="2101", code="2101",
role=roles.LIABILITY_CL_ACC_PAYABLE, role=roles.LIABILITY_CL_ACC_PAYABLE,
@ -139,8 +158,10 @@ def create_ledger_entity(sender, instance, created, **kwargs):
balance_type="credit", balance_type="credit",
active=True, active=True,
) )
asset_ca_accounts_payable.role_default = True
asset_ca_accounts_payable.save()
# Create Equity Account # Create Equity Account
entity.create_account( asset_ca_equity = entity.create_account(
coa_model=coa, coa_model=coa,
code="3101", code="3101",
role=roles.EQUITY_CAPITAL, role=roles.EQUITY_CAPITAL,
@ -148,9 +169,11 @@ def create_ledger_entity(sender, instance, created, **kwargs):
balance_type="credit", balance_type="credit",
active=True, active=True,
) )
asset_ca_equity.role_default = True
asset_ca_equity.save()
# Create Sales Revenue Account # Create Sales Revenue Account
entity.create_account( asset_ca_revenue = entity.create_account(
coa_model=coa, coa_model=coa,
code="4101", code="4101",
role=roles.INCOME_OPERATIONAL, role=roles.INCOME_OPERATIONAL,
@ -158,9 +181,11 @@ def create_ledger_entity(sender, instance, created, **kwargs):
balance_type="credit", balance_type="credit",
active=True, active=True,
) )
asset_ca_revenue.role_default = True
asset_ca_revenue.save()
# Create Cost of Goods Sold Account # Create Cost of Goods Sold Account
entity.create_account( asset_ca_cogs = entity.create_account(
coa_model=coa, coa_model=coa,
code="5101", code="5101",
role=roles.COGS, role=roles.COGS,
@ -168,9 +193,11 @@ def create_ledger_entity(sender, instance, created, **kwargs):
balance_type="debit", balance_type="debit",
active=True, active=True,
) )
asset_ca_cogs.role_default = True
asset_ca_cogs.save()
# Create Rent Expense Account # Create Rent Expense Account
entity.create_account( expense = entity.create_account(
coa_model=coa, coa_model=coa,
code="6101", code="6101",
role=roles.EXPENSE_OPERATIONAL, role=roles.EXPENSE_OPERATIONAL,
@ -178,6 +205,8 @@ def create_ledger_entity(sender, instance, created, **kwargs):
balance_type="debit", balance_type="debit",
active=True, active=True,
) )
expense.role_default = True
expense.save()
# Create Utilities Expense Account # Create Utilities Expense Account
entity.create_account( entity.create_account(
@ -188,7 +217,15 @@ def create_ledger_entity(sender, instance, created, **kwargs):
balance_type="debit", balance_type="debit",
active=True, active=True,
) )
#Create Deferred Revenue Account
entity.create_account(
coa_model=coa,
code="2060",
role=roles.LIABILITY_CL_DEFERRED_REVENUE,
name=_("Deferred Revenue"),
balance_type="credit",
active=True,
)
# Create Vendor # Create Vendor
@receiver(post_save, sender=models.Vendor) @receiver(post_save, sender=models.Vendor)
@ -241,95 +278,38 @@ def create_customer(sender, instance, created, **kwargs):
# Create Item # Create Item
@receiver(post_save, sender=models.Car) @receiver(post_save, sender=models.Car)
def create_item_product(sender, instance, created, **kwargs): def create_item_model(sender, instance, created, **kwargs):
name = instance.dealer.name
entity = EntityModel.objects.filter(name=name).first()
if created: if created:
product_name = f"{instance.year} - {instance.id_car_make} - {instance.id_car_model}" coa = entity.get_default_coa()
entity = EntityModel.objects.get(name=instance.dealer.name) uom = entity.get_uom_all().get(name="Unit")
uom = entity.get_uom_all().first()
# coa_model = entity.get_default_coa() if not entity.get_items_all().filter(name=instance.vin).first():
inventory_account = AccountModel.objects.first() entity.create_item_product(
cogs_account = AccountModel.objects.first() name=f"{instance.vin}",
earnings_account = AccountModel.objects.first() item_type=ItemModel.ITEM_TYPE_MATERIAL,
uom_model=uom,
coa_model=coa
ItemModel.objects.create( )
entity=entity, entity.create_item_inventory(
uom=uom, name=f"{instance.vin}",
name=product_name, item_type=ItemModel.ITEM_TYPE_MATERIAL,
item_role=ItemModelAbstract.ITEM_ROLE_PRODUCT, uom_model=uom,
item_type=ItemModelAbstract.ITEM_TYPE_MATERIAL, coa_model=coa
item_id=instance.vin, )
sold_as_unit=True,
for_inventory=True,
is_product_or_service=True,
inventory_received=1.00,
inventory_received_value=0.00,
inventory_account=inventory_account,
earnings_account=earnings_account,
cogs_account=cogs_account,
additional_info={
"remarks": instance.remarks,
"status": instance.status,
"stock_type": instance.stock_type,
"mileage": instance.mileage,
},
)
print(f"Item created: {product_name}")
@receiver(post_save, sender=models.AdditionalServices)
def create_item_for_service(sender, instance, created, **kwargs):
"""
Signal to create an ItemModel of type SERVICE when a new AdditionalServices instance is created.
"""
if created: # Only run on creation, not update
entity = EntityModel.objects.get(name=instance.dealer.name)
uom_model = entity.get_uom_all().first()
entity.create_item_service(
name=instance.name,
uom_model=uom_model,
commit=True
)
# ItemModel.objects.create(
# entity=entity,
# uom=uom,
# name=instance.name,
# item_role=ItemModelAbstract.ITEM_ROLE_SERVICE,
# item_type=ItemModelAbstract.ITEM_TYPE_LABOR,
# # item_id=instance.vin,
# sold_as_unit=True,
# for_inventory=True,
# is_product_or_service=True,
# inventory_received=1.00,
# inventory_received_value=instance.price,
# inventory_account=inventory_account,
# earnings_account=earnings_account,
# cogs_account=cogs_account,
# additional_info={
# "remarks": instance.remarks,
# "status": instance.status,
# "stock_type": instance.stock_type,
# "mileage": instance.mileage,
# },
# )
print(f"Service Item created for AdditionalService: {instance.name}")
# # 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.selling_price, default_amount=instance.cost_price,
# ) )
# print(f"Inventory item updated with CarFinance data for Car: {instance.car}") print(f"Inventory item updated with CarFinance data for Car: {instance.car}")

View File

@ -71,6 +71,8 @@ from .utils import get_calculations, send_email, get_user_type
from django.contrib.auth.models import User from django.contrib.auth.models import User
from allauth.account import views from allauth.account import views
from django.db.models import Count, F, Value from django.db.models import Count, F, Value
from django.contrib.auth import authenticate
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
@ -118,6 +120,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")
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")
@ -133,7 +136,9 @@ def dealer_signup(request, *args, **kwargs):
try: try:
with transaction.atomic(): with transaction.atomic():
user = User.objects.create(email=email, password=password) user = User.objects.create(username=username, email=email)
user.set_password(password)
user.save()
models.Dealer.objects.create( models.Dealer.objects.create(
user=user, user=user,
@ -144,7 +149,14 @@ def dealer_signup(request, *args, **kwargs):
phone_number=phone, phone_number=phone,
address=address, address=address,
) )
return JsonResponse({"message": "User created successfully."}, status=200) user = authenticate(request, username=username, password=password)
if user is not None:
return JsonResponse(
{"message": "User created successfully."}, status=200
)
else:
return JsonResponse({"error": "User creation failed."}, status=400)
except Exception as e: except Exception as e:
return JsonResponse({"error": str(e)}, status=400) return JsonResponse({"error": str(e)}, status=400)
@ -171,7 +183,6 @@ class TestView(TemplateView):
template_name = "test.html" template_name = "test.html"
class AccountingDashboard(LoginRequiredMixin, TemplateView): class AccountingDashboard(LoginRequiredMixin, TemplateView):
template_name = "dashboards/accounting.html" template_name = "dashboards/accounting.html"
@ -256,7 +267,9 @@ class AjaxHandlerView(LoginRequiredMixin, View):
return JsonResponse({"error": _("VIN number exists")}, status=400) return JsonResponse({"error": _("VIN number exists")}, status=400)
if not vin_no or len(vin_no.strip()) != 17: if not vin_no or len(vin_no.strip()) != 17:
return JsonResponse({"success": False, "error": "Invalid VIN number provided."}, status=400) return JsonResponse(
{"success": False, "error": "Invalid VIN number provided."}, status=400
)
vin_no = vin_no.strip() vin_no = vin_no.strip()
vin_data = {} vin_data = {}
@ -481,26 +494,26 @@ def inventory_stats_view(request):
]["total_cars"] += 1 ]["total_cars"] += 1
except Exception as e: except Exception as e:
print(e) print(e)
result = { result = {
"total_cars": cars.count(), "total_cars": cars.count(),
"makes": [ "makes": [
{ {
"make_id": make_data["make_id"], "make_id": make_data["make_id"],
"make_name": make_data["make_name"], "make_name": make_data["make_name"],
"total_cars": make_data["total_cars"], "total_cars": make_data["total_cars"],
"models": [ "models": [
{ {
"model_id": model_data["model_id"], "model_id": model_data["model_id"],
"model_name": model_data["model_name"], "model_name": model_data["model_name"],
"total_cars": model_data["total_cars"], "total_cars": model_data["total_cars"],
"trims": list(model_data["trims"].values()), "trims": list(model_data["trims"].values()),
} }
for model_data in make_data["models"].values() for model_data in make_data["models"].values()
], ],
} }
for make_data in inventory.values() for make_data in inventory.values()
], ],
} }
return render(request, "inventory/inventory_stats.html", {"inventory": result}) return render(request, "inventory/inventory_stats.html", {"inventory": result})
@ -634,6 +647,8 @@ def reserve_car_view(request, car_id):
models.CarReservation.objects.create( models.CarReservation.objects.create(
car=car, reserved_by=request.user, reserved_until=reserved_until car=car, reserved_by=request.user, reserved_until=reserved_until
) )
car.status = models.CarStatusChoices.RESERVED
car.save()
messages.success(request, _("Car reserved successfully.")) messages.success(request, _("Car reserved successfully."))
except Exception as e: except Exception as e:
messages.error(request, f"Error reserving car: {e}") messages.error(request, f"Error reserving car: {e}")
@ -659,7 +674,10 @@ def manage_reservation(request, reservation_id):
return redirect("car_detail", pk=reservation.car.pk) return redirect("car_detail", pk=reservation.car.pk)
elif action == "cancel": elif action == "cancel":
car = reservation.car
reservation.delete() reservation.delete()
car.status = models.CarStatusChoices.AVAILABLE
car.save()
messages.success(request, _("Reservation canceled successfully.")) messages.success(request, _("Reservation canceled successfully."))
return redirect("car_detail", pk=reservation.car.pk) return redirect("car_detail", pk=reservation.car.pk)
@ -724,9 +742,9 @@ class CustomerListView(LoginRequiredMixin, ListView):
if query: if query:
customers = customers.filter( customers = customers.filter(
Q(national_id__icontains=query) | Q(national_id__icontains=query)
Q(first_name__icontains=query) | | Q(first_name__icontains=query)
Q(last_name__icontains=query) | Q(last_name__icontains=query)
) )
return customers return customers
@ -1691,6 +1709,10 @@ class EstimateListView(LoginRequiredMixin, ListView):
template_name = "sales/estimates/estimate_list.html" template_name = "sales/estimates/estimate_list.html"
context_object_name = "estimates" context_object_name = "estimates"
def get_queryset(self):
entity = self.request.user.dealer.entity
return entity.get_estimates()
# class EstimateCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): # class EstimateCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
# model = EstimateModel # model = EstimateModel
@ -1727,98 +1749,143 @@ class EstimateListView(LoginRequiredMixin, ListView):
def create_estimate(request): def create_estimate(request):
dealer = get_user_type(request) dealer = get_user_type(request)
entity = dealer.entity entity = dealer.entity
if request.method == "POST": if request.method == "POST":
try: # try:
data = json.loads(request.body) data = json.loads(request.body)
title = data.get("title") title = data.get("title")
customer_id = data.get("customer") customer_id = data.get("customer")
terms = data.get("terms") terms = data.get("terms")
customer = entity.get_customers().filter(pk=customer_id).first() customer = entity.get_customers().filter(pk=customer_id).first()
estimate = entity.create_estimate( items = data.get("item", [])
estimate_title=title, customer_model=customer, contract_terms=terms quantities = data.get("quantity", [])
)
items = data.get("item", [])
quantities = data.get("quantity", [])
if items and quantities:
if isinstance(items, list):
items_list = [
{"item_id": items[i], "quantity": quantities[i]}
for i in range(len(items))
]
items_txs = []
for item in items_list:
item_instance = ItemModel.objects.get(pk=item.get("item_id"))
car_instace = models.Car.objects.get(vin=item_instance.name)
items_txs.append({
"item_number": item_instance.item_number,
"quantity": float(item.get("quantity")),
"unit_cost": car_instace.finances.cost_price,
"unit_revenue": car_instace.finances.selling_price,
"total_amount": car_instace.finances.cost_price * int(item.get("quantity")),
})
estimate_itemtxs = {
item.get("item_number"): {
"unit_cost": item.get("unit_cost"),
"unit_revenue": item.get("unit_revenue"),
"quantity": item.get("quantity"),
"total_amount": item.get("total_amount"),
}
for item in items_txs
}
else:
item = entity.get_items_all().filter(pk=items).first()
instance = models.Car.objects.get(vin=item.name)
estimate_itemtxs = {
item.item_number: {
"unit_cost": instance.finances.cost_price,
"unit_revenue": instance.finances.selling_price,
"quantity": float(quantities),
"total_amount": instance.finances.total * int(quantities),
}
}
estimate.migrate_itemtxs(
itemtxs=estimate_itemtxs,
commit=True,
operation=EstimateModel.ITEMIZE_APPEND,
)
if isinstance(items, list):
for item in items:
instance = models.Car.objects.get(vin=item)
instance.status = models.CarStatusChoices.RESERVED
instance.save()
else:
instance = models.Car.objects.get(vin=items)
instance.status = models.CarStatusChoices.RESERVED
instance.save()
url = reverse("estimate_detail", kwargs={"pk": estimate.pk})
if not all([items, quantities]):
return JsonResponse( return JsonResponse(
{ {"status": "error", "message": "Items and Quantities are required"},
"status": "success", status=400,
"message": "Estimate created successfully!", )
"url": url, if isinstance(quantities, list):
}, if '0' in quantities:
status=200, return JsonResponse(
) {
except Exception as e: "status": "error",
return JsonResponse( "message": "Quantity must be greater than zero"
})
else:
if int(quantities) <= 0:
return JsonResponse(
{ {
"status": "error", "status": "error",
"message": f"An error occurred while processing the request.{e}", "message": "Quantity must be greater than zero"
}, }
status=400, )
)
estimate = entity.create_estimate(
estimate_title=title, customer_model=customer, contract_terms=terms
)
if isinstance(items, list):
item_quantity_map = {}
for item, quantity in zip(items, quantities):
if item in item_quantity_map:
item_quantity_map[item] += int(quantity)
else:
item_quantity_map[item] = int(quantity)
item_list = list(item_quantity_map.keys())
quantity_list = list(item_quantity_map.values())
items_list = [
{"item_id": item_list[i], "quantity": quantity_list[i]}
for i in range(len(item_list))
]
items_txs = []
for item in items_list:
item_instance = ItemModel.objects.get(pk=item.get("item_id"))
car_instance = models.Car.objects.get(vin=item_instance.name)
items_txs.append(
{
"item_number": item_instance.item_number,
"quantity": float(item.get("quantity")),
"unit_cost": car_instance.finances.cost_price,
"unit_revenue": car_instance.finances.selling_price,
"total_amount": car_instance.finances.cost_price
* int(item.get("quantity")),
}
)
estimate_itemtxs = {
item.get("item_number"): {
"unit_cost": item.get("unit_cost"),
"unit_revenue": item.get("unit_revenue"),
"quantity": item.get("quantity"),
"total_amount": item.get("total_amount"),
}
for item in items_txs
}
else:
item = entity.get_items_all().filter(pk=items).first()
instance = models.Car.objects.get(vin=item.name)
estimate_itemtxs = {
item.item_number: {
"unit_cost": instance.finances.cost_price,
"unit_revenue": instance.finances.selling_price,
"quantity": float(quantities),
"total_amount": instance.finances.total * int(quantities),
}
}
estimate.migrate_itemtxs(
itemtxs=estimate_itemtxs,
commit=True,
operation=EstimateModel.ITEMIZE_APPEND,
)
if isinstance(items, list):
for item in items:
item_instance = ItemModel.objects.get(pk=item)
instance = models.Car.objects.get(vin=item_instance.name)
instance.status = models.CarStatusChoices.RESERVED
instance.save()
else:
item_instance = ItemModel.objects.get(pk=items)
instance = models.Car.objects.get(vin=item_instance.name)
instance.status = models.CarStatusChoices.RESERVED
instance.save()
url = reverse("estimate_detail", kwargs={"pk": estimate.pk})
return JsonResponse(
{
"status": "success",
"message": "Estimate created successfully!",
"url": f"{url}"
})
# 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) form = EstimateModelCreateForm(entity_slug=entity.slug, user_model=entity.admin)
car_list = models.Car.objects.filter(
dealer=dealer, finances__selling_price__gt=0
).exclude(status="reserved")
context = { context = {
"form": form, "form": form,
"items": entity.get_items_all().filter(item_role=ItemModel.ITEM_ROLE_PRODUCT), "items": [
{
"car": x,
"product": entity.get_items_all()
.filter(item_role=ItemModel.ITEM_ROLE_PRODUCT, name=x.vin)
.first(),
}
for x in car_list
],
} }
return render(request, "sales/estimates/estimate_form.html", context) return render(request, "sales/estimates/estimate_form.html", context)
@ -1832,12 +1899,17 @@ class EstimateDetailView(LoginRequiredMixin, DetailView):
estimate = kwargs.get("object") estimate = kwargs.get("object")
if estimate.get_itemtxs_data(): if estimate.get_itemtxs_data():
total = sum( total = sum(
x.ce_cost_estimate for x in estimate.get_itemtxs_data()[0].all() (x.ce_revenue_estimate - models.Car.objects.get(vin=x.item_model.name).finances.discount_amount) for x in estimate.get_itemtxs_data()[0].all()
) )
vat = models.VatRate.objects.filter(is_active=True).first() vat = models.VatRate.objects.filter(is_active=True).first()
# vat = settings.VAT_RATE
kwargs["vat_amount"] = total * vat.vat_rate # Calculate VAT and total with 2 decimal places
kwargs["total"] = (total * vat.vat_rate) + total 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
kwargs["vat"] = vat.rate kwargs["vat"] = vat.rate
kwargs["invoice"] = ( kwargs["invoice"] = (
InvoiceModel.objects.all().filter(ce_model=estimate).first() InvoiceModel.objects.all().filter(ce_model=estimate).first()
@ -1893,11 +1965,22 @@ def estimate_mark_as(request, pk):
messages.error(request, "Estimate is not ready for approval") messages.error(request, "Estimate is not ready for approval")
return redirect("estimate_detail", pk=estimate.pk) return redirect("estimate_detail", pk=estimate.pk)
estimate.mark_as_approved() estimate.mark_as_approved()
messages.success(request, "Estimate approved successfully.")
for i in estimate.get_itemtxs_data()[0]:
car = models.Car.objects.get(vin=i.item_model.name)
car.status = models.CarStatusChoices.SOLD
car.save()
elif mark == "rejected": elif mark == "rejected":
if not estimate.can_cancel(): if not estimate.can_cancel():
messages.error(request, "Estimate is not ready for rejection") messages.error(request, "Estimate is not ready for rejection")
return redirect("estimate_detail", pk=estimate.pk) return redirect("estimate_detail", pk=estimate.pk)
estimate.mark_as_canceled() estimate.mark_as_canceled()
messages.success(request, "Estimate canceled successfully.")
for i in estimate.get_itemtxs_data()[0]:
car = models.Car.objects.get(vin=i.item_model.name)
car.status = models.CarStatusChoices.AVAILABLE
car.save()
elif mark == "completed": elif mark == "completed":
if not estimate.can_complete(): if not estimate.can_complete():
messages.error(request, "Estimate is not ready for completion") messages.error(request, "Estimate is not ready for completion")
@ -2240,7 +2323,7 @@ def create_lead(request, pk):
class LeadListView(ListView): class LeadListView(ListView):
model = models.Customer model = models.Customer
template_name = "crm/lead_list.html" template_name = "crm/lead_list.html"
context_object_name ='customers' context_object_name = "customers"
def get_queryset(self): def get_queryset(self):
query = self.request.GET.get("q") query = self.request.GET.get("q")
@ -2250,9 +2333,9 @@ class LeadListView(ListView):
if query: if query:
customers = customers.filter( customers = customers.filter(
Q(national_id__icontains=query) | Q(national_id__icontains=query)
Q(first_name__icontains=query) | | Q(first_name__icontains=query)
Q(last_name__icontains=query) | Q(last_name__icontains=query)
) )
return customers return customers
@ -2395,5 +2478,5 @@ class ItemServiceListView(ListView):
class SubscriptionPlans(ListView): class SubscriptionPlans(ListView):
model = models.SubscriptionPlan model = models.SubscriptionPlan
template_name = 'subscriptions/subscription_plan.html' template_name = "subscriptions/subscription_plan.html"
context_object_name = 'plans' context_object_name = "plans"

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

View File

@ -117,6 +117,9 @@ const url = "{% url 'account_signup' %}";
const data = await response.json(); const data = await response.json();
if (response.ok) { if (response.ok) {
notify("success","Account created successfully"); notify("success","Account created successfully");
setTimeout(() => {
window.location.href = "{% url 'account_login' %}";
}, 1000);
} else { } else {
notify("error",data.error); notify("error",data.error);
} }

View File

@ -122,12 +122,10 @@
<div class="table-responsive scrollbar mb-3"> <div class="table-responsive scrollbar mb-3">
<table class="table table-sm fs-9 mb-0 overflow-hidden"> <table class="table table-sm fs-9 mb-0 overflow-hidden">
{% if car.finances %} {% if car.finances %}
{% if perms.inventory.view_carfinance %}
<tr> <tr>
<th>{% trans "Cost Price"|capfirst %}</th> <th>{% trans "Cost Price"|capfirst %}</th>
<td>{{ car.finances.cost_price }}</td> <td>{{ car.finances.cost_price }}</td>
</tr> </tr>
{% endif %}
<tr> <tr>
<th>{% trans "Selling Price"|capfirst %}</th> <th>{% trans "Selling Price"|capfirst %}</th>
<td>{{ car.finances.selling_price }}</td> <td>{{ car.finances.selling_price }}</td>
@ -154,15 +152,13 @@
</tr> </tr>
<tr> <tr>
<th>{% trans "Total"|capfirst %}</th> <th>{% trans "Total"|capfirst %}</th>
<td>{{ car.finances.total }}</td> <td>{{ car.finances.total_vat }}</td>
</tr> </tr>
<tr> <tr>
<td colspan="2"> <td colspan="2">
{% if perms.inventory.change_carfinance %}
<a href="{% url 'car_finance_update' car.finances.pk %}" class="btn btn-phoenix-warning btn-sm mb-3"> <a href="{% url 'car_finance_update' car.finances.pk %}" class="btn btn-phoenix-warning btn-sm mb-3">
{% trans "Edit" %} {% trans "Edit" %}
</a> </a>
{% endif %}
{% else %} {% else %}
<p>{% trans "No finance details available." %}</p> <p>{% trans "No finance details available." %}</p>
<a href="{% url 'car_finance_create' car.pk %}" class="btn btn-phoenix-success btn-sm mb-3"> <a href="{% url 'car_finance_create' car.pk %}" class="btn btn-phoenix-success btn-sm mb-3">

View File

@ -131,8 +131,8 @@
<td class="">{{forloop.counter}}</td> <td class="">{{forloop.counter}}</td>
<td class="">{{item.item_model.name}}</td> <td class="">{{item.item_model.name}}</td>
<td class="align-middle">{{item.ce_quantity}}</td> <td class="align-middle">{{item.ce_quantity}}</td>
<td class="align-middle ps-5">{{item.ce_unit_cost_estimate}}</td> <td class="align-middle ps-5">{{item.ce_unit_revenue_estimate}}</td>
<td class="align-middle text-body-tertiary fw-semibold">{{item.ce_total_amount}}</td> <td class="align-middle text-body-tertiary fw-semibold">{{item.ce_revenue_estimate}}</td>
</tr> </tr>
{% endfor %} {% endfor %}
<tr class="bg-body-secondary total-sum"> <tr class="bg-body-secondary total-sum">
@ -187,7 +187,7 @@
// Run the function on page load // Run the function on page load
window.onload = calculateTotals; //window.onload = calculateTotals;
function setFormAction(action) { function setFormAction(action) {
// Get the form element // Get the form element

View File

@ -18,7 +18,7 @@
<div class="mb-2 col-sm-2"> <div class="mb-2 col-sm-2">
<select class="form-control item" name="item[]" required> <select class="form-control item" name="item[]" required>
{% for item in items %} {% for item in items %}
<option value="{{ item.pk }}">{{ item.name }}</option> <option value="{{ item.product.pk }}">{{ item.car.id_car_model }}</option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
@ -68,7 +68,7 @@
<div class="mb-2 col-sm-2"> <div class="mb-2 col-sm-2">
<select class="form-control item" name="item[]" required> <select class="form-control item" name="item[]" required>
{% for item in items %} {% for item in items %}
<option value="{{ item.pk }}">{{ item.name }}</option> <option value="{{ item.product.pk }}">{{ item.car.id_car_model }}</option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>

View File

@ -335,14 +335,12 @@
}); });
document.getElementById('confirmAccept').addEventListener('click', function () { document.getElementById('confirmAccept').addEventListener('click', function () {
// Handle the accept action here // Handle the accept action here
alert('Estimate Accepted');
$('#acceptModal').modal('hide'); $('#acceptModal').modal('hide');
}); });
document.getElementById('confirmReject').addEventListener('click', function () { document.getElementById('confirmReject').addEventListener('click', function () {
// Handle the reject action here // Handle the reject action here
alert('Estimate Rejected');
$('#rejectModal').modal('hide'); $('#rejectModal').modal('hide');
}); });
</script> </script>