diff --git a/inventory/forms.py b/inventory/forms.py index 3dd9e7af..d9ebacc0 100644 --- a/inventory/forms.py +++ b/inventory/forms.py @@ -367,6 +367,18 @@ class CarSelectionTable(tables.Table): 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( widget=forms.EmailInput( attrs={ diff --git a/inventory/models.py b/inventory/models.py index 5a1fae58..c112ad6d 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -38,18 +38,22 @@ class DealerUserManager(UserManager): return user - -UNIT_CHOICES = ( - ("Unit", _("Unit")), - ("Kg", _("Kg")), - ("L", _("L")), - ("m", _("m")), - ("cm", _("cm")), - ("m2", _("m2")), - ("m3", _("m3")), - ("m3", _("m3")), -) - +class UnitOfMeasure(models.TextChoices): + EACH = 'EA', 'Each' + PAIR = 'PR', 'Pair' + SET = 'SET', 'Set' + GALLON = 'GAL', 'Gallon' + LITER = 'L', 'Liter' + METER = 'M', 'Meter' + KILOGRAM = 'KG', 'Kilogram' + HOUR = 'HR', 'Hour' + 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): rate = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal('0.15')) is_active = models.BooleanField(default=True) @@ -173,7 +177,7 @@ class AdditionalServices(models.Model, LocalizedNameMixin): description = models.TextField(verbose_name=_("Description")) price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Price")) 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")) 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"), # default=Decimal('0.00')) @property - def total(self): - vat = VatRate.objects.filter(is_active=True).first() + def total(self): total = 0 if self.additional_services.count() != 0: total_additional_services = sum(x.default_amount for x in self.additional_services.all()) @@ -344,9 +347,19 @@ class CarFinance(models.Model): total = self.selling_price if self.discount_amount != 0: total = total - self.discount_amount - total = (total * vat.vat_rate).quantize(Decimal('0.01')) + 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): return f"Car: {self.car}, Selling Price: {self.selling_price}" diff --git a/inventory/signals.py b/inventory/signals.py index dc03f048..5914020c 100644 --- a/inventory/signals.py +++ b/inventory/signals.py @@ -3,11 +3,10 @@ from django.dispatch import receiver from django.utils.translation import gettext_lazy as _ from django.contrib.auth import get_user_model from django_ledger.io import roles -from django_ledger.models import EntityModel, AccountModel, ItemModel, ItemModelAbstract, UnitOfMeasureModel, \ - VendorModel, ChartOfAccountModel +from django_ledger.models import EntityModel,AccountModel,ItemModel,ItemModelAbstract,UnitOfMeasureModel, VendorModel from . import models from .models import OpportunityLog - +from .utils import get_dealer_from_instance User = get_user_model() @@ -45,42 +44,49 @@ User = get_user_model() # 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): """ Signal to create or update the car's location when a car instance is saved. """ - if created: - print(instance) - models.CarLocation.objects.create( - car=instance, - owner=instance.dealer, - showroom=instance.dealer, - description=f"Initial location set for car {instance.vin}." - ) - print("Car Location created") + try: + if created: + if instance.dealer is None: + raise ValueError(f"Cannot create CarLocation for car {instance.vin}: dealer is missing.") + + models.CarLocation.objects.create( + car=instance, + owner=instance.dealer, + 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) -def update_car_status_on_reservation(sender, instance, created, **kwargs): - if created: - car = instance.car - car.status = models.CarStatusChoices.RESERVED - car.save() +# @receiver(post_save, sender=models.CarReservation) +# def update_car_status_on_reservation(sender, instance, created, **kwargs): +# if created: +# car = instance.car +# car.status = models.CarStatusChoices.RESERVED +# car.save() -@receiver(post_delete, sender=models.CarReservation) -def update_car_status_on_reservation_delete(sender, instance, **kwargs): - car = instance.car - if not car.is_reserved(): - car.status = models.CarStatusChoices.AVAILABLE - car.save() +# @receiver(post_delete, sender=models.CarReservation) +# def update_car_status_on_reservation_delete(sender, instance, **kwargs): +# car = instance.car +# if not car.is_reserved(): +# car.status = models.CarStatusChoices.AVAILABLE +# car.save() # Create Entity @receiver(post_save, sender=models.Dealer) def create_ledger_entity(sender, instance, created, **kwargs): + if created: entity_name = instance.user.dealer.name 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) 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, - code="1101", + code="1010", role=roles.ASSET_CA_CASH, name=_("Cash"), balance_type="debit", active=True, ) - + asset_ca_cash.role_default = True + asset_ca_cash.save() # Create Accounts Receivable Account - entity.create_account( + asset_ca_receivables = entity.create_account( coa_model=coa, code="1102", role=roles.ASSET_CA_RECEIVABLES, @@ -119,9 +134,11 @@ def create_ledger_entity(sender, instance, created, **kwargs): balance_type="debit", active=True, ) + asset_ca_receivables.role_default = True + asset_ca_receivables.save() # Create Inventory Account - entity.create_account( + asset_ca_inventory = entity.create_account( coa_model=coa, code="1103", role=roles.ASSET_CA_INVENTORY, @@ -130,8 +147,10 @@ def create_ledger_entity(sender, instance, created, **kwargs): active=True, ) + asset_ca_inventory.role_default = True + asset_ca_inventory.save() # Create Accounts Payable Account - entity.create_account( + asset_ca_accounts_payable = entity.create_account( coa_model=coa, code="2101", role=roles.LIABILITY_CL_ACC_PAYABLE, @@ -139,8 +158,10 @@ def create_ledger_entity(sender, instance, created, **kwargs): balance_type="credit", active=True, ) + asset_ca_accounts_payable.role_default = True + asset_ca_accounts_payable.save() # Create Equity Account - entity.create_account( + asset_ca_equity = entity.create_account( coa_model=coa, code="3101", role=roles.EQUITY_CAPITAL, @@ -148,9 +169,11 @@ def create_ledger_entity(sender, instance, created, **kwargs): balance_type="credit", active=True, ) + asset_ca_equity.role_default = True + asset_ca_equity.save() # Create Sales Revenue Account - entity.create_account( + asset_ca_revenue = entity.create_account( coa_model=coa, code="4101", role=roles.INCOME_OPERATIONAL, @@ -158,9 +181,11 @@ def create_ledger_entity(sender, instance, created, **kwargs): balance_type="credit", active=True, ) + asset_ca_revenue.role_default = True + asset_ca_revenue.save() # Create Cost of Goods Sold Account - entity.create_account( + asset_ca_cogs = entity.create_account( coa_model=coa, code="5101", role=roles.COGS, @@ -168,9 +193,11 @@ def create_ledger_entity(sender, instance, created, **kwargs): balance_type="debit", active=True, ) + asset_ca_cogs.role_default = True + asset_ca_cogs.save() # Create Rent Expense Account - entity.create_account( + expense = entity.create_account( coa_model=coa, code="6101", role=roles.EXPENSE_OPERATIONAL, @@ -178,6 +205,8 @@ def create_ledger_entity(sender, instance, created, **kwargs): balance_type="debit", active=True, ) + expense.role_default = True + expense.save() # Create Utilities Expense Account entity.create_account( @@ -188,7 +217,15 @@ def create_ledger_entity(sender, instance, created, **kwargs): balance_type="debit", 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 @receiver(post_save, sender=models.Vendor) @@ -241,95 +278,38 @@ def create_customer(sender, instance, created, **kwargs): # Create Item @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: - product_name = f"{instance.year} - {instance.id_car_make} - {instance.id_car_model}" - entity = EntityModel.objects.get(name=instance.dealer.name) - uom = entity.get_uom_all().first() - # coa_model = entity.get_default_coa() - inventory_account = AccountModel.objects.first() - cogs_account = AccountModel.objects.first() - earnings_account = AccountModel.objects.first() - - - ItemModel.objects.create( - entity=entity, - uom=uom, - name=product_name, - item_role=ItemModelAbstract.ITEM_ROLE_PRODUCT, - item_type=ItemModelAbstract.ITEM_TYPE_MATERIAL, - 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}") - + coa = entity.get_default_coa() + uom = entity.get_uom_all().get(name="Unit") + + if not entity.get_items_all().filter(name=instance.vin).first(): + entity.create_item_product( + name=f"{instance.vin}", + item_type=ItemModel.ITEM_TYPE_MATERIAL, + uom_model=uom, + coa_model=coa + ) + entity.create_item_inventory( + name=f"{instance.vin}", + item_type=ItemModel.ITEM_TYPE_MATERIAL, + uom_model=uom, + coa_model=coa + ) + + + # # update price - CarFinance -# @receiver(post_save, sender=models.CarFinance) -# def update_item_model_cost(sender, instance, created, **kwargs): -# -# ItemModel.objects.filter(item_id=instance.car.vin).update( -# inventory_received_value=instance.cost_price, -# default_amount=instance.selling_price, -# ) -# print(f"Inventory item updated with CarFinance data for Car: {instance.car}") +@receiver(post_save, sender=models.CarFinance) +def update_item_model_cost(sender, instance, created, **kwargs): + + ItemModel.objects.filter(item_id=instance.car.vin).update( + inventory_received_value=instance.cost_price, + default_amount=instance.cost_price, + ) + print(f"Inventory item updated with CarFinance data for Car: {instance.car}") diff --git a/inventory/views.py b/inventory/views.py index cd84b736..7c2cbc59 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -71,6 +71,8 @@ from .utils import get_calculations, send_email, get_user_type from django.contrib.auth.models import User from allauth.account import views from django.db.models import Count, F, Value +from django.contrib.auth import authenticate + logger = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) @@ -118,6 +120,7 @@ def dealer_signup(request, *args, **kwargs): wf1 = data.get("wizardValidationForm1") wf2 = data.get("wizardValidationForm2") wf3 = data.get("wizardValidationForm3") + username = wf1.get("username") email = wf1.get("email") password = wf1.get("password") password_confirm = wf1.get("confirm_password") @@ -133,7 +136,9 @@ def dealer_signup(request, *args, **kwargs): try: 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( user=user, @@ -144,7 +149,14 @@ def dealer_signup(request, *args, **kwargs): phone_number=phone, 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: return JsonResponse({"error": str(e)}, status=400) @@ -171,7 +183,6 @@ class TestView(TemplateView): template_name = "test.html" - class AccountingDashboard(LoginRequiredMixin, TemplateView): template_name = "dashboards/accounting.html" @@ -256,7 +267,9 @@ class AjaxHandlerView(LoginRequiredMixin, View): return JsonResponse({"error": _("VIN number exists")}, status=400) 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_data = {} @@ -481,26 +494,26 @@ def inventory_stats_view(request): ]["total_cars"] += 1 except Exception as e: print(e) - result = { - "total_cars": cars.count(), - "makes": [ - { - "make_id": make_data["make_id"], - "make_name": make_data["make_name"], - "total_cars": make_data["total_cars"], - "models": [ - { - "model_id": model_data["model_id"], - "model_name": model_data["model_name"], - "total_cars": model_data["total_cars"], - "trims": list(model_data["trims"].values()), - } - for model_data in make_data["models"].values() - ], - } - for make_data in inventory.values() - ], - } + result = { + "total_cars": cars.count(), + "makes": [ + { + "make_id": make_data["make_id"], + "make_name": make_data["make_name"], + "total_cars": make_data["total_cars"], + "models": [ + { + "model_id": model_data["model_id"], + "model_name": model_data["model_name"], + "total_cars": model_data["total_cars"], + "trims": list(model_data["trims"].values()), + } + for model_data in make_data["models"].values() + ], + } + for make_data in inventory.values() + ], + } return render(request, "inventory/inventory_stats.html", {"inventory": result}) @@ -634,6 +647,8 @@ def reserve_car_view(request, car_id): models.CarReservation.objects.create( car=car, reserved_by=request.user, reserved_until=reserved_until ) + car.status = models.CarStatusChoices.RESERVED + car.save() messages.success(request, _("Car reserved successfully.")) except Exception as 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) elif action == "cancel": + car = reservation.car reservation.delete() + car.status = models.CarStatusChoices.AVAILABLE + car.save() messages.success(request, _("Reservation canceled successfully.")) return redirect("car_detail", pk=reservation.car.pk) @@ -724,9 +742,9 @@ class CustomerListView(LoginRequiredMixin, ListView): if query: customers = customers.filter( - Q(national_id__icontains=query) | - Q(first_name__icontains=query) | - Q(last_name__icontains=query) + Q(national_id__icontains=query) + | Q(first_name__icontains=query) + | Q(last_name__icontains=query) ) return customers @@ -1691,6 +1709,10 @@ class EstimateListView(LoginRequiredMixin, ListView): template_name = "sales/estimates/estimate_list.html" context_object_name = "estimates" + def get_queryset(self): + entity = self.request.user.dealer.entity + return entity.get_estimates() + # class EstimateCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): # model = EstimateModel @@ -1727,98 +1749,143 @@ class EstimateListView(LoginRequiredMixin, ListView): def create_estimate(request): dealer = get_user_type(request) entity = dealer.entity + if request.method == "POST": - try: - data = json.loads(request.body) - title = data.get("title") - customer_id = data.get("customer") - terms = data.get("terms") - customer = entity.get_customers().filter(pk=customer_id).first() + # try: + data = json.loads(request.body) + title = data.get("title") + customer_id = data.get("customer") + terms = data.get("terms") + customer = entity.get_customers().filter(pk=customer_id).first() - estimate = entity.create_estimate( - estimate_title=title, customer_model=customer, contract_terms=terms - ) - - 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}) + items = data.get("item", []) + quantities = data.get("quantity", []) + + if not all([items, quantities]): return JsonResponse( - { - "status": "success", - "message": "Estimate created successfully!", - "url": url, - }, - status=200, - ) - except Exception as e: - return JsonResponse( + {"status": "error", "message": "Items and Quantities are required"}, + status=400, + ) + if isinstance(quantities, list): + if '0' in quantities: + return JsonResponse( + { + "status": "error", + "message": "Quantity must be greater than zero" + }) + else: + if int(quantities) <= 0: + return JsonResponse( { "status": "error", - "message": f"An error occurred while processing the request.{e}", - }, - status=400, - ) + "message": "Quantity must be greater than zero" + } + ) + + 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) + car_list = models.Car.objects.filter( + dealer=dealer, finances__selling_price__gt=0 + ).exclude(status="reserved") context = { "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) @@ -1832,12 +1899,17 @@ class EstimateDetailView(LoginRequiredMixin, DetailView): estimate = kwargs.get("object") if estimate.get_itemtxs_data(): 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 = settings.VAT_RATE - kwargs["vat_amount"] = total * vat.vat_rate - kwargs["total"] = (total * vat.vat_rate) + total + + # Calculate VAT and total with 2 decimal places + vat_amount = round(total * vat.vat_rate, 2) # Round to 2 decimal places + grand_total = round((total * vat.vat_rate) + total, 2) # Round to 2 decimal places + + # Add values to the context + kwargs["vat_amount"] = vat_amount + kwargs["total"] = grand_total kwargs["vat"] = vat.rate kwargs["invoice"] = ( 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") return redirect("estimate_detail", pk=estimate.pk) 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": if not estimate.can_cancel(): messages.error(request, "Estimate is not ready for rejection") return redirect("estimate_detail", pk=estimate.pk) estimate.mark_as_canceled() + messages.success(request, "Estimate canceled successfully.") + 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": if not estimate.can_complete(): messages.error(request, "Estimate is not ready for completion") @@ -2240,7 +2323,7 @@ def create_lead(request, pk): class LeadListView(ListView): model = models.Customer template_name = "crm/lead_list.html" - context_object_name ='customers' + context_object_name = "customers" def get_queryset(self): query = self.request.GET.get("q") @@ -2250,9 +2333,9 @@ class LeadListView(ListView): if query: customers = customers.filter( - Q(national_id__icontains=query) | - Q(first_name__icontains=query) | - Q(last_name__icontains=query) + Q(national_id__icontains=query) + | Q(first_name__icontains=query) + | Q(last_name__icontains=query) ) return customers @@ -2395,5 +2478,5 @@ class ItemServiceListView(ListView): class SubscriptionPlans(ListView): model = models.SubscriptionPlan - template_name = 'subscriptions/subscription_plan.html' - context_object_name = 'plans' + template_name = "subscriptions/subscription_plan.html" + context_object_name = "plans" diff --git a/static/images/logos/users/tenhal_hero.png b/static/images/logos/users/tenhal_hero.png new file mode 100644 index 00000000..c4892c6c Binary files /dev/null and b/static/images/logos/users/tenhal_hero.png differ diff --git a/templates/account/signup-wizard.html b/templates/account/signup-wizard.html index ac3c850b..66399b65 100644 --- a/templates/account/signup-wizard.html +++ b/templates/account/signup-wizard.html @@ -117,6 +117,9 @@ const url = "{% url 'account_signup' %}"; const data = await response.json(); if (response.ok) { notify("success","Account created successfully"); + setTimeout(() => { + window.location.href = "{% url 'account_login' %}"; + }, 1000); } else { notify("error",data.error); } diff --git a/templates/inventory/car_detail.html b/templates/inventory/car_detail.html index 84483915..f85a4bc4 100644 --- a/templates/inventory/car_detail.html +++ b/templates/inventory/car_detail.html @@ -122,12 +122,10 @@