diff --git a/.gitignore b/.gitignore
index 3bc4a7b9..3dc9a18c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,7 +17,7 @@ media
.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 8123f86c..5ed86b8d 100644
--- a/inventory/forms.py
+++ b/inventory/forms.py
@@ -28,6 +28,10 @@ import django_tables2 as tables
from django.forms import formset_factory
+class AdditionalServiceForm(forms.ModelForm):
+ class Meta:
+ model = AdditionalServices
+ 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 c1b5f4df..0a7468e3 100644
--- a/inventory/models.py
+++ b/inventory/models.py
@@ -148,6 +148,9 @@ 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"))
+ 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"))
class Meta:
verbose_name = _("Additional Services")
diff --git a/inventory/signals.py b/inventory/signals.py
index 85279d00..54ed0a11 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):
"""
@@ -62,71 +83,106 @@ def update_car_status_on_reservation_delete(sender, instance, **kwargs):
# Create Entity
@receiver(post_save, sender=models.Dealer)
def create_ledger_entity(sender, instance, created, **kwargs):
- if created:
- entity, created = EntityModel.objects.get_or_create(
- name=instance.get_root_dealer.name,
- admin=instance.get_root_dealer.user,
- # address_1=instance.address,
- accrual_method=False,
- fy_start_month=1,
- depth=0,
- )
- print(entity)
- if created:
- default_coa = entity.create_chart_of_accounts(assign_as_default=True,
- commit=True,
- coa_name=_("Chart of Accounts"))
- if default_coa:
- entity.populate_default_coa(activate_accounts=True, coa_model=default_coa)
- print(f"Ledger entity created for Dealer: {instance.name}")
+ if created:
+ root_dealer = instance.get_root_dealer
+ if not root_dealer.entity:
+ 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=False,
+ fy_start_month=1,
+ )
- # entity.create_account(
- # coa_model=coa,
- # code=1010,
- # role='asset_ca_cash',
- # name=_('Cash'),
- # balance_type="debit",
- # )
- # entity.create_account(
- # coa_model=coa,
- # code=1100,
- # role='asset_ca_recv',
- # name=_('Accounts Receivable'),
- # balance_type="debit",
- # )
- # entity.create_account(
- # coa_model=coa,
- # code=1200,
- # role='asset_ca_inv',
- # name=_('Inventory'),
- # balance_type="debit",
- # active=True)
- #
- # entity.create_account(
- # coa_model=coa,
- # code=2010,
- # role='lia_cl_acc_payable',
- # name=_('Accounts Payable'),
- # balance_type="credit",
- # active=True)
- #
- # entity.create_account(
- # coa_model=coa,
- # code=4010,
- # role='in_operational',
- # name=_('Sales Income'),
- # balance_type="credit",
- # active=True)
- #
- # entity.create_account(
- # coa_model=coa,
- # code=5010,
- # role='cogs_regular',
- # name=_('Cost of Goods Sold'),
- # balance_type="debit",
- # active=True)
+ if entity:
+ instance.entity = entity
+ instance.save()
+ coa = entity.create_chart_of_accounts(
+ assign_as_default=True, commit=True, coa_name=_(f"{entity_name}-COA")
+ )
+ if coa:
+ # 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(
+ coa_model=coa,
+ code="1010",
+ role=roles.ASSET_CA_CASH,
+ name=_("Cash"),
+ balance_type="debit",
+ 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,
+ )
+
+ # 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,
+ )
+
+ # 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")
@@ -175,7 +231,7 @@ def create_customer(sender, instance, created, **kwargs):
"sales_tax_rate": 0.15,
"active": True,
"hidden": False,
- "additional_info": {}
+ "additional_info": {},
}
)
@@ -183,65 +239,31 @@ def create_customer(sender, instance, created, **kwargs):
# 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 be0bfa25..ffbb56de 100644
--- a/inventory/utilities/financials.py
+++ b/inventory/utilities/financials.py
@@ -23,22 +23,12 @@ def get_financial_value(name,vat=False):
def get_total_financials(instance,vat=False):
total = 0
- if instance.additional_services.exists():
- total = sum(x.price for x in instance.additional_services.all()) + instance.selling_price
-
+ 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
- # 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 total
-
+
def get_total(instance):
- total = get_total_financials(instance,vat=True)
- # total_vat = get_total_financials(instance,vat=True)
+ 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 44d01d4f..8df32453 100644
--- a/inventory/views.py
+++ b/inventory/views.py
@@ -759,8 +759,8 @@ class QuotationCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateVie
permission_required = ("inventory.add_salequotation",)
def form_valid(self, form):
- dealer = self.request.user.dealer.get_root_dealer
- form.instance.dealer = dealer
+ 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:
@@ -810,12 +810,12 @@ class QuotationDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailVie
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 = dealer.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")
@@ -956,10 +956,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")
@@ -1011,7 +1012,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)
@@ -1355,9 +1357,9 @@ def download_quotation_pdf(request, quotation_id):
@login_required
def invoice_detail(request,pk):
- quotation = get_object_or_404(models.SaleQuotation, pk=pk)
- dealer = request.user.dealer.get_root_dealer
- entity = dealer.entity
+ quotation = get_object_or_404(models.SaleQuotation, pk=pk)
+ 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()
@@ -1366,9 +1368,9 @@ def invoice_detail(request,pk):
return redirect('quotation_detail', pk=pk)
@login_required
def payment_invoice(request,pk):
- quotation = get_object_or_404(models.SaleQuotation, pk=pk)
- dealer = request.user.dealer.get_root_dealer
- entity = dealer.entity
+ quotation = get_object_or_404(models.SaleQuotation, pk=pk)
+ 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()
@@ -1403,7 +1405,9 @@ def payment_create(request, pk):
if form.is_valid():
form.instance.quotation = quotation
insatnce = form.save()
- entity = dealer.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 0f1dda53..27c7b2ab 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -41,6 +41,7 @@
+
{% if LANGUAGE_CODE == 'ar' %}