diff --git a/inventory/forms.py b/inventory/forms.py index 8123f86c..b15d7cf1 100644 --- a/inventory/forms.py +++ b/inventory/forms.py @@ -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() \ No newline at end of file diff --git a/inventory/migrations/0025_dealer_entity.py b/inventory/migrations/0025_dealer_entity.py new file mode 100644 index 00000000..e22aaca2 --- /dev/null +++ b/inventory/migrations/0025_dealer_entity.py @@ -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'), + ), + ] diff --git a/inventory/migrations/0026_additionalservices_vatable.py b/inventory/migrations/0026_additionalservices_vatable.py new file mode 100644 index 00000000..f5d06642 --- /dev/null +++ b/inventory/migrations/0026_additionalservices_vatable.py @@ -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'), + ), + ] diff --git a/inventory/migrations/0027_alter_additionalservices_vatable.py b/inventory/migrations/0027_alter_additionalservices_vatable.py new file mode 100644 index 00000000..a8834fed --- /dev/null +++ b/inventory/migrations/0027_alter_additionalservices_vatable.py @@ -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'), + ), + ] diff --git a/inventory/migrations/0028_additionalservices_uom.py b/inventory/migrations/0028_additionalservices_uom.py new file mode 100644 index 00000000..5396f119 --- /dev/null +++ b/inventory/migrations/0028_additionalservices_uom.py @@ -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, + ), + ] diff --git a/inventory/migrations/0029_additionalservices_dealer.py b/inventory/migrations/0029_additionalservices_dealer.py new file mode 100644 index 00000000..11199ba1 --- /dev/null +++ b/inventory/migrations/0029_additionalservices_dealer.py @@ -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, + ), + ] diff --git a/inventory/migrations/0030_account.py b/inventory/migrations/0030_account.py new file mode 100644 index 00000000..9e1c8f47 --- /dev/null +++ b/inventory/migrations/0030_account.py @@ -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')), + ], + ), + ] diff --git a/inventory/models.py b/inventory/models.py index 05e1314c..5662aacf 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -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}" \ No newline at end of file + 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}" \ No newline at end of file diff --git a/inventory/signals.py b/inventory/signals.py index 437e9510..5bf6f486 100644 --- a/inventory/signals.py +++ b/inventory/signals.py @@ -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() - diff --git a/inventory/urls.py b/inventory/urls.py index fdc8ca11..089ed064 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -105,6 +105,19 @@ urlpatterns = [ path('representatives/create/', views.RepresentativeCreateView.as_view(), name='representative_create'), path('representatives//update/', views.RepresentativeUpdateView.as_view(), name='representative_update'), path('representatives//delete/', views.RepresentativeDeleteView.as_view(), name='representative_delete'), + + #AdditionalServices URLs + path('additionalservices/', views.AdditionalServiceListView.as_view(), name='additional_service_list'), + path('additionalservices//', views.AdditionalServiceDetailView.as_view(), name='additional_service_detail'), + path('additionalservices/create/', views.AdditionalServiceCreateView.as_view(), name='additional_service_create'), + path('additionalservices//update/', views.AdditionalServiceUpdateView.as_view(), name='additional_service_update'), + path('additionalservices//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//', views.AccountUpdateView.as_view(), name='account_update'), + path('accounts/delete//', views.AccountDeleteView.as_view(), name='account_delete') ] diff --git a/inventory/views.py b/inventory/views.py index 5daeb62e..349f8ce3 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -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') \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index e7743809..5f8a86fe 100644 --- a/templates/base.html +++ b/templates/base.html @@ -229,6 +229,19 @@ + + + diff --git a/templates/ledger_accounts/account_confirm_delete.html b/templates/ledger_accounts/account_confirm_delete.html new file mode 100644 index 00000000..6ec980b2 --- /dev/null +++ b/templates/ledger_accounts/account_confirm_delete.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} + +{% block content %} +

Delete Account

+

Are you sure you want to delete "{{ object }}"?

+
+ {% csrf_token %} + + Cancel +
+{% endblock %} \ No newline at end of file diff --git a/templates/ledger_accounts/account_form.html b/templates/ledger_accounts/account_form.html new file mode 100644 index 00000000..05f3a750 --- /dev/null +++ b/templates/ledger_accounts/account_form.html @@ -0,0 +1,13 @@ +{% extends 'base.html' %} +{% load crispy_forms_tags %} + +{% block title %}{% if object %}Edit{% else %}Create{% endif %} Account{% endblock %} + +{% block content %} +

{% if object %}Edit{% else %}Create{% endif %} Account

+
+ {% csrf_token %} + {{ form|crispy }} + +
+{% endblock %} \ No newline at end of file diff --git a/templates/ledger_accounts/account_list.html b/templates/ledger_accounts/account_list.html new file mode 100644 index 00000000..e45674df --- /dev/null +++ b/templates/ledger_accounts/account_list.html @@ -0,0 +1,33 @@ +{% extends 'base.html' %} + +{% block content %} +

Accounts

+Create New Account + + + + + + + + + + + + + {% for account in accounts %} + + + + + + + + + {% endfor %} + +
CodeNameRoleBalance TypeActiveActions
{{ account.code }}{{ account.name }}{{ account.role }}{{ account.balance_type }}{{ account.active }} + Edit + Delete +
+{% endblock %} \ No newline at end of file diff --git a/templates/sales/additional_services/additional_service_detail.html b/templates/sales/additional_services/additional_service_detail.html new file mode 100644 index 00000000..e69de29b diff --git a/templates/sales/additional_services/additional_service_form.html b/templates/sales/additional_services/additional_service_form.html new file mode 100644 index 00000000..1f97cd02 --- /dev/null +++ b/templates/sales/additional_services/additional_service_form.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} +{% load i18n %} +{% load crispy_forms_filters %} +{% block title %}{% trans "Additional Services" %}{% endblock title %} +{% block additional_services %} + + {% trans "Additional Services"|capfirst %} + (current) + +{% endblock %} +{% block content %} +
{% csrf_token %}{{ form|crispy }}
+{% endblock %} \ No newline at end of file diff --git a/templates/sales/additional_services/additional_service_list.html b/templates/sales/additional_services/additional_service_list.html new file mode 100644 index 00000000..025894a1 --- /dev/null +++ b/templates/sales/additional_services/additional_service_list.html @@ -0,0 +1,47 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block title %}{% trans "Additional Services" %}{% endblock title %} + +{% block additional_services %} + + {% trans "Additional Services" %} + (current) + +{% endblock %} + +{% block content %} +{% trans "Create" %} + + + + + + + + + + + + + + + {% for service in services %} + + + + + + + + + + {% endfor %} + +
#NameArabic NameDescriptionPriceVatableActions
{{service.name}}{{service.arabic_name}}{{service.description}}{{service.price}}{{service.vatable}} + {% trans "Details" %} + {% trans "Update" %} +
+ + +{% endblock %} \ No newline at end of file