update
This commit is contained in:
parent
c6a115576d
commit
71ad0bc9e6
@ -26,8 +26,15 @@ from django.forms import ModelMultipleChoiceField
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
import django_tables2 as tables
|
||||
from django.forms import formset_factory
|
||||
from .models import Account
|
||||
from django_ledger.models import EntityModel, ChartOfAccountModel
|
||||
from django_ledger.io import roles, DEBIT, CREDIT
|
||||
|
||||
|
||||
class AdditionalServiceForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = AdditionalServices
|
||||
fields = ['name', 'price','description','vatable', 'uom']
|
||||
|
||||
class PaymentForm(forms.ModelForm):
|
||||
class Meta:
|
||||
@ -253,3 +260,18 @@ class CarSelectionTable(tables.Table):
|
||||
model = Car
|
||||
fields = ['vin', 'year', 'id_car_make', 'id_car_model']
|
||||
template_name = "django_tables2/bootstrap4.html"
|
||||
|
||||
|
||||
|
||||
class AccountForm(forms.ModelForm):
|
||||
role = forms.ChoiceField(choices=roles.ROLE_TUPLES, widget=forms.Select(attrs={'class': 'form-select'}))
|
||||
balance_type = forms.ChoiceField(choices=[(DEBIT, DEBIT),(CREDIT, CREDIT)], widget=forms.Select(attrs={'class': 'form-select'}))
|
||||
class Meta:
|
||||
model = Account
|
||||
fields = ['code', 'name', 'role', 'balance_type', 'active']
|
||||
|
||||
# def __init__(self, *args, **kwargs):
|
||||
# super().__init__(*args, **kwargs)
|
||||
# # Customize the queryset for entity and coa fields if needed
|
||||
# self.fields['entity'].queryset = EntityModel.objects.all()
|
||||
# self.fields['coa'].queryset = ChartOfAccountModel.objects.all()
|
||||
20
inventory/migrations/0025_dealer_entity.py
Normal file
20
inventory/migrations/0025_dealer_entity.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Generated by Django 4.2.17 on 2024-12-25 15:52
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
|
||||
('inventory', '0024_dealer_email'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dealer',
|
||||
name='entity',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='django_ledger.entitymodel'),
|
||||
),
|
||||
]
|
||||
18
inventory/migrations/0026_additionalservices_vatable.py
Normal file
18
inventory/migrations/0026_additionalservices_vatable.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.17 on 2024-12-25 16:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0025_dealer_entity'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='additionalservices',
|
||||
name='vatable',
|
||||
field=models.BooleanField(default=True, verbose_name='Vatable'),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.17 on 2024-12-25 16:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0026_additionalservices_vatable'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='additionalservices',
|
||||
name='vatable',
|
||||
field=models.BooleanField(default=False, verbose_name='Vatable'),
|
||||
),
|
||||
]
|
||||
19
inventory/migrations/0028_additionalservices_uom.py
Normal file
19
inventory/migrations/0028_additionalservices_uom.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.2.17 on 2024-12-25 16:36
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0027_alter_additionalservices_vatable'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='additionalservices',
|
||||
name='uom',
|
||||
field=models.CharField(choices=[('m', 'Meter'), ('cm', 'Centimeter'), ('mm', 'Millimeter'), ('in', 'Inch'), ('ft', 'Foot'), ('kg', 'Kilogram'), ('g', 'Gram'), ('mg', 'Milligram'), ('lb', 'Pound'), ('oz', 'Ounce'), ('L', 'Liter'), ('mL', 'Milliliter'), ('m³', 'Cubic Meter'), ('ft³', 'Cubic Foot'), ('gal', 'Gallon'), ('s', 'Second'), ('min', 'Minute'), ('h', 'Hour'), ('d', 'Day'), ('wk', 'Week'), ('°C', 'Celsius'), ('°F', 'Fahrenheit'), ('K', 'Kelvin'), ('km/h', 'Kilometer per hour'), ('mph', 'Miles per hour'), ('m/s', 'Meter per second'), ('m²', 'Square Meter'), ('cm²', 'Square Centimeter'), ('ft²', 'Square Foot'), ('in²', 'Square Inch'), ('J', 'Joule'), ('kJ', 'Kilojoule'), ('kWh', 'Kilowatt-hour'), ('cal', 'Calorie'), ('W', 'Watt'), ('kW', 'Kilowatt'), ('MW', 'Megawatt'), ('hp', 'Horsepower'), ('N', 'Newton'), ('kN', 'Kilonewton'), ('lbf', 'Pound-force'), ('Pa', 'Pascal'), ('kPa', 'Kilopascal'), ('MPa', 'Megapascal'), ('bar', 'Bar'), ('psi', 'Pound per square inch'), ('Nm', 'Newton-meter'), ('lb-ft', 'Pound-foot'), ('Hz', 'Hertz'), ('kHz', 'Kilohertz'), ('MHz', 'Megahertz'), ('A', 'Ampere'), ('mA', 'Milliampere'), ('µA', 'Microampere'), ('unit', 'Unit'), ('dozen', 'Dozen'), ('box', 'Box'), ('pack', 'Pack')], default='KG', max_length=10, verbose_name='Unit of Measurement'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
20
inventory/migrations/0029_additionalservices_dealer.py
Normal file
20
inventory/migrations/0029_additionalservices_dealer.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Generated by Django 4.2.17 on 2024-12-25 16:47
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0028_additionalservices_uom'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='additionalservices',
|
||||
name='dealer',
|
||||
field=models.ForeignKey(default=2, on_delete=django.db.models.deletion.CASCADE, to='inventory.dealer', verbose_name='Dealer'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
28
inventory/migrations/0030_account.py
Normal file
28
inventory/migrations/0030_account.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Generated by Django 4.2.17 on 2024-12-25 17:00
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
|
||||
('inventory', '0029_additionalservices_dealer'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Account',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('code', models.CharField(max_length=10, unique=True)),
|
||||
('name', models.CharField(max_length=100)),
|
||||
('role', models.CharField(max_length=50)),
|
||||
('balance_type', models.CharField(max_length=10)),
|
||||
('active', models.BooleanField(default=True)),
|
||||
('coa', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_ledger.chartofaccountmodel')),
|
||||
('entity', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_ledger.entitymodel')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@ -27,7 +27,8 @@ from django.utils.timezone import now
|
||||
from .utilities.financials import get_financial_value, get_total, get_total_financials
|
||||
from django.db.models import FloatField
|
||||
from .mixins import LocalizedNameMixin
|
||||
from django_ledger.models import EntityModel
|
||||
from django_ledger.models import EntityModel, ChartOfAccountModel
|
||||
|
||||
|
||||
class CarMake(models.Model, LocalizedNameMixin):
|
||||
id_car_make = models.AutoField(primary_key=True)
|
||||
@ -141,13 +142,104 @@ class DEALER_TYPES(models.TextChoices):
|
||||
Inventory = "Inventory", _("Inventory")
|
||||
Accountent = "Accountent", _("Accountent")
|
||||
Sales = "sales", _("Sales")
|
||||
UNIT_CHOICES = [
|
||||
# Length
|
||||
('m', 'Meter'),
|
||||
('cm', 'Centimeter'),
|
||||
('mm', 'Millimeter'),
|
||||
('in', 'Inch'),
|
||||
('ft', 'Foot'),
|
||||
|
||||
# Mass
|
||||
('kg', 'Kilogram'),
|
||||
('g', 'Gram'),
|
||||
('mg', 'Milligram'),
|
||||
('lb', 'Pound'),
|
||||
('oz', 'Ounce'),
|
||||
|
||||
# Volume
|
||||
('L', 'Liter'),
|
||||
('mL', 'Milliliter'),
|
||||
('m³', 'Cubic Meter'),
|
||||
('ft³', 'Cubic Foot'),
|
||||
('gal', 'Gallon'),
|
||||
|
||||
# Time
|
||||
('s', 'Second'),
|
||||
('min', 'Minute'),
|
||||
('h', 'Hour'),
|
||||
('d', 'Day'),
|
||||
('wk', 'Week'),
|
||||
|
||||
# Temperature
|
||||
('°C', 'Celsius'),
|
||||
('°F', 'Fahrenheit'),
|
||||
('K', 'Kelvin'),
|
||||
|
||||
# Speed
|
||||
('km/h', 'Kilometer per hour'),
|
||||
('mph', 'Miles per hour'),
|
||||
('m/s', 'Meter per second'),
|
||||
|
||||
# Area
|
||||
('m²', 'Square Meter'),
|
||||
('cm²', 'Square Centimeter'),
|
||||
('ft²', 'Square Foot'),
|
||||
('in²', 'Square Inch'),
|
||||
|
||||
# Energy
|
||||
('J', 'Joule'),
|
||||
('kJ', 'Kilojoule'),
|
||||
('kWh', 'Kilowatt-hour'),
|
||||
('cal', 'Calorie'),
|
||||
|
||||
# Power
|
||||
('W', 'Watt'),
|
||||
('kW', 'Kilowatt'),
|
||||
('MW', 'Megawatt'),
|
||||
('hp', 'Horsepower'),
|
||||
|
||||
# Force
|
||||
('N', 'Newton'),
|
||||
('kN', 'Kilonewton'),
|
||||
('lbf', 'Pound-force'),
|
||||
|
||||
# Pressure
|
||||
('Pa', 'Pascal'),
|
||||
('kPa', 'Kilopascal'),
|
||||
('MPa', 'Megapascal'),
|
||||
('bar', 'Bar'),
|
||||
('psi', 'Pound per square inch'),
|
||||
|
||||
# Torque
|
||||
('Nm', 'Newton-meter'),
|
||||
('lb-ft', 'Pound-foot'),
|
||||
|
||||
# Frequency
|
||||
('Hz', 'Hertz'),
|
||||
('kHz', 'Kilohertz'),
|
||||
('MHz', 'Megahertz'),
|
||||
|
||||
# Electric Current
|
||||
('A', 'Ampere'),
|
||||
('mA', 'Milliampere'),
|
||||
('µA', 'Microampere'),
|
||||
|
||||
# Other
|
||||
('unit', 'Unit'), # For counting items
|
||||
('dozen', 'Dozen'), # For counting in dozens
|
||||
('box', 'Box'), # For counting in boxes
|
||||
('pack', 'Pack'), # For counting in packs
|
||||
]
|
||||
|
||||
class AdditionalServices(models.Model, LocalizedNameMixin):
|
||||
name = models.CharField(max_length=255, verbose_name=_("Name"))
|
||||
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"))
|
||||
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")
|
||||
@ -499,7 +591,7 @@ class Dealer(models.Model, LocalizedNameMixin):
|
||||
upload_to="logos/users", blank=True, null=True, verbose_name=_("Logo")
|
||||
)
|
||||
joined_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Joined At"))
|
||||
# entity = models.ForeignKey(EntityModel,on_delete=models.CASCADE,null=True,blank=True)
|
||||
entity = models.ForeignKey(EntityModel,on_delete=models.CASCADE,null=True,blank=True)
|
||||
email = models.EmailField(unique=True, verbose_name=_("Email"))
|
||||
parent_dealer = models.ForeignKey(
|
||||
"self",
|
||||
@ -562,7 +654,7 @@ class Dealer(models.Model, LocalizedNameMixin):
|
||||
@receiver(post_save, sender=User)
|
||||
def create_dealer(instance, created, *args, **kwargs):
|
||||
if created:
|
||||
Dealer.objects.create(user=instance)
|
||||
Dealer.objects.create(user=instance,name=instance.username,email=instance.email)
|
||||
|
||||
|
||||
# Vendor Model
|
||||
@ -875,4 +967,19 @@ class Refund(models.Model):
|
||||
verbose_name_plural = _("refunds")
|
||||
|
||||
def __str__(self):
|
||||
return f"Refund of {self.amount} on {self.refund_date}"
|
||||
return f"Refund of {self.amount} on {self.refund_date}"
|
||||
|
||||
|
||||
|
||||
# accounts
|
||||
class Account(models.Model):
|
||||
entity = models.ForeignKey(EntityModel, on_delete=models.CASCADE)
|
||||
coa = models.ForeignKey(ChartOfAccountModel, on_delete=models.CASCADE)
|
||||
code = models.CharField(max_length=10, unique=True)
|
||||
name = models.CharField(max_length=100)
|
||||
role = models.CharField(max_length=50)
|
||||
balance_type = models.CharField(max_length=10)
|
||||
active = models.BooleanField(default=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.code} - {self.name}"
|
||||
@ -10,7 +10,6 @@ from django.utils.translation import gettext_lazy as _
|
||||
from . import models
|
||||
|
||||
|
||||
|
||||
# @receiver(post_save, sender=models.SaleQuotation)
|
||||
# def link_quotation_to_entity(sender, instance, created, **kwargs):
|
||||
# if created:
|
||||
@ -26,18 +25,20 @@ from . import models
|
||||
@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.
|
||||
"""
|
||||
Signal to create or update the car's location when a car instance is saved.
|
||||
"""
|
||||
try:
|
||||
if created:
|
||||
if instance.dealer is None:
|
||||
raise ValueError(f"Cannot create CarLocation for car {instance.vin}: dealer is missing.")
|
||||
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}."
|
||||
description=f"Initial location set for car {instance.vin}.",
|
||||
)
|
||||
print("Car Location created")
|
||||
except Exception as e:
|
||||
@ -63,70 +64,78 @@ 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,
|
||||
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.pk}-{root_dealer.joined_at.date()}"
|
||||
entity = EntityModel.create_entity(
|
||||
name=entity_name,
|
||||
admin=root_dealer.user,
|
||||
use_accrual_method=True,
|
||||
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}")
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
entity.create_account(
|
||||
coa_model=coa,
|
||||
code="20100",
|
||||
role="lia_cl_acc_payable",
|
||||
name=_("Accounts Payable"),
|
||||
balance_type="credit",
|
||||
active=True,
|
||||
)
|
||||
|
||||
entity.create_account(
|
||||
coa_model=coa,
|
||||
code="40100",
|
||||
role="in_operational",
|
||||
name=_("Sales Income"),
|
||||
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,
|
||||
)
|
||||
|
||||
# uom_name = _("Unit")
|
||||
# unit_abbr = _("U")
|
||||
@ -134,11 +143,9 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
||||
# entity.create_uom(uom_name, unit_abbr)
|
||||
|
||||
|
||||
|
||||
# Create Vendor
|
||||
@receiver(post_save, sender=models.Vendor)
|
||||
def create_ledger_vendor(sender, instance, created, **kwargs):
|
||||
|
||||
if created:
|
||||
entity = EntityModel.objects.filter(name=instance.dealer.name).first()
|
||||
|
||||
@ -153,8 +160,8 @@ def create_ledger_vendor(sender, instance, created, **kwargs):
|
||||
additional_info={
|
||||
"arabic_name": instance.arabic_name,
|
||||
"contact_person": instance.contact_person,
|
||||
})
|
||||
|
||||
},
|
||||
)
|
||||
|
||||
print(f"VendorModel created for Vendor: {instance.name}")
|
||||
|
||||
@ -162,7 +169,9 @@ 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()
|
||||
entity = EntityModel.objects.filter(
|
||||
name=instance.dealer.get_root_dealer.name
|
||||
).first()
|
||||
name = f"{instance.first_name} {instance.middle_name} {instance.last_name}"
|
||||
|
||||
entity.create_customer(
|
||||
@ -174,7 +183,7 @@ def create_customer(sender, instance, created, **kwargs):
|
||||
"sales_tax_rate": 0.15,
|
||||
"active": True,
|
||||
"hidden": False,
|
||||
"additional_info": {}
|
||||
"additional_info": {},
|
||||
}
|
||||
)
|
||||
|
||||
@ -273,4 +282,3 @@ def create_customer(sender, instance, created, **kwargs):
|
||||
# else:
|
||||
# quotation.status = 'pending'
|
||||
# quotation.save()
|
||||
|
||||
|
||||
@ -105,6 +105,19 @@ urlpatterns = [
|
||||
path('representatives/create/', views.RepresentativeCreateView.as_view(), name='representative_create'),
|
||||
path('representatives/<int:pk>/update/', views.RepresentativeUpdateView.as_view(), name='representative_update'),
|
||||
path('representatives/<int:pk>/delete/', views.RepresentativeDeleteView.as_view(), name='representative_delete'),
|
||||
|
||||
#AdditionalServices URLs
|
||||
path('additionalservices/', views.AdditionalServiceListView.as_view(), name='additional_service_list'),
|
||||
path('additionalservices/<int:pk>/', views.AdditionalServiceDetailView.as_view(), name='additional_service_detail'),
|
||||
path('additionalservices/create/', views.AdditionalServiceCreateView.as_view(), name='additional_service_create'),
|
||||
path('additionalservices/<int:pk>/update/', views.AdditionalServiceUpdateView.as_view(), name='additional_service_update'),
|
||||
path('additionalservices/<int:pk>/delete/', views.AdditionalServiceDeleteView.as_view(), name='additional_service_delete'),
|
||||
|
||||
#ledger accounts
|
||||
path('accounts/',views.AccountListView.as_view(), name='account_list'),
|
||||
path('accounts/create/', views.AccountCreateView.as_view(), name='account_create'),
|
||||
path('accounts/update/<int:pk>/', views.AccountUpdateView.as_view(), name='account_update'),
|
||||
path('accounts/delete/<int:pk>/', views.AccountDeleteView.as_view(), name='account_delete')
|
||||
]
|
||||
|
||||
|
||||
|
||||
@ -54,6 +54,8 @@ from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.auth import get_user_model
|
||||
from .utils import get_calculations
|
||||
from .models import Account
|
||||
from .forms import AccountForm
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
@ -1445,3 +1447,92 @@ def payment_create(request, pk):
|
||||
else:
|
||||
form = forms.PaymentForm()
|
||||
return render(request, "sales/payments/payment_create.html", {"quotation": quotation,"form": form})
|
||||
|
||||
|
||||
class AdditionalServiceListView(LoginRequiredMixin, ListView):
|
||||
model = models.AdditionalServices
|
||||
template_name = "sales/additional_services/additional_service_list.html"
|
||||
context_object_name = "services"
|
||||
|
||||
def get_queryset(self):
|
||||
dealer = self.request.user.dealer.get_root_dealer
|
||||
return models.AdditionalServices.objects.filter(dealer=dealer)
|
||||
|
||||
class AdditionalServiceDetailView(LoginRequiredMixin, DetailView):
|
||||
model = models.AdditionalServices
|
||||
template_name = "sales/additional_services/additional_service_detail.html"
|
||||
context_object_name = "service"
|
||||
|
||||
class AdditionalServiceCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
model = models.AdditionalServices
|
||||
form_class = forms.AdditionalServiceForm
|
||||
template_name = "sales/additional_services/additional_service_form.html"
|
||||
success_url = reverse_lazy("additional_service_list")
|
||||
success_message = "Additional Service created successfully."
|
||||
def form_valid(self, form):
|
||||
dealer = self.request.user.dealer.get_root_dealer
|
||||
form.instance.dealer = dealer
|
||||
for entity in EntityModel.objects.all():
|
||||
if entity:
|
||||
uom_name = form.cleaned_data.get('uom')
|
||||
|
||||
uom = entity.create_uom(
|
||||
name=uom_name,
|
||||
unit_abbr=uom_name)
|
||||
service_name = form.cleaned_data.get('name')
|
||||
service_model = entity.create_item_service(
|
||||
name=service_name,
|
||||
uom_model=uom)
|
||||
|
||||
form.save()
|
||||
return super().form_valid(form)
|
||||
class AdditionalServiceUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
model = models.AdditionalServices
|
||||
form_class = forms.AdditionalServiceForm
|
||||
template_name = "sales/additional_services/additional_service_form.html"
|
||||
success_url = reverse_lazy("additional_service_list")
|
||||
success_message = "Additional Service updated successfully."
|
||||
|
||||
class AdditionalServiceDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
||||
model = models.AdditionalServices
|
||||
success_url = reverse_lazy("additional_service_list")
|
||||
success_message = "Additional Service deleted successfully."
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class AccountListView(ListView):
|
||||
model = Account
|
||||
template_name = 'ledger_accounts/account_list.html'
|
||||
context_object_name = 'accounts'
|
||||
|
||||
def get_queryset(self):
|
||||
dealer = self.request.user.dealer.get_root_dealer
|
||||
return Account.objects.filter(entity=dealer.entity)
|
||||
|
||||
class AccountCreateView(CreateView):
|
||||
model = Account
|
||||
form_class = AccountForm
|
||||
template_name = 'ledger_accounts/account_form.html'
|
||||
success_url = reverse_lazy('account_list')
|
||||
|
||||
def form_valid(self, form):
|
||||
dealer = self.request.user.dealer.get_root_dealer
|
||||
if dealer.entity:
|
||||
form.instance.entity = dealer.entity
|
||||
coa = dealer.entity.get_default_coa()
|
||||
if coa:
|
||||
form.instance.coa = coa
|
||||
return super().form_valid(form)
|
||||
|
||||
class AccountUpdateView(UpdateView):
|
||||
model = Account
|
||||
form_class = AccountForm
|
||||
template_name = 'ledger_accounts/account_form.html'
|
||||
success_url = reverse_lazy('account_list')
|
||||
|
||||
class AccountDeleteView(DeleteView):
|
||||
model = Account
|
||||
template_name = 'ledger_accounts/account_confirm_delete.html'
|
||||
success_url = reverse_lazy('account_list')
|
||||
@ -229,6 +229,19 @@
|
||||
</a>
|
||||
<!-- more inner pages-->
|
||||
</li>
|
||||
<li class="nav-item"><a class="nav-link" href="{% url 'additional_service_list' %}">
|
||||
<div class="d-flex align-items-center"><span class="nav-link-text">{% trans "Services"|capfirst %}</span>
|
||||
</div>
|
||||
</a>
|
||||
<!-- more inner pages-->
|
||||
</li>
|
||||
</li>
|
||||
<li class="nav-item"><a class="nav-link" href="{% url 'account_list' %}">
|
||||
<div class="d-flex align-items-center"><span class="nav-link-text">{% trans "Account"|capfirst %}</span>
|
||||
</div>
|
||||
</a>
|
||||
<!-- more inner pages-->
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
11
templates/ledger_accounts/account_confirm_delete.html
Normal file
11
templates/ledger_accounts/account_confirm_delete.html
Normal file
@ -0,0 +1,11 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Delete Account</h1>
|
||||
<p>Are you sure you want to delete "{{ object }}"?</p>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-danger">Delete</button>
|
||||
<a href="{% url 'account_list' %}" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
{% endblock %}
|
||||
13
templates/ledger_accounts/account_form.html
Normal file
13
templates/ledger_accounts/account_form.html
Normal file
@ -0,0 +1,13 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}{% if object %}Edit{% else %}Create{% endif %} Account{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% if object %}Edit{% else %}Create{% endif %} Account</h1>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
</form>
|
||||
{% endblock %}
|
||||
33
templates/ledger_accounts/account_list.html
Normal file
33
templates/ledger_accounts/account_list.html
Normal file
@ -0,0 +1,33 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Accounts</h1>
|
||||
<a href="{% url 'account_create' %}" class="btn btn-primary">Create New Account</a>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Code</th>
|
||||
<th>Name</th>
|
||||
<th>Role</th>
|
||||
<th>Balance Type</th>
|
||||
<th>Active</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for account in accounts %}
|
||||
<tr>
|
||||
<td>{{ account.code }}</td>
|
||||
<td>{{ account.name }}</td>
|
||||
<td>{{ account.role }}</td>
|
||||
<td>{{ account.balance_type }}</td>
|
||||
<td>{{ account.active }}</td>
|
||||
<td>
|
||||
<a href="{% url 'account_update' account.pk %}" class="btn btn-warning">Edit</a>
|
||||
<a href="{% url 'account_delete' account.pk %}" class="btn btn-danger">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
@ -0,0 +1,13 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_filters %}
|
||||
{% block title %}{% trans "Additional Services" %}{% endblock title %}
|
||||
{% block additional_services %}
|
||||
<a class="nav-link active fw-bold">
|
||||
{% trans "Additional Services"|capfirst %}
|
||||
<span class="visually-hidden">(current)</span>
|
||||
</a>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<form method="post">{% csrf_token %}{{ form|crispy }}<button class="btn btn-primary" type="submit">{% trans "Save" %}</button></form>
|
||||
{% endblock %}
|
||||
@ -0,0 +1,47 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Additional Services" %}{% endblock title %}
|
||||
|
||||
{% block additional_services %}
|
||||
<a class="nav-link active fw-bold">
|
||||
{% trans "Additional Services" %}
|
||||
<span class="visually-hidden">(current)</span>
|
||||
</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<a href="{% url 'additional_service_create' %}" class="btn btn-sm btn-success">{% trans "Create" %}</a>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Arabic Name</th>
|
||||
<th scope="col">Description</th>
|
||||
<th scope="col">Price</th>
|
||||
<th scope="col">Vatable</th>
|
||||
<th scope="col">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for service in services %}
|
||||
<tr>
|
||||
<th scope="row"></th>
|
||||
<td>{{service.name}}</td>
|
||||
<td>{{service.arabic_name}}</td>
|
||||
<td>{{service.description}}</td>
|
||||
<td>{{service.price}}</td>
|
||||
<td>{{service.vatable}}</td>
|
||||
<td>
|
||||
<a href="{% url 'additional_service_detail' service.id %}" class="btn btn-sm btn-info">{% trans "Details" %}</a>
|
||||
<a href="{% url 'additional_service_update' service.id %}" class="btn btn-sm btn-primary">{% trans "Update" %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
Loading…
x
Reference in New Issue
Block a user