diff --git a/car_inventory/__pycache__/settings.cpython-311.pyc b/car_inventory/__pycache__/settings.cpython-311.pyc deleted file mode 100644 index e01d2827..00000000 Binary files a/car_inventory/__pycache__/settings.cpython-311.pyc and /dev/null differ diff --git a/inventory/management/commands/test.py b/inventory/management/commands/test.py new file mode 100644 index 00000000..eae6f1b1 --- /dev/null +++ b/inventory/management/commands/test.py @@ -0,0 +1,17 @@ +from django.core.management.base import BaseCommand +from django.core.mail import send_mail +from allauth.account.models import EmailConfirmation +from inventory.tasks import send_email +from django.contrib.auth import get_user_model + +User = get_user_model() +class Command(BaseCommand): + + def handle(self, *args, **kwargs): + user = User.objects.last() + print(user.email) + # 2. Force email confirmation + # email = user.emailaddress_set.first() + confirmation = EmailConfirmation.create(user.email) + confirmation.send() + diff --git a/inventory/management/commands/test_task_process_running.py b/inventory/management/commands/test_task_process_running.py new file mode 100644 index 00000000..8603c5b3 --- /dev/null +++ b/inventory/management/commands/test_task_process_running.py @@ -0,0 +1,12 @@ +from django.core.management.base import BaseCommand +# from background_task.models import Task +# from background_task import background +# from django_q.tasks import async_task +from inventory.tasks import send_email + + +class Command(BaseCommand): + + def handle(self, *args, **kwargs): + send_email('ismail.mosa@gmail.com', 'teset1@gmail.com', 'test', 'test') + diff --git a/inventory/migrations/0001_initial.py b/inventory/migrations/0001_initial.py index 17b2bbcb..97404722 100644 --- a/inventory/migrations/0001_initial.py +++ b/inventory/migrations/0001_initial.py @@ -1,7 +1,9 @@ -# Generated by Django 5.1.7 on 2025-03-27 20:55 +# Generated by Django 5.1.7 on 2025-05-04 10:42 import datetime +import django.core.validators import django.db.models.deletion +import django.utils.timezone import inventory.mixins import inventory.models import phonenumber_field.modelfields @@ -442,7 +444,7 @@ class Migration(migrations.Migration): name='Email', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.PositiveIntegerField()), + ('object_id', models.UUIDField()), ('from_email', models.TextField(blank=True, null=True, verbose_name='From Email')), ('to_email', models.TextField(blank=True, null=True, verbose_name='To Email')), ('subject', models.TextField(blank=True, null=True, verbose_name='Subject')), @@ -491,7 +493,7 @@ class Migration(migrations.Migration): name='Notes', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.PositiveIntegerField()), + ('object_id', models.UUIDField()), ('note', models.TextField(verbose_name='Note')), ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), ('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')), @@ -698,7 +700,7 @@ class Migration(migrations.Migration): ('contact_person', models.CharField(max_length=100, verbose_name='Contact Person')), ('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')), ('email', models.EmailField(max_length=255, verbose_name='Email Address')), - ('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')), + ('address', models.CharField(max_length=200, verbose_name='Address')), ('logo', models.ImageField(blank=True, null=True, upload_to='logos/vendors', verbose_name='Logo')), ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), ('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vendors', to='inventory.dealer')), @@ -751,4 +753,33 @@ class Migration(migrations.Migration): 'unique_together': {('car', 'exterior', 'interior')}, }, ), + migrations.CreateModel( + name='PaymentHistory', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('user_data', models.JSONField(blank=True, null=True)), + ('amount', models.DecimalField(decimal_places=2, max_digits=10, validators=[django.core.validators.MinValueValidator(0.01)])), + ('currency', models.CharField(default='SAR', max_length=3)), + ('payment_date', models.DateTimeField(default=django.utils.timezone.now)), + ('status', models.CharField(choices=[('initiated', 'initiated'), ('pending', 'Pending'), ('completed', 'Completed'), ('paid', 'Paid'), ('failed', 'Failed'), ('refunded', 'Refunded'), ('cancelled', 'Cancelled')], default='pending', max_length=10)), + ('payment_method', models.CharField(choices=[('credit_card', 'Credit Card'), ('debit_card', 'Debit Card'), ('paypal', 'PayPal'), ('bank_transfer', 'Bank Transfer'), ('crypto', 'Cryptocurrency'), ('other', 'Other')], max_length=20)), + ('transaction_id', models.CharField(blank=True, max_length=100, null=True, unique=True)), + ('invoice_number', models.CharField(blank=True, max_length=50, null=True)), + ('order_reference', models.CharField(blank=True, max_length=100, null=True)), + ('gateway_response', models.JSONField(blank=True, null=True)), + ('gateway_name', models.CharField(blank=True, max_length=50, null=True)), + ('description', models.TextField(blank=True, null=True)), + ('is_recurring', models.BooleanField(default=False)), + ('billing_email', models.EmailField(blank=True, max_length=254, null=True)), + ('billing_address', models.TextField(blank=True, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name_plural': 'Payment Histories', + 'ordering': ['-payment_date'], + 'indexes': [models.Index(fields=['transaction_id'], name='inventory_p_transac_9469f3_idx'), models.Index(fields=['user'], name='inventory_p_user_id_c31626_idx'), models.Index(fields=['status'], name='inventory_p_status_abcb77_idx'), models.Index(fields=['payment_date'], name='inventory_p_payment_b3068c_idx')], + }, + ), ] diff --git a/inventory/migrations/0002_alter_notes_object_id.py b/inventory/migrations/0002_alter_notes_object_id.py deleted file mode 100644 index 25b4e530..00000000 --- a/inventory/migrations/0002_alter_notes_object_id.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.7 on 2025-04-24 16:23 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='notes', - name='object_id', - field=models.PositiveBigIntegerField(), - ), - ] diff --git a/inventory/migrations/0003_alter_email_object_id.py b/inventory/migrations/0003_alter_email_object_id.py deleted file mode 100644 index a2d528d2..00000000 --- a/inventory/migrations/0003_alter_email_object_id.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.7 on 2025-04-24 16:23 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0002_alter_notes_object_id'), - ] - - operations = [ - migrations.AlterField( - model_name='email', - name='object_id', - field=models.PositiveBigIntegerField(), - ), - ] diff --git a/inventory/migrations/0004_alter_email_object_id_alter_notes_object_id.py b/inventory/migrations/0004_alter_email_object_id_alter_notes_object_id.py deleted file mode 100644 index da3b3751..00000000 --- a/inventory/migrations/0004_alter_email_object_id_alter_notes_object_id.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 5.1.7 on 2025-04-24 16:25 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0003_alter_email_object_id'), - ] - - operations = [ - migrations.AlterField( - model_name='email', - name='object_id', - field=models.UUIDField(), - ), - migrations.AlterField( - model_name='notes', - name='object_id', - field=models.UUIDField(), - ), - ] diff --git a/inventory/migrations/0005_alter_email_object_id_alter_notes_object_id.py b/inventory/migrations/0005_alter_email_object_id_alter_notes_object_id.py deleted file mode 100644 index e74ab873..00000000 --- a/inventory/migrations/0005_alter_email_object_id_alter_notes_object_id.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 5.1.7 on 2025-04-24 16:54 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0004_alter_email_object_id_alter_notes_object_id'), - ] - - operations = [ - migrations.AlterField( - model_name='email', - name='object_id', - field=models.PositiveIntegerField(), - ), - migrations.AlterField( - model_name='notes', - name='object_id', - field=models.PositiveIntegerField(), - ), - ] diff --git a/inventory/migrations/0006_alter_email_object_id_alter_notes_object_id.py b/inventory/migrations/0006_alter_email_object_id_alter_notes_object_id.py deleted file mode 100644 index 55c616ad..00000000 --- a/inventory/migrations/0006_alter_email_object_id_alter_notes_object_id.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 5.1.7 on 2025-04-24 17:01 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0005_alter_email_object_id_alter_notes_object_id'), - ] - - operations = [ - migrations.AlterField( - model_name='email', - name='object_id', - field=models.UUIDField(), - ), - migrations.AlterField( - model_name='notes', - name='object_id', - field=models.UUIDField(), - ), - ] diff --git a/inventory/migrations/0007_paymenthistory.py b/inventory/migrations/0007_paymenthistory.py deleted file mode 100644 index 3fead346..00000000 --- a/inventory/migrations/0007_paymenthistory.py +++ /dev/null @@ -1,47 +0,0 @@ -# Generated by Django 5.1.7 on 2025-04-29 13:33 - -import django.core.validators -import django.db.models.deletion -import django.utils.timezone -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0006_alter_email_object_id_alter_notes_object_id'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='PaymentHistory', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('user_data', models.JSONField(blank=True, null=True)), - ('amount', models.DecimalField(decimal_places=2, max_digits=10, validators=[django.core.validators.MinValueValidator(0.01)])), - ('currency', models.CharField(default='SAR', max_length=3)), - ('payment_date', models.DateTimeField(default=django.utils.timezone.now)), - ('status', models.CharField(choices=[('initiated', 'initiated'), ('pending', 'Pending'), ('completed', 'Completed'), ('paid', 'Paid'), ('failed', 'Failed'), ('refunded', 'Refunded'), ('cancelled', 'Cancelled')], default='pending', max_length=10)), - ('payment_method', models.CharField(choices=[('credit_card', 'Credit Card'), ('debit_card', 'Debit Card'), ('paypal', 'PayPal'), ('bank_transfer', 'Bank Transfer'), ('crypto', 'Cryptocurrency'), ('other', 'Other')], max_length=20)), - ('transaction_id', models.CharField(blank=True, max_length=100, null=True, unique=True)), - ('invoice_number', models.CharField(blank=True, max_length=50, null=True)), - ('order_reference', models.CharField(blank=True, max_length=100, null=True)), - ('gateway_response', models.JSONField(blank=True, null=True)), - ('gateway_name', models.CharField(blank=True, max_length=50, null=True)), - ('description', models.TextField(blank=True, null=True)), - ('is_recurring', models.BooleanField(default=False)), - ('billing_email', models.EmailField(blank=True, max_length=254, null=True)), - ('billing_address', models.TextField(blank=True, null=True)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name_plural': 'Payment Histories', - 'ordering': ['-payment_date'], - 'indexes': [models.Index(fields=['transaction_id'], name='inventory_p_transac_9469f3_idx'), models.Index(fields=['user'], name='inventory_p_user_id_c31626_idx'), models.Index(fields=['status'], name='inventory_p_status_abcb77_idx'), models.Index(fields=['payment_date'], name='inventory_p_payment_b3068c_idx')], - }, - ), - ] diff --git a/inventory/migrations/0008_alter_vendor_address.py b/inventory/migrations/0008_alter_vendor_address.py deleted file mode 100644 index 226fd8ab..00000000 --- a/inventory/migrations/0008_alter_vendor_address.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.1.7 on 2025-05-01 13:25 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('inventory', '0007_paymenthistory'), - ] - - operations = [ - migrations.AlterField( - model_name='vendor', - name='address', - field=models.CharField(default='', max_length=200, verbose_name='Address'), - preserve_default=False, - ), - ] diff --git a/inventory/signals.py b/inventory/signals.py index 67a1ab72..63dedd73 100644 --- a/inventory/signals.py +++ b/inventory/signals.py @@ -145,7 +145,8 @@ def create_ledger_entity(sender, instance, created, **kwargs): # Create COA accounts, background task create_coa_accounts(instance.pk) - create_settings(instance.pk) + + # create_settings(instance.pk) # create_accounts_for_make(instance.pk) @receiver(post_save, sender=models.Dealer) diff --git a/inventory/tasks.py b/inventory/tasks.py index adb82e9b..e3fdcb18 100644 --- a/inventory/tasks.py +++ b/inventory/tasks.py @@ -1,540 +1,15 @@ +from time import sleep +from datetime import datetime +from django.db import transaction from django_ledger.io import roles from django.core.mail import send_mail from background_task import background from django.utils.translation import gettext_lazy as _ from inventory.models import DealerSettings,CarMake,Dealer -@background -def create_coa_accounts(pk): - instance = Dealer.objects.get(pk=pk) - entity = instance.entity - coa = entity.get_default_coa() - - # Cash Account - asset_ca_cash = entity.create_account( - coa_model=coa, - code="1101", - role=roles.ASSET_CA_CASH, - name=_("Cash"), - balance_type="debit", - active=True, - ) - asset_ca_cash.role_default = True - asset_ca_cash.save() - - # Accounts Receivable Account - asset_ca_receivables = entity.create_account( - coa_model=coa, - code="1102", - role=roles.ASSET_CA_RECEIVABLES, - name=_("Accounts Receivable"), - balance_type="debit", - active=True, - ) - asset_ca_receivables.role_default = True - asset_ca_receivables.save() - - # Inventory Account - asset_ca_inventory = entity.create_account( - coa_model=coa, - code="1103", - role=roles.ASSET_CA_INVENTORY, - name=_("Inventory"), - balance_type="debit", - active=True, - ) - asset_ca_inventory.role_default = True - asset_ca_inventory.save() - - - # Prepaid Expenses Account - asset_ca_prepaid = entity.create_account( - coa_model=coa, - code="1104", - role=roles.ASSET_CA_PREPAID, - name=_("Prepaid Expenses"), - balance_type="debit", - active=True, - ) - asset_ca_prepaid.role_default = True - asset_ca_prepaid.save() - - # Employee Expenses Account - asset_ca_prepaid_employee = entity.create_account( - coa_model=coa, - code="1105", - role=roles.ASSET_CA_PREPAID, - name=_("Employee Advance"), - balance_type="debit", - active=True, - ) - - - # VAT Payable Account - liability_ltl_vat_receivable = entity.create_account( - coa_model=coa, - code="1106", - role=roles.ASSET_CA_RECEIVABLES, - name=_("VAT Receivable"), - balance_type="debit", - active=True, - ) - - # Buildings Accumulated Depreciation Account - asset_ppe_buildings_accum_depreciation = entity.create_account( - coa_model=coa, - code="1201", - role=roles.ASSET_PPE_BUILDINGS_ACCUM_DEPRECIATION, - name=_("Buildings - Accum. Depreciation"), - balance_type="credit", - active=True, - ) - asset_ppe_buildings_accum_depreciation.role_default = True - asset_ppe_buildings_accum_depreciation.save() - - # intangible Account - asset_lti_land_intangable = entity.create_account( - coa_model=coa, - code="1202", - role=roles.ASSET_INTANGIBLE_ASSETS, - name=_("Intangible Assets"), - balance_type="debit", - active=True, - ) - asset_lti_land_intangable.role_default = True - asset_lti_land_intangable.save() - - # investment property Account - asset_lti_land_investment = entity.create_account( - coa_model=coa, - code="1204", - role=roles.ASSET_LTI_SECURITIES, - name=_("Investments"), - balance_type="debit", - active=True, - ) - asset_lti_land_investment.role_default = True - asset_lti_land_investment.save() - - # # Notes Receivable Account - # asset_lti_notes_receivable = entity.create_account( - # coa_model=coa, - # code="1201", - # role=roles.ASSET_LTI_NOTES_RECEIVABLE, - # name=_("Notes Receivable"), - # balance_type="debit", - # active=True, - # ) - # asset_lti_notes_receivable.role_default = True - # asset_lti_notes_receivable.save() - - # # Land Account - # asset_lti_land = entity.create_account( - # coa_model=coa, - # code="1202", - # role=roles.ASSET_LTI_LAND, - # name=_("Land"), - # balance_type="debit", - # active=True, - # ) - # asset_lti_land.role_default = True - # asset_lti_land.save() - - - # Buildings Account - asset_ppe_buildings = entity.create_account( - coa_model=coa, - code="1301", - role=roles.ASSET_PPE_BUILDINGS, - name=_("Buildings"), - balance_type="debit", - active=True, - ) - asset_ppe_buildings.role_default = True - asset_ppe_buildings.save() - - - - # Accounts Payable Account - liability_cl_acc_payable = entity.create_account( - coa_model=coa, - code="2101", - role=roles.LIABILITY_CL_ACC_PAYABLE, - name=_("Accounts Payable"), - balance_type="credit", - active=True, - ) - liability_cl_acc_payable.role_default = True - liability_cl_acc_payable.save() - - # Deferred Revenue Account - liability_cl_def_rev = entity.create_account( - coa_model=coa, - code="2103", - role=roles.LIABILITY_CL_DEFERRED_REVENUE, - name=_("Deferred Revenue"), - balance_type="credit", - active=True, - ) - liability_cl_def_rev.role_default = True - liability_cl_def_rev.save() - - # Wages Payable Account - liability_cl_wages_payable = entity.create_account( - coa_model=coa, - code="2102", - role=roles.LIABILITY_CL_WAGES_PAYABLE, - name=_("Wages Payable"), - balance_type="credit", - active=True, - ) - liability_cl_wages_payable.role_default = True - liability_cl_wages_payable.save() - - # Long-Term Notes Payable Account - liability_ltl_notes_payable = entity.create_account( - coa_model=coa, - code="2201", - role=roles.LIABILITY_LTL_NOTES_PAYABLE, - name=_("Long-Term Notes Payable"), - balance_type="credit", - active=True, - ) - liability_ltl_notes_payable.role_default = True - liability_ltl_notes_payable.save() - - # VAT Payable Account - liability_ltl_vat_payable = entity.create_account( - coa_model=coa, - code="2106", - role=roles.LIABILITY_CL_OTHER, - name=_("VAT Payable"), - balance_type="credit", - active=True, - ) - - # taxes Payable Account - liability_ltl_taxes_payable = entity.create_account( - coa_model=coa, - code="2107", - role=roles.LIABILITY_CL_OTHER, - name=_("Taxes Payable"), - balance_type="credit", - active=True, - ) - - # social insurance Payable Account - liability_ltl_social_insurance_payable = entity.create_account( - coa_model=coa, - code="2108", - role=roles.LIABILITY_LTL_NOTES_PAYABLE, - name=_("Social Insurance Payable"), - balance_type="credit", - active=True, - ) - - # End of Service Benefits - entity.create_account(coa_model=coa, code="2202", role=roles.LIABILITY_LTL_NOTES_PAYABLE, name=_("End of Service Benefits"), balance_type="credit", active=True) - - # Mortgage Payable Account - liability_ltl_mortgage_payable = entity.create_account( - coa_model=coa, - code="2203", - role=roles.LIABILITY_LTL_MORTGAGE_PAYABLE, - name=_("Mortgage Payable"), - balance_type="credit", - active=True, - ) - liability_ltl_mortgage_payable.role_default = True - liability_ltl_mortgage_payable.save() - - # Capital - equity_capital = entity.create_account(coa_model=coa, code="3101", role=roles.EQUITY_CAPITAL, name=_("Registered Capital"), balance_type="credit", active=True) - equity_capital.role_default = True - equity_capital.save() - entity.create_account(coa_model=coa, code="3102", role=roles.EQUITY_CAPITAL, name=_("Additional Paid-In Capital"), balance_type="credit", active=True) - - # Other Equity - other_equity = entity.create_account(coa_model=coa, code="3201", role=roles.EQUITY_COMMON_STOCK, name=_("Opening Balances"), balance_type="credit", active=True) - other_equity.role_default = True - other_equity.save() - - # Reserves - reserve = entity.create_account(coa_model=coa, code="3301", role=roles.EQUITY_ADJUSTMENT, name=_("Statutory Reserve"), balance_type="credit", active=True) - reserve.role_default = True - reserve.save() - entity.create_account(coa_model=coa, code="3302", role=roles.EQUITY_ADJUSTMENT, name=_("Foreign Currency Translation Reserve"), balance_type="credit", active=True) - - # Retained Earnings Account - equity_retained_earnings = entity.create_account( - coa_model=coa, - code="3401", - role=roles.EQUITY_PREFERRED_STOCK, - name=_("Operating Profits and Losses"), - balance_type="credit", - active=True, - ) - equity_retained_earnings.role_default = True - equity_retained_earnings.save() - - equity_retained_earnings_losses = entity.create_account( - coa_model=coa, - code="3402", - role=roles.EQUITY_PREFERRED_STOCK, - name=_("Retained Earnings (or Losses)"), - balance_type="credit", - active=True, - ) - - # Sales Revenue Account - income_operational = entity.create_account( - coa_model=coa, - code="4101", - role=roles.INCOME_OPERATIONAL, - name=_("Sales Revenue"), - balance_type="credit", - active=True, - ) - income_operational.role_default = True - income_operational.save() - - # Interest Income Account - income_interest = entity.create_account( - coa_model=coa, - code="4102", - role=roles.INCOME_INTEREST, - name=_("Interest Income"), - balance_type="credit", - active=True, - ) - income_interest.role_default = True - income_interest.save() - - # Uneared Income Account - income_unearned = entity.create_account( - coa_model=coa, - code="4103", - role=roles.INCOME_OTHER, - name=_("Unearned Income"), - balance_type="credit", - active=True, - ) - - # Operating Revenues - entity.create_account(coa_model=coa, code="4104", role=roles.INCOME_OPERATIONAL, name=_("Sales/Service Revenue"), balance_type="credit", active=True) - - #Non-Operating Revenues - entity.create_account(coa_model=coa, code="4201", role=roles.INCOME_OTHER, name=_("Non-Operating Revenues"), balance_type="credit", active=True) - - - # Cost of Goods Sold (COGS) Account - expense_cogs = entity.create_account( - coa_model=coa, - code="5101", - role=roles.COGS, - name=_("Cost of Goods Sold"), - balance_type="debit", - active=True, - ) - expense_cogs.role_default = True - expense_cogs.save() - - - # accrued Expenses Account - expense_cogs = entity.create_account( - coa_model=coa, - code="6117", - role=roles.EXPENSE_OPERATIONAL, - name=_("Accrued Expenses"), - balance_type="debit", - active=True, - ) - - # accrued salaries Account - expense_cogs = entity.create_account( - coa_model=coa, - code="6118", - role=roles.EXPENSE_OPERATIONAL, - name=_("Accrued Salaries"), - balance_type="debit", - active=True, - ) - - # Rent Expense Account - expense_rent = entity.create_account( - coa_model=coa, - code="6102", - role=roles.EXPENSE_OPERATIONAL, - name=_("Rent Expense"), - balance_type="debit", - active=True, - ) - # expense_rent.role_default = True - # expense_rent.save() - - # Salaries and Administrative Fees - expense_salaries = entity.create_account( - coa_model=coa, - code="6103", - role=roles.EXPENSE_OPERATIONAL, - name=_("Salaries and Administrative Fees"), - balance_type="debit", - active=True, - ) - - # Medical Insurance - expense_medical_insurance = entity.create_account( - coa_model=coa, - code="6104", - role=roles.EXPENSE_OPERATIONAL, - name=_("Medical Insurance"), - balance_type="debit", - active=True, - ) - - # Marketing and Advertising Expenses - expense_marketing = entity.create_account( - coa_model=coa, - code="6105", - role=roles.EXPENSE_OPERATIONAL, - name=_("Marketing and Advertising Expenses"), - balance_type="debit", - active=True, - ) - - # Commissions and Incentives - expense_commissions = entity.create_account( - coa_model=coa, - code="6106", - role=roles.EXPENSE_OPERATIONAL, - name=_("Commissions and Incentives"), - balance_type="debit", - active=True, - ) - - # Travel Tickets - expense_travel = entity.create_account( - coa_model=coa, - code="6107", - role=roles.EXPENSE_OPERATIONAL, - name=_("Travel Tickets"), - balance_type="debit", - active=True, - ) - - # Social Insurance - expense_other = entity.create_account( - coa_model=coa, - code="6108", - role=roles.EXPENSE_OPERATIONAL, - name=_("Social Insurance"), - balance_type="debit", - active=True, - ) - - # Government Fees - expense_other = entity.create_account( - coa_model=coa, - code="6109", - role=roles.EXPENSE_OPERATIONAL, - name=_("Government Fees"), - balance_type="debit", - active=True, - ) - - # Fees and Subscriptions - expense_other = entity.create_account( - coa_model=coa, - code="6110", - role=roles.EXPENSE_OPERATIONAL, - name=_("Fees and Subscriptions"), - balance_type="debit", - active=True, - ) - - # Office Services Expenses - expense_other = entity.create_account( - coa_model=coa, - code="6111", - role=roles.EXPENSE_OPERATIONAL, - name=_("Office Services Expenses"), - balance_type="debit", - active=True, - ) - - # Office Supplies and Printing - expense_other = entity.create_account( - coa_model=coa, - code="6112", - role=roles.EXPENSE_OPERATIONAL, - name=_("Office Supplies and Printing"), - balance_type="debit", - active=True, - ) - - # Hospitality Expenses - expense_other = entity.create_account( - coa_model=coa, - code="6113", - role=roles.EXPENSE_OPERATIONAL, - name=_("Hospitality Expenses"), - balance_type="debit", - active=True, - ) - - # Bank Commissions - expense_other = entity.create_account( - coa_model=coa, - code="6114", - role=roles.EXPENSE_OPERATIONAL, - name=_("Bank Commissions"), - balance_type="debit", - active=True, - ) - - # Other Expenses - expense_other = entity.create_account( - coa_model=coa, - code="6115", - role=roles.EXPENSE_OPERATIONAL, - name=_("Other Expenses"), - balance_type="debit", - active=True, - ) - - # Transportation Expenses - expense_other = entity.create_account( - coa_model=coa, - code="6116", - role=roles.EXPENSE_OPERATIONAL, - name=_("Transportation Expenses"), - balance_type="debit", - active=True, - ) - - # 5.1 Direct Costs - entity.create_account(coa_model=coa, code="6201", role=roles.EXPENSE_OPERATIONAL, name=_("Cost of Goods Sold"), balance_type="debit", active=True) - entity.create_account(coa_model=coa, code="6202", role=roles.EXPENSE_OPERATIONAL, name=_("Salaries and Wages"), balance_type="debit", active=True) - entity.create_account(coa_model=coa, code="6203", role=roles.EXPENSE_OPERATIONAL, name=_("Sales Commissions"), balance_type="debit", active=True) - entity.create_account(coa_model=coa, code="6204", role=roles.EXPENSE_OPERATIONAL, name=_("Shipping and Customs Clearance"), balance_type="debit", active=True) - - # 5.3 Non-Operating Expenses - entity.create_account(coa_model=coa, code="6301", role=roles.EXPENSE_OTHER, name=_("Zakat"), balance_type="debit", active=True) - entity.create_account(coa_model=coa, code="6302", role=roles.EXPENSE_OTHER, name=_("Taxes"), balance_type="debit", active=True) - entity.create_account(coa_model=coa, code="6303", role=roles.EXPENSE_OTHER, name=_("Foreign Currency Translation"), balance_type="debit", active=True) - entity.create_account(coa_model=coa, code="6304", role=roles.EXPENSE_OTHER, name=_("Interest Expenses"), balance_type="debit", active=True) # @background -# def create_groups(instance): -# group_names = ["Inventory", "Accountant", "Sales"] -# for group_name in group_names: -# group, _ = Group.objects.get_or_create(name=f"{instance.pk}_{group_name}") -# group_manager,_ = CustomGroup.objects.get_or_create(name=group_name, dealer=instance, group=group) -# group_manager.set_default_permissions() -# instance.user.groups.add(group) - -@background def create_settings(pk): instance = Dealer.objects.get(pk=pk) @@ -548,6 +23,539 @@ def create_settings(pk): bill_unearned_account=instance.entity.get_all_accounts().filter(role=roles.LIABILITY_CL_ACC_PAYABLE).first() ) + +@background +def create_coa_accounts(pk): + with transaction.atomic(): + instance = Dealer.objects.select_for_update().get(pk=pk) + entity = instance.entity + coa = entity.get_default_coa() + + # Cash Account + asset_ca_cash = entity.create_account( + coa_model=coa, + code="1101", + role=roles.ASSET_CA_CASH, + name=_("Cash"), + balance_type="debit", + active=True, + ) + asset_ca_cash.role_default = True + asset_ca_cash.save() + + # Accounts Receivable Account + asset_ca_receivables = entity.create_account( + coa_model=coa, + code="1102", + role=roles.ASSET_CA_RECEIVABLES, + name=_("Accounts Receivable"), + balance_type="debit", + active=True, + ) + asset_ca_receivables.role_default = True + asset_ca_receivables.save() + + # Inventory Account + asset_ca_inventory = entity.create_account( + coa_model=coa, + code="1103", + role=roles.ASSET_CA_INVENTORY, + name=_("Inventory"), + balance_type="debit", + active=True, + ) + asset_ca_inventory.role_default = True + asset_ca_inventory.save() + + + # Prepaid Expenses Account + asset_ca_prepaid = entity.create_account( + coa_model=coa, + code="1104", + role=roles.ASSET_CA_PREPAID, + name=_("Prepaid Expenses"), + balance_type="debit", + active=True, + ) + asset_ca_prepaid.role_default = True + asset_ca_prepaid.save() + + # Employee Expenses Account + asset_ca_prepaid_employee = entity.create_account( + coa_model=coa, + code="1105", + role=roles.ASSET_CA_PREPAID, + name=_("Employee Advance"), + balance_type="debit", + active=True, + ) + + + # VAT Payable Account + liability_ltl_vat_receivable = entity.create_account( + coa_model=coa, + code="1106", + role=roles.ASSET_CA_RECEIVABLES, + name=_("VAT Receivable"), + balance_type="debit", + active=True, + ) + + # Buildings Accumulated Depreciation Account + asset_ppe_buildings_accum_depreciation = entity.create_account( + coa_model=coa, + code="1201", + role=roles.ASSET_PPE_BUILDINGS_ACCUM_DEPRECIATION, + name=_("Buildings - Accum. Depreciation"), + balance_type="credit", + active=True, + ) + asset_ppe_buildings_accum_depreciation.role_default = True + asset_ppe_buildings_accum_depreciation.save() + + # intangible Account + asset_lti_land_intangable = entity.create_account( + coa_model=coa, + code="1202", + role=roles.ASSET_INTANGIBLE_ASSETS, + name=_("Intangible Assets"), + balance_type="debit", + active=True, + ) + asset_lti_land_intangable.role_default = True + asset_lti_land_intangable.save() + + # investment property Account + asset_lti_land_investment = entity.create_account( + coa_model=coa, + code="1204", + role=roles.ASSET_LTI_SECURITIES, + name=_("Investments"), + balance_type="debit", + active=True, + ) + asset_lti_land_investment.role_default = True + asset_lti_land_investment.save() + + # # Notes Receivable Account + # asset_lti_notes_receivable = entity.create_account( + # coa_model=coa, + # code="1201", + # role=roles.ASSET_LTI_NOTES_RECEIVABLE, + # name=_("Notes Receivable"), + # balance_type="debit", + # active=True, + # ) + # asset_lti_notes_receivable.role_default = True + # asset_lti_notes_receivable.save() + + # # Land Account + # asset_lti_land = entity.create_account( + # coa_model=coa, + # code="1202", + # role=roles.ASSET_LTI_LAND, + # name=_("Land"), + # balance_type="debit", + # active=True, + # ) + # asset_lti_land.role_default = True + # asset_lti_land.save() + + + # Buildings Account + asset_ppe_buildings = entity.create_account( + coa_model=coa, + code="1301", + role=roles.ASSET_PPE_BUILDINGS, + name=_("Buildings"), + balance_type="debit", + active=True, + ) + asset_ppe_buildings.role_default = True + asset_ppe_buildings.save() + + + + # Accounts Payable Account + liability_cl_acc_payable = entity.create_account( + coa_model=coa, + code="2101", + role=roles.LIABILITY_CL_ACC_PAYABLE, + name=_("Accounts Payable"), + balance_type="credit", + active=True, + ) + liability_cl_acc_payable.role_default = True + liability_cl_acc_payable.save() + + # Deferred Revenue Account + liability_cl_def_rev = entity.create_account( + coa_model=coa, + code="2103", + role=roles.LIABILITY_CL_DEFERRED_REVENUE, + name=_("Deferred Revenue"), + balance_type="credit", + active=True, + ) + liability_cl_def_rev.role_default = True + liability_cl_def_rev.save() + + # Wages Payable Account + liability_cl_wages_payable = entity.create_account( + coa_model=coa, + code="2102", + role=roles.LIABILITY_CL_WAGES_PAYABLE, + name=_("Wages Payable"), + balance_type="credit", + active=True, + ) + liability_cl_wages_payable.role_default = True + liability_cl_wages_payable.save() + + # Long-Term Notes Payable Account + liability_ltl_notes_payable = entity.create_account( + coa_model=coa, + code="2201", + role=roles.LIABILITY_LTL_NOTES_PAYABLE, + name=_("Long-Term Notes Payable"), + balance_type="credit", + active=True, + ) + liability_ltl_notes_payable.role_default = True + liability_ltl_notes_payable.save() + + # VAT Payable Account + liability_ltl_vat_payable = entity.create_account( + coa_model=coa, + code="2106", + role=roles.LIABILITY_CL_OTHER, + name=_("VAT Payable"), + balance_type="credit", + active=True, + ) + + # taxes Payable Account + liability_ltl_taxes_payable = entity.create_account( + coa_model=coa, + code="2107", + role=roles.LIABILITY_CL_OTHER, + name=_("Taxes Payable"), + balance_type="credit", + active=True, + ) + + # social insurance Payable Account + liability_ltl_social_insurance_payable = entity.create_account( + coa_model=coa, + code="2108", + role=roles.LIABILITY_LTL_NOTES_PAYABLE, + name=_("Social Insurance Payable"), + balance_type="credit", + active=True, + ) + + # End of Service Benefits + entity.create_account(coa_model=coa, code="2202", role=roles.LIABILITY_LTL_NOTES_PAYABLE, name=_("End of Service Benefits"), balance_type="credit", active=True) + + # Mortgage Payable Account + liability_ltl_mortgage_payable = entity.create_account( + coa_model=coa, + code="2203", + role=roles.LIABILITY_LTL_MORTGAGE_PAYABLE, + name=_("Mortgage Payable"), + balance_type="credit", + active=True, + ) + liability_ltl_mortgage_payable.role_default = True + liability_ltl_mortgage_payable.save() + + # Capital + equity_capital = entity.create_account(coa_model=coa, code="3101", role=roles.EQUITY_CAPITAL, name=_("Registered Capital"), balance_type="credit", active=True) + equity_capital.role_default = True + equity_capital.save() + entity.create_account(coa_model=coa, code="3102", role=roles.EQUITY_CAPITAL, name=_("Additional Paid-In Capital"), balance_type="credit", active=True) + + # Other Equity + other_equity = entity.create_account(coa_model=coa, code="3201", role=roles.EQUITY_COMMON_STOCK, name=_("Opening Balances"), balance_type="credit", active=True) + other_equity.role_default = True + other_equity.save() + + # Reserves + reserve = entity.create_account(coa_model=coa, code="3301", role=roles.EQUITY_ADJUSTMENT, name=_("Statutory Reserve"), balance_type="credit", active=True) + reserve.role_default = True + reserve.save() + entity.create_account(coa_model=coa, code="3302", role=roles.EQUITY_ADJUSTMENT, name=_("Foreign Currency Translation Reserve"), balance_type="credit", active=True) + + # Retained Earnings Account + equity_retained_earnings = entity.create_account( + coa_model=coa, + code="3401", + role=roles.EQUITY_PREFERRED_STOCK, + name=_("Operating Profits and Losses"), + balance_type="credit", + active=True, + ) + equity_retained_earnings.role_default = True + equity_retained_earnings.save() + + equity_retained_earnings_losses = entity.create_account( + coa_model=coa, + code="3402", + role=roles.EQUITY_PREFERRED_STOCK, + name=_("Retained Earnings (or Losses)"), + balance_type="credit", + active=True, + ) + + # Sales Revenue Account + income_operational = entity.create_account( + coa_model=coa, + code="4101", + role=roles.INCOME_OPERATIONAL, + name=_("Sales Revenue"), + balance_type="credit", + active=True, + ) + income_operational.role_default = True + income_operational.save() + + # Interest Income Account + income_interest = entity.create_account( + coa_model=coa, + code="4102", + role=roles.INCOME_INTEREST, + name=_("Interest Income"), + balance_type="credit", + active=True, + ) + income_interest.role_default = True + income_interest.save() + + # Uneared Income Account + income_unearned = entity.create_account( + coa_model=coa, + code="4103", + role=roles.INCOME_OTHER, + name=_("Unearned Income"), + balance_type="credit", + active=True, + ) + + # Operating Revenues + entity.create_account(coa_model=coa, code="4104", role=roles.INCOME_OPERATIONAL, name=_("Sales/Service Revenue"), balance_type="credit", active=True) + + #Non-Operating Revenues + entity.create_account(coa_model=coa, code="4201", role=roles.INCOME_OTHER, name=_("Non-Operating Revenues"), balance_type="credit", active=True) + + + # Cost of Goods Sold (COGS) Account + expense_cogs = entity.create_account( + coa_model=coa, + code="5101", + role=roles.COGS, + name=_("Cost of Goods Sold"), + balance_type="debit", + active=True, + ) + expense_cogs.role_default = True + expense_cogs.save() + + + # accrued Expenses Account + expense_cogs = entity.create_account( + coa_model=coa, + code="6117", + role=roles.EXPENSE_OPERATIONAL, + name=_("Accrued Expenses"), + balance_type="debit", + active=True, + ) + + # accrued salaries Account + expense_cogs = entity.create_account( + coa_model=coa, + code="6118", + role=roles.EXPENSE_OPERATIONAL, + name=_("Accrued Salaries"), + balance_type="debit", + active=True, + ) + + # Rent Expense Account + expense_rent = entity.create_account( + coa_model=coa, + code="6102", + role=roles.EXPENSE_OPERATIONAL, + name=_("Rent Expense"), + balance_type="debit", + active=True, + ) + # expense_rent.role_default = True + # expense_rent.save() + + # Salaries and Administrative Fees + expense_salaries = entity.create_account( + coa_model=coa, + code="6103", + role=roles.EXPENSE_OPERATIONAL, + name=_("Salaries and Administrative Fees"), + balance_type="debit", + active=True, + ) + + # Medical Insurance + expense_medical_insurance = entity.create_account( + coa_model=coa, + code="6104", + role=roles.EXPENSE_OPERATIONAL, + name=_("Medical Insurance"), + balance_type="debit", + active=True, + ) + + # Marketing and Advertising Expenses + expense_marketing = entity.create_account( + coa_model=coa, + code="6105", + role=roles.EXPENSE_OPERATIONAL, + name=_("Marketing and Advertising Expenses"), + balance_type="debit", + active=True, + ) + + # Commissions and Incentives + expense_commissions = entity.create_account( + coa_model=coa, + code="6106", + role=roles.EXPENSE_OPERATIONAL, + name=_("Commissions and Incentives"), + balance_type="debit", + active=True, + ) + + # Travel Tickets + expense_travel = entity.create_account( + coa_model=coa, + code="6107", + role=roles.EXPENSE_OPERATIONAL, + name=_("Travel Tickets"), + balance_type="debit", + active=True, + ) + + # Social Insurance + expense_other = entity.create_account( + coa_model=coa, + code="6108", + role=roles.EXPENSE_OPERATIONAL, + name=_("Social Insurance"), + balance_type="debit", + active=True, + ) + + # Government Fees + expense_other = entity.create_account( + coa_model=coa, + code="6109", + role=roles.EXPENSE_OPERATIONAL, + name=_("Government Fees"), + balance_type="debit", + active=True, + ) + + # Fees and Subscriptions + expense_other = entity.create_account( + coa_model=coa, + code="6110", + role=roles.EXPENSE_OPERATIONAL, + name=_("Fees and Subscriptions"), + balance_type="debit", + active=True, + ) + + # Office Services Expenses + expense_other = entity.create_account( + coa_model=coa, + code="6111", + role=roles.EXPENSE_OPERATIONAL, + name=_("Office Services Expenses"), + balance_type="debit", + active=True, + ) + + # Office Supplies and Printing + expense_other = entity.create_account( + coa_model=coa, + code="6112", + role=roles.EXPENSE_OPERATIONAL, + name=_("Office Supplies and Printing"), + balance_type="debit", + active=True, + ) + + # Hospitality Expenses + expense_other = entity.create_account( + coa_model=coa, + code="6113", + role=roles.EXPENSE_OPERATIONAL, + name=_("Hospitality Expenses"), + balance_type="debit", + active=True, + ) + + # Bank Commissions + expense_other = entity.create_account( + coa_model=coa, + code="6114", + role=roles.EXPENSE_OPERATIONAL, + name=_("Bank Commissions"), + balance_type="debit", + active=True, + ) + + # Other Expenses + expense_other = entity.create_account( + coa_model=coa, + code="6115", + role=roles.EXPENSE_OPERATIONAL, + name=_("Other Expenses"), + balance_type="debit", + active=True, + ) + + # Transportation Expenses + expense_other = entity.create_account( + coa_model=coa, + code="6116", + role=roles.EXPENSE_OPERATIONAL, + name=_("Transportation Expenses"), + balance_type="debit", + active=True, + ) + + # 5.1 Direct Costs + entity.create_account(coa_model=coa, code="6201", role=roles.EXPENSE_OPERATIONAL, name=_("Cost of Goods Sold"), balance_type="debit", active=True) + entity.create_account(coa_model=coa, code="6202", role=roles.EXPENSE_OPERATIONAL, name=_("Salaries and Wages"), balance_type="debit", active=True) + entity.create_account(coa_model=coa, code="6203", role=roles.EXPENSE_OPERATIONAL, name=_("Sales Commissions"), balance_type="debit", active=True) + entity.create_account(coa_model=coa, code="6204", role=roles.EXPENSE_OPERATIONAL, name=_("Shipping and Customs Clearance"), balance_type="debit", active=True) + + # 5.3 Non-Operating Expenses + entity.create_account(coa_model=coa, code="6301", role=roles.EXPENSE_OTHER, name=_("Zakat"), balance_type="debit", active=True) + entity.create_account(coa_model=coa, code="6302", role=roles.EXPENSE_OTHER, name=_("Taxes"), balance_type="debit", active=True) + entity.create_account(coa_model=coa, code="6303", role=roles.EXPENSE_OTHER, name=_("Foreign Currency Translation"), balance_type="debit", active=True) + entity.create_account(coa_model=coa, code="6304", role=roles.EXPENSE_OTHER, name=_("Interest Expenses"), balance_type="debit", active=True) + + # create_settings(instance.pk) + +# @background +# def create_groups(instance): +# group_names = ["Inventory", "Accountant", "Sales"] +# for group_name in group_names: +# group, _ = Group.objects.get_or_create(name=f"{instance.pk}_{group_name}") +# group_manager,_ = CustomGroup.objects.get_or_create(name=group_name, dealer=instance, group=group) +# group_manager.set_default_permissions() +# instance.user.groups.add(group) + @background def create_accounts_for_make(pk): instance = Dealer.objects.get(pk=pk) @@ -577,4 +585,18 @@ def send_email(from_, to_, subject, message): message = message from_email = from_ recipient_list = [to_] - send_mail(subject, message, from_email, recipient_list) \ No newline at end of file + send_mail(subject, message, from_email, recipient_list) + + +@background +def long_running_task(task_id, *args, **kwargs): + """Example background task""" + print(f"Starting task {task_id} with args: {args}, kwargs: {kwargs}") + + # Simulate work + for i in range(5): + print(f"Task {task_id} progress: {i+1}/5") + + result = f"Task {task_id} completed at {datetime.now()}" + print(result) + return result \ No newline at end of file diff --git a/inventory/urls.py b/inventory/urls.py index 30d64030..487f11e5 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -43,12 +43,16 @@ urlpatterns = [ # template_name="account/request_login_code.html" # ), # ), + # Tasks + + path('tasks/', views.task_list, name='task_list'), + # path('tasks//detail/', views.task_detail, name='task_detail'), # Dashboards # path("user//settings/", views.UserSettingsView.as_view(), name="user_settings"), path("pricing/", views.pricing_page, name="pricing_page"), path("submit_plan/", views.submit_plan, name="submit_plan"), path('payment-callback/', views.payment_callback, name='payment_callback'), - # + # path("dealers//settings/", views.DealerSettingsView, name="dealer_settings"), path("dealers/assign-car-makes/", views.assign_car_makes, name="assign_car_makes"), path("dashboards/manager/", views.ManagerDashboard.as_view(), name="manager_dashboard"), diff --git a/inventory/views.py b/inventory/views.py index 2012916b..fe6b9528 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -14,6 +14,7 @@ from urllib.parse import urlparse, urlunparse ##################################################################### +from background_task.models import Task from django.db.models.deletion import RestrictedError # Django @@ -7702,3 +7703,16 @@ def payment_callback(request): history.save() message = request.GET.get('message') return render(request, "payment_failed.html", {"message": message}) + + +# Background Tasks +def task_list(request): + # Get all tasks ordered by creation time + tasks = Task.objects.all() + + # Add pagination + paginator = Paginator(tasks, 10) # Show 10 tasks per page + page_number = request.GET.get('page') + page_obj = paginator.get_page(page_number) + + return render(request, 'tasks/task_list.html', {'page_obj': page_obj}) \ No newline at end of file diff --git a/templates/tasks/task_list.html b/templates/tasks/task_list.html new file mode 100644 index 00000000..3bbe37c1 --- /dev/null +++ b/templates/tasks/task_list.html @@ -0,0 +1,158 @@ +{% extends "base.html" %} +{% load static i18n %} + +{% block content %} +
+

Background Tasks

+ + +
+
+ Total Tasks: {{ page_obj.paginator.count }} +
+ +
+ + +
+ + + + + + + + + + + + + + + + {% for task in page_obj %} + + + + + + + + + + {% comment %} {% endcomment %} + + {% empty %} + + + + {% endfor %} + +
IDTask NameStatusCreated AtScheduled RunLast RunAttemptsPriorityActions
{{ task.id }} + + {{ task.task_name }} + + + {% if task.locked_at %} + Running + {% elif task.last_run_at and task.has_error %} + Failed + {% elif task.last_run_at %} + Completed + {% else %} + Pending + {% endif %} + {{ task.created_at|date:"Y-m-d H:i" }}{{ task.run_at|date:"Y-m-d H:i" }} + {% if task.last_run_at %} + {{ task.last_run_at|date:"Y-m-d H:i" }} + {% else %} + - + {% endif %} + {{ task.attempts }}{{ task.priority }} +
+ +
+
No tasks found
+
+ + + {% if page_obj.paginator.num_pages > 1 %} + + {% endif %} +
+ + + +{% endblock %} + +{% block extra_js %} + +{% endblock %} \ No newline at end of file