diff --git a/.gitignore b/.gitignore index 16c1bd8a..e656dd72 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,7 @@ car_inventory/settings.py .idea/**/usage.statistics.xml .idea/**/dictionaries .idea/**/shelf - +Makefile # AWS User-specific .idea/**/aws.xml diff --git a/inventory/forms.py b/inventory/forms.py index b15d7cf1..fee214e9 100644 --- a/inventory/forms.py +++ b/inventory/forms.py @@ -34,7 +34,7 @@ from django_ledger.io import roles, DEBIT, CREDIT class AdditionalServiceForm(forms.ModelForm): class Meta: model = AdditionalServices - fields = ['name', 'price','description','vatable', 'uom'] + fields = ['name', 'price','description','taxable', 'uom'] class PaymentForm(forms.ModelForm): class Meta: diff --git a/inventory/migrations/0031_remove_salequotation_entity.py b/inventory/migrations/0031_remove_salequotation_entity.py new file mode 100644 index 00000000..715069b0 --- /dev/null +++ b/inventory/migrations/0031_remove_salequotation_entity.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.17 on 2024-12-26 07:58 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0030_account'), + ] + + operations = [ + migrations.RemoveField( + model_name='salequotation', + name='entity', + ), + ] diff --git a/inventory/migrations/0032_remove_additionalservices_vatable_and_more.py b/inventory/migrations/0032_remove_additionalservices_vatable_and_more.py new file mode 100644 index 00000000..e97bfde8 --- /dev/null +++ b/inventory/migrations/0032_remove_additionalservices_vatable_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.17 on 2024-12-26 08:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0031_remove_salequotation_entity'), + ] + + operations = [ + migrations.RemoveField( + model_name='additionalservices', + name='vatable', + ), + migrations.AddField( + model_name='additionalservices', + name='taxable', + field=models.BooleanField(default=False, verbose_name='taxable'), + ), + ] diff --git a/inventory/models.py b/inventory/models.py index 5662aacf..92df4595 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -237,7 +237,7 @@ class AdditionalServices(models.Model, LocalizedNameMixin): arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name")) description = models.TextField(verbose_name=_("Description")) price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Price")) - vatable = models.BooleanField(default=False, verbose_name=_("Vatable")) + taxable = models.BooleanField(default=False, verbose_name=_("taxable")) uom = models.CharField(max_length=10, choices=UNIT_CHOICES, verbose_name=_("Unit of Measurement")) dealer = models.ForeignKey("Dealer", on_delete=models.CASCADE, verbose_name=_("Dealer")) @@ -767,8 +767,7 @@ class SaleQuotation(models.Model): ] dealer = models.ForeignKey( Dealer, on_delete=models.CASCADE, related_name="sales", null=True - ) - entity = models.ForeignKey(EntityModel, on_delete=models.CASCADE) + ) customer = models.ForeignKey( Customer, on_delete=models.CASCADE, diff --git a/inventory/signals.py b/inventory/signals.py index 5bf6f486..22c81d21 100644 --- a/inventory/signals.py +++ b/inventory/signals.py @@ -5,10 +5,12 @@ from django.dispatch import receiver from django.utils import timezone from django_ledger.models import EntityModel 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 from . import models +User = get_user_model() # @receiver(post_save, sender=models.SaleQuotation) # def link_quotation_to_entity(sender, instance, created, **kwargs): @@ -22,6 +24,25 @@ from . import models # user = instance.user # if user: # user.delete() + +@receiver(post_save, sender=User) +def create_dealer(instance, created, *args, **kwargs): + if created: + models.Dealer.objects.create(user=instance,name=instance.username,email=instance.email) + +@receiver(post_save, sender=models.Dealer) +def create_user_account(sender, instance, created, **kwargs): + if created: + if instance.dealer_type != "Owner": + user = User.objects.create_user( + username=instance.name, + email=instance.email, + ) + user.set_password("Tenhal@123") + user.save() + instance.user = user + instance.save() + @receiver(post_save, sender=models.Car) def create_car_location(sender, instance, created, **kwargs): """ @@ -67,11 +88,11 @@ def create_ledger_entity(sender, instance, created, **kwargs): if created: root_dealer = instance.get_root_dealer if not root_dealer.entity: - entity_name = f"{root_dealer.name}-{root_dealer.pk}-{root_dealer.joined_at.date()}" + entity_name = f"{root_dealer.name}-{root_dealer.joined_at.date()}" entity = EntityModel.create_entity( name=entity_name, admin=root_dealer.user, - use_accrual_method=True, + use_accrual_method=False, fy_start_month=1, ) @@ -82,60 +103,88 @@ def create_ledger_entity(sender, instance, created, **kwargs): assign_as_default=True, commit=True, coa_name=_(f"{entity_name}-COA") ) if coa: - 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}") - entity.create_account( - coa_model=coa, - code="10100", - role="asset_ca_cash", - name=_("Cash"), - balance_type="debit", - active=True, - ) - entity.create_account( - coa_model=coa, - code="11000", - role="asset_ca_recv", - name=_("Accounts Receivable"), - balance_type="debit", - active=True, - ) - entity.create_account( - coa_model=coa, - code="12000", - role="asset_ca_inv", - name=_("Inventory"), - balance_type="debit", - active=True, - ) + # Create Cash Account + entity.create_account( + coa_model=coa, + code="1010", + role=roles.ASSET_CA_CASH, + name=_("Cash"), + balance_type="debit", + active=True, + ) - entity.create_account( - coa_model=coa, - code="20100", - role="lia_cl_acc_payable", - name=_("Accounts Payable"), - balance_type="credit", - active=True, - ) + # Create Accounts Receivable Account + entity.create_account( + coa_model=coa, + code="1020", + role=roles.ASSET_CA_RECEIVABLES, + name=_("Accounts Receivable"), + balance_type="debit", + active=True, + ) + + # Create Inventory Account + entity.create_account( + coa_model=coa, + code="1030", + role=roles.ASSET_CA_INVENTORY, + name=_("Inventory"), + balance_type="debit", + active=True, + ) - entity.create_account( - coa_model=coa, - code="40100", - role="in_operational", - name=_("Sales Income"), - balance_type="credit", - active=True, - ) + # Create Accounts Payable Account + entity.create_account( + coa_model=coa, + code="2010", + role=roles.LIABILITY_CL_ACC_PAYABLE, + name=_("Accounts Payable"), + balance_type="credit", + active=True, + ) - entity.create_account( - coa_model=coa, - code="50100", - role="cogs_regular", - name=_("Cost of Goods Sold"), - balance_type="debit", - active=True, - ) + # Create Sales Revenue Account + entity.create_account( + coa_model=coa, + code="4010", + role=roles.INCOME_OPERATIONAL, + name=_("Sales Revenue"), + balance_type="credit", + active=True, + ) + + # Create Cost of Goods Sold Account + entity.create_account( + coa_model=coa, + code="5010", + role=roles.COGS, + name=_("Cost of Goods Sold"), + balance_type="debit", + active=True, + ) + + # Create Rent Expense Account + entity.create_account( + coa_model=coa, + code="6010", + role=roles.EXPENSE_OPERATIONAL, + name=_("Rent Expense"), + balance_type="debit", + active=True, + ) + + # Create Utilities Expense Account + entity.create_account( + coa_model=coa, + code="6020", + role=roles.EXPENSE_OPERATIONAL, + name=_("Utilities Expense"), + balance_type="debit", + active=True, + ) # uom_name = _("Unit") # unit_abbr = _("U") @@ -169,87 +218,52 @@ def create_ledger_vendor(sender, instance, created, **kwargs): @receiver(post_save, sender=models.Customer) def create_customer(sender, instance, created, **kwargs): if created: - entity = EntityModel.objects.filter( - name=instance.dealer.get_root_dealer.name - ).first() + dealer = instance.dealer.get_root_dealer + entity = dealer.entity name = f"{instance.first_name} {instance.middle_name} {instance.last_name}" + if entity: + entity.create_customer( + customer_model_kwargs={ + "customer_name": name, + "address_1": instance.address, + "phone": instance.phone_number, + "email": instance.email, + "sales_tax_rate": 0.15, + "active": True, + "hidden": False, + "additional_info": {}, + } + ) - entity.create_customer( - customer_model_kwargs={ - "customer_name": name, - "address_1": instance.address, - "phone": instance.phone_number, - "email": instance.email, - "sales_tax_rate": 0.15, - "active": True, - "hidden": False, - "additional_info": {}, - } - ) - - print(f"Customer created: {name}") + print(f"Customer created: {name}") # Create Item -# @receiver(post_save, sender=models.Car) -# def create_item_model(sender, instance, created, **kwargs): -# item_name = f"{instance.year} - {instance.id_car_make} - {instance.id_car_model} - {instance.id_car_trim}" -# uom_name = _("Car") -# unit_abbr = _("C") -# -# uom, uom_created = UnitOfMeasureModel.objects.get_or_create( -# name=uom_name, -# unit_abbr=unit_abbr -# ) -# -# if uom_created: -# print(f"UOM created: {uom_name}") -# else: -# print(f"Using existing UOM: {uom_name}") -# -# entity = EntityModel.objects.filter(name=instance.dealer.name).first() -# -# inventory_account = AccountModel.objects.first() -# cogs_account = AccountModel.objects.first() -# earnings_account = AccountModel.objects.first() -# -# entity.create_item_product( -# item_name=item_name, -# item_role=ItemModelAbstract.ITEM_ROLE_PRODUCT, -# item_type=ItemModelAbstract.ITEM_TYPE_MATERIAL, -# item_id=instance.vin, -# sold_as_unit=True, -# inventory_received=1.00, -# inventory_received_value=0.00, -# inventory_account=inventory_account, -# for_inventory=True,) -# -# item = ItemModel.objects.create( -# entity=entity, -# uom=uom, -# name=item_name, -# item_role=ItemModelAbstract.ITEM_ROLE_INVENTORY, -# item_type=ItemModelAbstract.ITEM_TYPE_MATERIAL, -# item_id=instance.vin, -# sold_as_unit=True, -# inventory_received=1.00, -# inventory_received_value=0.00, -# inventory_account=inventory_account, -# for_inventory=True, -# is_product_or_service=True, -# cogs_account=cogs_account, -# earnings_account=earnings_account, -# is_active=True, -# additional_info={ -# "remarks": instance.remarks, -# "status": instance.status, -# "stock_type": instance.stock_type, -# "mileage": instance.mileage, -# }, -# ) -# -# print(f"ItemModel {'created' if created else 'updated'} for Car: {item.name}") -# +@receiver(post_save, sender=models.Car) +def create_item_model(sender, instance, created, **kwargs): + item_name = f"{instance.year} - {instance.id_car_make} - {instance.id_car_model} - {instance.id_car_trim}" + dealer = instance.dealer + entity = dealer.entity + + if not entity: + return + + uom_name = _("Car") + unit_abbr = _("C") + uom = entity.get_uom_all().filter(name=uom_name, unit_abbr=unit_abbr).first() + if not uom: + uom = entity.create_uom( + name=uom_name, + unit_abbr=unit_abbr + ) + + entity.create_item_product( + name=item_name, + uom_model=uom, + item_type=ItemModel.ITEM_TYPE_MATERIAL) + + print(f"ItemModel for Car:") + # # # update price - CarFinance # @receiver(post_save, sender=CarFinance) diff --git a/inventory/utilities/financials.py b/inventory/utilities/financials.py index 068f8661..ffbb56de 100644 --- a/inventory/utilities/financials.py +++ b/inventory/utilities/financials.py @@ -22,17 +22,13 @@ def get_financial_value(name,vat=False): def get_total_financials(instance,vat=False): - # price_after_discount = get_financial_value(instance,"selling_price",vat) - get_financial_value(instance,"discount_amount",vat) - # subtotal = ( - # price_after_discount + - # get_financial_value("registration_fee") + - # get_financial_value("administration_fee",vat) + - # get_financial_value("transportation_fee",vat) + - # get_financial_value("custom_card_fee",vat)) - - return 1000 - + total = 0 + if instance.additional_services.count() != 0: + total = sum(x.price for x in instance.additional_services) + instance.selling_price + if vat: + total = (total * settings.VAT_RATE).quantize(Decimal('0.01')) + total + return total + def get_total(instance): - total = get_total_financials(instance) - total_vat = get_total_financials(instance,vat=True) - return total + total_vat \ No newline at end of file + total = get_total_financials(instance,vat=True) + return total \ No newline at end of file diff --git a/inventory/views.py b/inventory/views.py index 349f8ce3..277f440b 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -761,10 +761,8 @@ class QuotationCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateVie permission_required = ("inventory.add_salequotation",) def form_valid(self, form): - dealer = self.request.user.dealer.get_root_dealer - entity = EntityModel.objects.get(name=dealer.get_root_dealer.name) - form.instance.dealer = dealer - form.instance.entity = entity + dealer = self.request.user.dealer.get_root_dealer + form.instance.dealer = dealer quotation = form.save() selected_cars = form.cleaned_data.get("cars") for car in selected_cars: @@ -815,12 +813,13 @@ class QuotationDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailVie @login_required def generate_invoice(request, pk): quotation = get_object_or_404(models.SaleQuotation, pk=pk) + dealer = request.user.dealer.get_root_dealer + entity = dealer.entity if not quotation.is_approved: messages.error( request, "Quotation must be approved before converting to an invoice." ) else: - entity = quotation.entity coa_qs, coa_map = entity.get_all_coa_accounts() cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash") recivable_account = coa_qs.first().get_coa_accounts().filter(name="Accounts Receivable") @@ -961,10 +960,11 @@ def generate_invoice(request, pk): @login_required def post_quotation(request, pk): qoutation = get_object_or_404(models.SaleQuotation, pk=pk) + dealer = request.user.dealer.get_root_dealer + entity = dealer.entity if qoutation.posted: messages.error(request, "Quotation is already posted") - return redirect("quotation_detail", pk=pk) - entity = qoutation.entity + return redirect("quotation_detail", pk=pk) coa_qs, coa_map = entity.get_all_coa_accounts() cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash") recivable_account = coa_qs.first().get_coa_accounts().filter(name="Accounts Receivable") @@ -1016,7 +1016,8 @@ def post_quotation(request, pk): def mark_quotation(request, pk): qoutation = get_object_or_404(models.SaleQuotation, pk=pk) status = request.GET.get("status") - entity = qoutation.entity + dealer = request.user.dealer.get_root_dealer + entity = dealer.entity date = datetime.datetime.now() customer = entity.get_customers().filter(customer_name=qoutation.customer.get_full_name).first() invoice_model = entity.get_invoices().filter(customer=customer) @@ -1361,7 +1362,8 @@ def download_quotation_pdf(request, quotation_id): @login_required def invoice_detail(request,pk): quotation = get_object_or_404(models.SaleQuotation, pk=pk) - entity = quotation.entity + dealer = request.user.dealer.get_root_dealer + entity = dealer.entity customer = entity.get_customers().filter(customer_name=quotation.customer.get_full_name).first() invoice_model = entity.get_invoices() @@ -1371,7 +1373,8 @@ def invoice_detail(request,pk): @login_required def payment_invoice(request,pk): quotation = get_object_or_404(models.SaleQuotation, pk=pk) - entity = quotation.entity + dealer = request.user.dealer.get_root_dealer + entity = dealer.entity customer = entity.get_customers().filter(customer_name=quotation.customer.get_full_name).first() invoice_model = entity.get_invoices() invoice = invoice_model.filter(customer=customer,date_draft=quotation.date_draft).first() @@ -1406,7 +1409,8 @@ def payment_create(request, pk): form.instance.quotation = quotation insatnce = form.save() - entity = quotation.entity + dealer = request.user.dealer.get_root_dealer + entity = dealer.entity customer = entity.get_customers().filter(customer_name=quotation.customer.get_full_name).first() coa_qs, coa_map = entity.get_all_coa_accounts() cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash") diff --git a/templates/base.html b/templates/base.html index 5f8a86fe..798ae8d2 100644 --- a/templates/base.html +++ b/templates/base.html @@ -41,6 +41,7 @@ + {% if LANGUAGE_CODE == 'ar' %} @@ -724,6 +725,7 @@ +