From e1b2fb9c83c8b8fec75a84023d03974ed26273bd Mon Sep 17 00:00:00 2001 From: gitea Date: Tue, 7 Jan 2025 14:35:36 +0000 Subject: [PATCH] fix the invoice and estimate calculation --- inventory/models.py | 48 +++---- inventory/utils.py | 10 +- inventory/views.py | 124 ++++++++++++------ .../sales/estimates/estimate_detail.html | 6 + templates/sales/invoices/invoice_detail.html | 14 +- 5 files changed, 128 insertions(+), 74 deletions(-) diff --git a/inventory/models.py b/inventory/models.py index 92927893..d8eb7301 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -321,42 +321,34 @@ class CarFinance(models.Model): selling_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Selling Price")) discount_amount = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Discount Amount"), default=Decimal('0.00')) - # profit_margin = models.DecimalField(max_digits=14, - # decimal_places=2, - # verbose_name=_("Profit Margin"), - # editable=False) - # vat_amount = models.DecimalField(max_digits=14, - # decimal_places=2, - # verbose_name=_("Vat Amount"), - # editable=False,default=Decimal('0.00')) - # registration_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Registration Fee"), - # default=Decimal('0.00')) - # administration_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Administration Fee"), - # default=Decimal('0.00')) - # transportation_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Transportation Fee"), - # default=Decimal('0.00')) - # custom_card_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Custom Card Fee"), - # default=Decimal('0.00')) + @property - def total(self): - total = 0 + def total(self): + total = self.selling_price + if self.additional_services.count() != 0: total_additional_services = sum(x.default_amount for x in self.additional_services.all()) - total = self.selling_price + total_additional_services - else: - total = self.selling_price - if self.discount_amount != 0: - total = total - self.discount_amount + total += total_additional_services + return total + @property + def total_discount(self): + if self.discount_amount != 0: + total = self.total - self.discount_amount + return total + return self.total + + @property + def total_vat(self): + return self.total_discount + self.vat_amount + @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 + if vat: + return (self.total_discount * Decimal(vat.vat_rate)).quantize(Decimal('0.01')) + return Decimal('0.00') diff --git a/inventory/utils.py b/inventory/utils.py index db755765..1bdeb3c2 100644 --- a/inventory/utils.py +++ b/inventory/utils.py @@ -1,3 +1,4 @@ +from decimal import Decimal from django.shortcuts import redirect from django.contrib import messages from django.utils import timezone @@ -90,4 +91,11 @@ def reserve_car(car,request): except Exception as e: messages.error(request, f"Error reserving car: {e}") - return redirect("car_detail", pk=car.pk) \ No newline at end of file + return redirect("car_detail", pk=car.pk) + + +def calculate_vat_amount(amount): + vat = models.VatRate.objects.filter(is_active=True).first() + if vat: + return ((amount * Decimal(vat.vat_rate)).quantize(Decimal('0.01')),vat.vat_rate) + return amount \ No newline at end of file diff --git a/inventory/views.py b/inventory/views.py index c8024d8a..e4ede577 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -69,7 +69,13 @@ from . import models, forms from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.messages.views import SuccessMessageMixin from django.contrib.auth.models import Group -from .utils import get_calculations, reserve_car, send_email, get_user_type +from .utils import ( + calculate_vat_amount, + get_calculations, + reserve_car, + send_email, + get_user_type, +) from django.contrib.auth.models import User from allauth.account import views from django.db.models import Count, F, Value @@ -639,8 +645,8 @@ class CustomCardCreateView(LoginRequiredMixin, CreateView): @login_required() def reserve_car_view(request, car_id): if request.method == "POST": - car = get_object_or_404(models.Car, pk=car_id) - if car.is_reserved(): + car = get_object_or_404(models.Car, pk=car_id) + if car.is_reserved(): messages.error(request, _("This car is already reserved.")) return redirect("car_detail", pk=car.pk) response = reserve_car(car, request) @@ -749,13 +755,16 @@ class CustomerDetailView(LoginRequiredMixin, DetailView): model = models.Customer template_name = "customers/view_customer.html" context_object_name = "customer" - + def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) name = f"{context['customer'].first_name} {context['customer'].middle_name} {context['customer'].last_name}" - context["estimates"] = self.request.entity.get_estimates().filter(customer__customer_name=name) + context["estimates"] = self.request.entity.get_estimates().filter( + customer__customer_name=name + ) return context + class CustomerCreateView( LoginRequiredMixin, SuccessMessageMixin, @@ -1425,6 +1434,7 @@ def download_quotation_pdf(request, quotation_id): # return redirect("quotation_detail", pk=pk) + def payment_create(request, pk): quotation = get_object_or_404(models.SaleQuotation, pk=pk) dealer = get_user_type(request) @@ -1573,6 +1583,7 @@ def bank_account_delete(request, pk): # Accounts + class AccountListView(LoginRequiredMixin, ListView): model = AccountModel template_name = "ledger/coa_accounts/account_list.html" @@ -1707,9 +1718,9 @@ def create_estimate(request): "item_number": item_instance.item_number, "quantity": float(item.get("quantity")), "unit_cost": car_instance.finances.cost_price, - "unit_revenue": car_instance.finances.total_vat, - "total_amount": car_instance.finances.total_vat - * int(item.get("quantity")), + "unit_revenue": car_instance.finances.selling_price, + "total_amount": (car_instance.finances.total_vat) + * int(item.get("quantity")), } ) @@ -1730,7 +1741,8 @@ def create_estimate(request): "unit_cost": instance.finances.cost_price, "unit_revenue": instance.finances.selling_price, "quantity": float(quantities), - "total_amount": instance.finances.total * int(quantities), + "total_amount": instance.finances.total_vat + * int(quantities), } } @@ -1743,9 +1755,9 @@ def create_estimate(request): if isinstance(items, list): for item in items: item_instance = ItemModel.objects.get(pk=item) - instance = models.Car.objects.get(vin=item_instance.name) + instance = models.Car.objects.get(vin=item_instance.name) reserve_car(instance, request) - + else: item_instance = ItemModel.objects.get(pk=items) instance = models.Car.objects.get(vin=item_instance.name) @@ -1759,7 +1771,7 @@ def create_estimate(request): "url": f"{url}", } ) - + form = EstimateModelCreateForm(entity_slug=entity.slug, user_model=entity.admin) car_list = models.Car.objects.filter( dealer=dealer, finances__selling_price__gt=0 @@ -1786,21 +1798,29 @@ class EstimateDetailView(LoginRequiredMixin, DetailView): def get_context_data(self, **kwargs): estimate = kwargs.get("object") - if estimate.get_itemtxs_data(): + if estimate.get_itemtxs_data(): total = sum( - ( - models.Car.objects.get(vin=x.item_model.name).finances.total_vat + 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() ) + discount_amount = sum( + models.CarFinance.objects.get( + car__vin=i.item_model.name + ).discount_amount + for i in estimate.get_itemtxs_data()[0].all() + ) vat = models.VatRate.objects.filter(is_active=True).first() - - vat_amount = round(total * vat.vat_rate, 2) - # grand_total = round( - # (total * vat.vat_rate) + total, 2 - # ) + grand_total = float(total) - float(discount_amount) + vat_amount = round(float(grand_total) * float(vat.vat_rate), 2) + kwargs["vat_amount"] = vat_amount - kwargs["total"] = total + kwargs["total"] = grand_total + vat_amount + kwargs["discount_amount"] = discount_amount kwargs["vat"] = vat.rate kwargs["invoice"] = ( InvoiceModel.objects.all().filter(ce_model=estimate).first() @@ -1848,24 +1868,24 @@ def estimate_mark_as(request, pk): if mark == "review": if not estimate.can_review(): messages.error(request, "Estimate is not ready for review") - return redirect("estimate_detail", pk=estimate.pk) + return redirect("estimate_detail", pk=estimate.pk) estimate.mark_as_review() elif mark == "approved": if not estimate.can_approve(): messages.error(request, "Estimate is not ready for approval") return redirect("estimate_detail", pk=estimate.pk) - estimate.mark_as_approved() - messages.success(request, "Estimate approved successfully.") + estimate.mark_as_approved() + messages.success(request, "Estimate approved successfully.") elif mark == "rejected": if not estimate.can_cancel(): messages.error(request, "Estimate is not ready for rejection") return redirect("estimate_detail", pk=estimate.pk) estimate.mark_as_canceled() - messages.success(request, "Estimate canceled successfully.") + messages.success(request, "Estimate canceled successfully.") elif mark == "completed": if not estimate.can_complete(): messages.error(request, "Estimate is not ready for completion") - return redirect("estimate_detail", pk=estimate.pk) + return redirect("estimate_detail", pk=estimate.pk) estimate.save() messages.success(request, "Estimate marked as " + mark.upper()) @@ -1891,15 +1911,27 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): def get_context_data(self, **kwargs): invoice = kwargs.get("object") + vat = models.VatRate.objects.filter(is_active=True).first() if invoice.get_itemtxs_data(): total = sum( - x.unit_cost * x.quantity for x in invoice.get_itemtxs_data()[0].all() + float(x.ce_revenue_estimate) * float(x.ce_quantity) + for x in invoice.ce_model.get_itemtxs_data()[0].all() ) - total = int(total) - vat = models.VatRate.objects.filter(is_active=True).first() - kwargs["vat_amount"] = total * vat.vat_rate - kwargs["total"] = (total * vat.vat_rate) + total + discount_amount = sum( + models.CarFinance.objects.get( + car__vin=i.item_model.name + ).discount_amount + for i in invoice.get_itemtxs_data()[0].all() + ) + + grand_total = float(total) - float(discount_amount) + vat_amount = round(float(grand_total) * float(vat.vat_rate), 2) + + + kwargs["vat_amount"] = vat_amount + kwargs["total"] = grand_total + vat_amount + kwargs["discount_amount"] = discount_amount kwargs["vat"] = vat.rate kwargs["amount_left"] = invoice.amount_due - invoice.amount_paid kwargs["payments"] = JournalEntryModel.objects.filter( @@ -1961,17 +1993,27 @@ def invoice_create(request, pk): invoice_model.save() unit_items = estimate.get_itemtxs_data()[0] - - # for item in unit_items: - # car = models.Car.objects.get(vin=item.item_model.name) - + + itemtxs = [] + for item in unit_items: + car = models.Car.objects.get(vin=item.item_model.name) + itemtxs.append( + { + "item_number": item.item_model.item_number, + "unit_cost": car.finances.total_vat, + "unit_revenue": car.finances.total_vat, + "quantity": item.ce_quantity, + "total_amount": float(car.finances.total_vat) + * float(item.ce_quantity), + } + ) invoice_itemtxs = { - i.item_model.item_number: { - "unit_cost": i.ce_unit_revenue_estimate, - "quantity": i.ce_quantity, - "total_amount": i.ce_revenue_estimate, + i.get("item_number"): { + "unit_cost": i.get("unit_cost"), + "quantity": i.get("quantity"), + "total_amount": i.get("total_amount"), } - for i in unit_items + for i in itemtxs } invoice_itemtxs = invoice_model.migrate_itemtxs( @@ -2117,7 +2159,6 @@ class UserActivityLogListView(ListView): return queryset - # CRM RELATED VIEWS def create_lead(request, pk): customer = get_object_or_404(models.Customer, pk=pk) @@ -2344,6 +2385,7 @@ def send_email_view(request, pk): {"estimate": estimate, "message": msg}, ) + # errors def custom_page_not_found_view(request, exception): return render(request, "errors/404.html", {}) diff --git a/templates/sales/estimates/estimate_detail.html b/templates/sales/estimates/estimate_detail.html index 67ad84c2..79bcdbd6 100644 --- a/templates/sales/estimates/estimate_detail.html +++ b/templates/sales/estimates/estimate_detail.html @@ -141,6 +141,12 @@ {{vat_amount}} + + {% trans "Discount Amount" %} + + {{discount_amount}} + + {% trans "Grand Total" %} diff --git a/templates/sales/invoices/invoice_detail.html b/templates/sales/invoices/invoice_detail.html index 669f5e38..5a0e6dd4 100644 --- a/templates/sales/invoices/invoice_detail.html +++ b/templates/sales/invoices/invoice_detail.html @@ -145,13 +145,13 @@ - {% for item in invoice.get_itemtxs_data.0 %} + {% for item in invoice.ce_model.get_itemtxs_data.0 %} {{forloop.counter}} {{item.item_model.name}} - {{item.quantity}} - {{item.unit_cost}} - {{item.total_amount}} + {{item.ce_quantity}} + {{item.ce_unit_revenue_estimate}} + {{item.ce_revenue_estimate}} {% endfor %} @@ -160,6 +160,12 @@ {{vat_amount}} + + {% trans "Discount Amount" %} + + {{discount_amount}} + + {% trans "Grand Total" %}