update
This commit is contained in:
commit
9a5339ba3c
BIN
db.sqlite3-shm
Normal file
BIN
db.sqlite3-shm
Normal file
Binary file not shown.
0
db.sqlite3-wal
Normal file
0
db.sqlite3-wal
Normal file
@ -1,7 +1,7 @@
|
|||||||
# Generated by Django 5.1.4 on 2025-01-12 17:20
|
# Generated by Django 5.1.4 on 2025-01-12 17:20
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
@ -9,8 +9,8 @@ class Migration(migrations.Migration):
|
|||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('haikalbot', '0001_initial'),
|
|
||||||
('inventory', '0001_initial'),
|
('inventory', '0001_initial'),
|
||||||
|
('haikalbot', '0001_initial'),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
|||||||
@ -8,6 +8,7 @@ from phonenumber_field.phonenumber import PhoneNumber
|
|||||||
|
|
||||||
from .mixins import AddClassMixin
|
from .mixins import AddClassMixin
|
||||||
from django.forms.models import inlineformset_factory
|
from django.forms.models import inlineformset_factory
|
||||||
|
from django_ledger.forms.invoice import InvoiceModelCreateForm as InvoiceModelCreateFormBase
|
||||||
from .models import (
|
from .models import (
|
||||||
Dealer,
|
Dealer,
|
||||||
# Branch,
|
# Branch,
|
||||||
@ -556,10 +557,22 @@ class PaymentForm(forms.Form):
|
|||||||
label="Payment Method",
|
label="Payment Method",
|
||||||
required=True,
|
required=True,
|
||||||
)
|
)
|
||||||
payment_date = forms.DateField(label="Payment Date", required=True)
|
payment_date = forms.DateField(label="Payment Date",widget=DateInput(attrs={'type': 'date'}), required=True)
|
||||||
|
|
||||||
|
def clean_amount(self):
|
||||||
|
invoice = self.cleaned_data['invoice']
|
||||||
|
amount = self.cleaned_data['amount']
|
||||||
|
if amount < invoice.amount_due:
|
||||||
|
raise forms.ValidationError("Payment amount is greater than invoice amount due")
|
||||||
|
if amount <= 0:
|
||||||
|
raise forms.ValidationError("Payment amount must be greater than 0")
|
||||||
|
if invoice.amount_due == invoice.amount_paid or invoice.invoice_status == "paid":
|
||||||
|
raise forms.ValidationError("Invoice is already paid")
|
||||||
|
if amount > invoice.amount_due:
|
||||||
|
raise forms.ValidationError("Payment amount is greater than invoice amount due")
|
||||||
|
return amount
|
||||||
|
|
||||||
|
|
||||||
from django import forms
|
|
||||||
|
|
||||||
class EmailForm(forms.Form):
|
class EmailForm(forms.Form):
|
||||||
subject = forms.CharField(max_length=255)
|
subject = forms.CharField(max_length=255)
|
||||||
@ -611,4 +624,14 @@ class ActivityForm(forms.ModelForm):
|
|||||||
class OpportunityForm(forms.ModelForm):
|
class OpportunityForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Opportunity
|
model = Opportunity
|
||||||
fields = ['customer', 'car', 'stage', 'probability', 'closing_date']
|
fields = ['customer', 'car', 'stage', 'probability', 'closing_date']
|
||||||
|
|
||||||
|
|
||||||
|
class InvoiceModelCreateForm(InvoiceModelCreateFormBase):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.fields['cash_account'].widget = forms.HiddenInput()
|
||||||
|
self.fields['prepaid_account'].widget = forms.HiddenInput()
|
||||||
|
self.fields['unearned_account'].widget = forms.HiddenInput()
|
||||||
|
self.fields['date_draft'] = forms.DateField(widget=DateInput(attrs={'type': 'date'}))
|
||||||
@ -1,12 +1,12 @@
|
|||||||
# Generated by Django 5.1.4 on 2025-01-12 17:20
|
# Generated by Django 5.1.4 on 2025-01-12 17:20
|
||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import inventory.mixins
|
import inventory.mixins
|
||||||
import inventory.models
|
import inventory.models
|
||||||
import phonenumber_field.modelfields
|
import phonenumber_field.modelfields
|
||||||
from decimal import Decimal
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
@ -16,10 +16,26 @@ class Migration(migrations.Migration):
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
('contenttypes', '0002_remove_content_type_name'),
|
('contenttypes', '0002_remove_content_type_name'),
|
||||||
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
|
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='AdditionalServices',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('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(decimal_places=2, max_digits=14, verbose_name='Price')),
|
||||||
|
('taxable', models.BooleanField(default=False, verbose_name='taxable')),
|
||||||
|
('uom', models.CharField(choices=[('EA', 'Each'), ('PR', 'Pair'), ('SET', 'Set'), ('GAL', 'Gallon'), ('L', 'Liter'), ('M', 'Meter'), ('KG', 'Kilogram'), ('HR', 'Hour'), ('BX', 'Box'), ('RL', 'Roll'), ('PKG', 'Package'), ('DZ', 'Dozen'), ('SQ_M', 'Square Meter'), ('PC', 'Piece'), ('BDL', 'Bundle')], max_length=10, verbose_name='Unit of Measurement')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Additional Services',
|
||||||
|
'verbose_name_plural': 'Additional Services',
|
||||||
|
},
|
||||||
|
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||||
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Car',
|
name='Car',
|
||||||
fields=[
|
fields=[
|
||||||
@ -203,11 +219,6 @@ class Migration(migrations.Migration):
|
|||||||
},
|
},
|
||||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
|
||||||
model_name='car',
|
|
||||||
name='id_car_model',
|
|
||||||
field=models.ForeignKey(blank=True, db_column='id_car_model', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel', verbose_name='Model'),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CarOption',
|
name='CarOption',
|
||||||
fields=[
|
fields=[
|
||||||
@ -221,36 +232,6 @@ class Migration(migrations.Migration):
|
|||||||
},
|
},
|
||||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
|
||||||
name='CarOptionValue',
|
|
||||||
fields=[
|
|
||||||
('id_car_option_value', models.AutoField(primary_key=True, serialize=False)),
|
|
||||||
('value', models.CharField(max_length=500)),
|
|
||||||
('unit', models.CharField(blank=True, max_length=255, null=True)),
|
|
||||||
('is_base', models.IntegerField()),
|
|
||||||
('id_car_equipment', models.ForeignKey(db_column='id_car_equipment', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carequipment')),
|
|
||||||
('id_car_option', models.ForeignKey(db_column='id_car_option', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.caroption')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Option Value',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='CarRegistration',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('plate_number', models.IntegerField(verbose_name='Plate Number')),
|
|
||||||
('text1', models.CharField(max_length=1, verbose_name='Text 1')),
|
|
||||||
('text2', models.CharField(max_length=1, verbose_name='Text 2')),
|
|
||||||
('text3', models.CharField(max_length=1, verbose_name='Text 3')),
|
|
||||||
('registration_date', models.DateTimeField(verbose_name='Registration Date')),
|
|
||||||
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='registrations', to='inventory.car', verbose_name='Car')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Registration',
|
|
||||||
'verbose_name_plural': 'Registrations',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CarSerie',
|
name='CarSerie',
|
||||||
fields=[
|
fields=[
|
||||||
@ -267,11 +248,6 @@ class Migration(migrations.Migration):
|
|||||||
},
|
},
|
||||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
|
||||||
model_name='car',
|
|
||||||
name='id_car_serie',
|
|
||||||
field=models.ForeignKey(blank=True, db_column='id_car_serie', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carserie', verbose_name='Series'),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CarSpecification',
|
name='CarSpecification',
|
||||||
fields=[
|
fields=[
|
||||||
@ -286,54 +262,26 @@ class Migration(migrations.Migration):
|
|||||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CarTrim',
|
name='Customer',
|
||||||
fields=[
|
|
||||||
('id_car_trim', models.AutoField(primary_key=True, serialize=False)),
|
|
||||||
('name', models.CharField(blank=True, max_length=255, null=True)),
|
|
||||||
('arabic_name', models.CharField(blank=True, max_length=255, null=True)),
|
|
||||||
('start_production_year', models.IntegerField(blank=True, null=True)),
|
|
||||||
('end_production_year', models.IntegerField(blank=True, null=True)),
|
|
||||||
('id_car_serie', models.ForeignKey(db_column='id_car_serie', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carserie')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Trim',
|
|
||||||
},
|
|
||||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='CarSpecificationValue',
|
|
||||||
fields=[
|
|
||||||
('id_car_specification_value', models.AutoField(primary_key=True, serialize=False)),
|
|
||||||
('value', models.CharField(max_length=500)),
|
|
||||||
('unit', models.CharField(blank=True, max_length=255, null=True)),
|
|
||||||
('id_car_specification', models.ForeignKey(db_column='id_car_specification', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carspecification')),
|
|
||||||
('id_car_trim', models.ForeignKey(db_column='id_car_trim', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.cartrim')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Specification Value',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='carequipment',
|
|
||||||
name='id_car_trim',
|
|
||||||
field=models.ForeignKey(db_column='id_car_trim', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.cartrim'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='car',
|
|
||||||
name='id_car_trim',
|
|
||||||
field=models.ForeignKey(blank=True, db_column='id_car_trim', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.cartrim', verbose_name='Trim'),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='CustomCard',
|
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('custom_number', models.CharField(max_length=255, verbose_name='Custom Number')),
|
('title', models.CharField(choices=[('mr', 'Mr'), ('mrs', 'Mrs'), ('ms', 'Ms'), ('miss', 'Miss'), ('dr', 'Dr'), ('prof', 'Prof'), ('prince', 'Prince'), ('princess', 'Princess'), ('company', 'Company'), ('na', 'N/A')], default='na', max_length=10, verbose_name='Title')),
|
||||||
('custom_date', models.DateField(verbose_name='Custom Date')),
|
('first_name', models.CharField(max_length=50, verbose_name='First Name')),
|
||||||
('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='custom_cards', to='inventory.car', verbose_name='Car')),
|
('middle_name', models.CharField(blank=True, max_length=50, null=True, verbose_name='Middle Name')),
|
||||||
|
('last_name', models.CharField(max_length=50, verbose_name='Last Name')),
|
||||||
|
('gender', models.CharField(choices=[('m', 'Male'), ('f', 'Female')], max_length=1, verbose_name='Gender')),
|
||||||
|
('dob', models.DateField(verbose_name='Date of Birth')),
|
||||||
|
('email', models.EmailField(max_length=254, unique=True, verbose_name='Email')),
|
||||||
|
('national_id', models.CharField(max_length=10, unique=True, verbose_name='National ID')),
|
||||||
|
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', unique=True, verbose_name='Phone Number')),
|
||||||
|
('city', models.CharField(blank=True, max_length=255, verbose_name='City')),
|
||||||
|
('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
|
||||||
|
('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Custom Card',
|
'verbose_name': 'Customer',
|
||||||
'verbose_name_plural': 'Custom Cards',
|
'verbose_name_plural': 'Customers',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
@ -493,38 +441,19 @@ class Migration(migrations.Migration):
|
|||||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Refund',
|
name='Payment',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='amount')),
|
('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='amount')),
|
||||||
('reason', models.TextField(blank=True, verbose_name='reason')),
|
('payment_method', models.CharField(choices=[('cash', 'cash'), ('credit', 'credit'), ('transfer', 'transfer'), ('debit', 'debit'), ('SADAD', 'SADAD')], max_length=50, verbose_name='method')),
|
||||||
('refund_date', models.DateField(auto_now_add=True, verbose_name='refund date')),
|
('reference_number', models.CharField(blank=True, max_length=100, null=True, verbose_name='reference number')),
|
||||||
('payment', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='refund', to='inventory.payment')),
|
('payment_date', models.DateField(auto_now_add=True, verbose_name='date')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'refund',
|
'verbose_name': 'payment',
|
||||||
'verbose_name_plural': 'refunds',
|
'verbose_name_plural': 'payments',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
|
||||||
name='Representative',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('name', models.CharField(max_length=255, verbose_name='Name')),
|
|
||||||
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
|
|
||||||
('id_number', models.CharField(max_length=10, verbose_name='ID Number')),
|
|
||||||
('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')),
|
|
||||||
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='representatives', to='inventory.dealer')),
|
|
||||||
('organization', models.ManyToManyField(related_name='representatives', to='inventory.organization')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Representative',
|
|
||||||
'verbose_name_plural': 'Representatives',
|
|
||||||
},
|
|
||||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='SaleQuotation',
|
name='SaleQuotation',
|
||||||
fields=[
|
fields=[
|
||||||
@ -651,7 +580,6 @@ class Migration(migrations.Migration):
|
|||||||
('billing_cycle', models.CharField(choices=[('monthly', 'Monthly'), ('annual', 'Annual')], default='monthly', help_text='Billing cycle for the subscription', max_length=10)),
|
('billing_cycle', models.CharField(choices=[('monthly', 'Monthly'), ('annual', 'Annual')], default='monthly', help_text='Billing cycle for the subscription', max_length=10)),
|
||||||
('last_payment_date', models.DateField(blank=True, help_text='Date of the last payment made', null=True)),
|
('last_payment_date', models.DateField(blank=True, help_text='Date of the last payment made', null=True)),
|
||||||
('next_payment_date', models.DateField(blank=True, help_text='Date of the next payment due', null=True)),
|
('next_payment_date', models.DateField(blank=True, help_text='Date of the next payment due', null=True)),
|
||||||
('plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subscriptions', to='inventory.subscriptionplan')),
|
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Subscription',
|
'verbose_name': 'Subscription',
|
||||||
@ -659,35 +587,32 @@ class Migration(migrations.Migration):
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='SubscriptionUser',
|
name='SubscriptionPlan',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('subscription', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.subscription')),
|
('name', models.CharField(help_text='Name of the subscription plan', max_length=100, unique=True)),
|
||||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
('description', models.TextField()),
|
||||||
|
('price', models.DecimalField(decimal_places=2, max_digits=10)),
|
||||||
|
('max_users', models.PositiveIntegerField(default=1, help_text='Maximum number of users allowed')),
|
||||||
|
('max_inventory_size', models.PositiveIntegerField(default=50, help_text='Maximum number of cars in inventory')),
|
||||||
|
('support_level', models.CharField(choices=[('basic', 'Basic Support'), ('priority', 'Priority Support'), ('dedicated', 'Dedicated Support')], default='basic', help_text='Level of support provided', max_length=50)),
|
||||||
|
('custom_features', models.JSONField(blank=True, help_text='Additional features specific to this plan', null=True)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Subscription User',
|
'verbose_name': 'Subscription Plan',
|
||||||
'verbose_name_plural': 'Subscription Users',
|
'verbose_name_plural': 'Subscription Plans',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
|
||||||
model_name='subscription',
|
|
||||||
name='users',
|
|
||||||
field=models.ManyToManyField(through='inventory.SubscriptionUser', to=settings.AUTH_USER_MODEL),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='UserActivityLog',
|
name='VatRate',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('action', models.TextField()),
|
('rate', models.DecimalField(decimal_places=2, default=Decimal('0.15'), max_digits=5)),
|
||||||
('timestamp', models.DateTimeField(auto_now_add=True)),
|
('is_active', models.BooleanField(default=True)),
|
||||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
],
|
],
|
||||||
options={
|
|
||||||
'verbose_name': 'User Activity Log',
|
|
||||||
'verbose_name_plural': 'User Activity Logs',
|
|
||||||
'ordering': ['-timestamp'],
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Vendor',
|
name='Vendor',
|
||||||
@ -711,11 +636,387 @@ class Migration(migrations.Migration):
|
|||||||
},
|
},
|
||||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||||
),
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserActivityLog',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('action', models.TextField()),
|
||||||
|
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'User Activity Log',
|
||||||
|
'verbose_name_plural': 'User Activity Logs',
|
||||||
|
'ordering': ['-timestamp'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SubscriptionUser',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('subscription', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.subscription')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Subscription User',
|
||||||
|
'verbose_name_plural': 'Subscription Users',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='subscription',
|
||||||
|
name='plan',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subscriptions', to='inventory.subscriptionplan'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='subscription',
|
||||||
|
name='users',
|
||||||
|
field=models.ManyToManyField(through='inventory.SubscriptionUser', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Staff',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=255, verbose_name='Name')),
|
||||||
|
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
|
||||||
|
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')),
|
||||||
|
('staff_type', models.CharField(choices=[('manager', 'Manager'), ('inventory', 'Inventory'), ('accountant', 'Accountant'), ('sales', 'Sales'), ('coordinator', 'Coordinator'), ('receptionist', 'Receptionist'), ('agent', 'Agent')], max_length=255, verbose_name='Staff Type')),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
|
||||||
|
('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')),
|
||||||
|
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='staff', to='inventory.dealer')),
|
||||||
|
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='staff', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Staff',
|
||||||
|
'verbose_name_plural': 'Staff',
|
||||||
|
'permissions': [],
|
||||||
|
},
|
||||||
|
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||||
|
managers=[
|
||||||
|
('objects', inventory.models.StaffUserManager()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SalesOrder',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')),
|
||||||
|
('total_amount', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Total Amount')),
|
||||||
|
('quotation', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='sales_order', to='inventory.salequotation', verbose_name='Quotation')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SaleQuotationCar',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('quantity', models.PositiveIntegerField(default=1, verbose_name='Quantity')),
|
||||||
|
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.car', verbose_name='Car')),
|
||||||
|
('quotation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='quotation_cars', to='inventory.salequotation', verbose_name='Quotation')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Representative',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=255, verbose_name='Name')),
|
||||||
|
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
|
||||||
|
('id_number', models.CharField(max_length=10, verbose_name='ID Number')),
|
||||||
|
('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')),
|
||||||
|
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='representatives', to='inventory.dealer')),
|
||||||
|
('organization', models.ManyToManyField(related_name='representatives', to='inventory.organization')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Representative',
|
||||||
|
'verbose_name_plural': 'Representatives',
|
||||||
|
},
|
||||||
|
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Refund',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='amount')),
|
||||||
|
('reason', models.TextField(blank=True, verbose_name='reason')),
|
||||||
|
('refund_date', models.DateField(auto_now_add=True, verbose_name='refund date')),
|
||||||
|
('payment', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='refund', to='inventory.payment')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'refund',
|
||||||
|
'verbose_name_plural': 'refunds',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='payment',
|
||||||
|
name='quotation',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='inventory.salequotation'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Opportunity',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('stage', models.CharField(choices=[('prospect', 'Prospect'), ('proposal', 'Proposal'), ('negotiation', 'Negotiation'), ('closed_won', 'Closed Won'), ('closed_lost', 'Closed Lost')], max_length=20, verbose_name='Stage')),
|
||||||
|
('status', models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('canceled', 'Canceled')], default='new', max_length=20, verbose_name='Status')),
|
||||||
|
('probability', models.PositiveIntegerField(validators=[inventory.models.validate_probability])),
|
||||||
|
('closing_date', models.DateField(verbose_name='Closing Date')),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
|
||||||
|
('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')),
|
||||||
|
('closed', models.BooleanField(default=False, verbose_name='Closed')),
|
||||||
|
('car', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='inventory.car', verbose_name='Car')),
|
||||||
|
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='opportunities', to='inventory.customer')),
|
||||||
|
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='opportunities', to='inventory.dealer')),
|
||||||
|
('staff', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='owner', to='inventory.staff', verbose_name='Owner')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Opportunity',
|
||||||
|
'verbose_name_plural': 'Opportunities',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Notification',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('message', models.CharField(max_length=255, verbose_name='Message')),
|
||||||
|
('is_read', models.BooleanField(default=False, verbose_name='Is Read')),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Notification',
|
||||||
|
'verbose_name_plural': 'Notifications',
|
||||||
|
'ordering': ['-created'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Notes',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('object_id', models.PositiveIntegerField()),
|
||||||
|
('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')),
|
||||||
|
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
|
||||||
|
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='notes_created', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Note',
|
||||||
|
'verbose_name_plural': 'Notes',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='LeadStatusHistory',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('old_status', models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('canceled', 'Canceled')], max_length=50, verbose_name='Old Status')),
|
||||||
|
('new_status', models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('canceled', 'Canceled')], max_length=50, verbose_name='New Status')),
|
||||||
|
('changed_at', models.DateTimeField(auto_now_add=True, verbose_name='Changed At')),
|
||||||
|
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='status_changes', to='inventory.staff')),
|
||||||
|
('lead', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='status_history', to='inventory.lead')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Lead Status History',
|
||||||
|
'verbose_name_plural': 'Lead Status Histories',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='lead',
|
||||||
|
name='assigned',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned', to='inventory.staff', verbose_name='Assigned'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='lead',
|
||||||
|
name='dealer',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='leads', to='inventory.dealer'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='lead',
|
||||||
|
name='id_car_make',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake', verbose_name='Make'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='lead',
|
||||||
|
name='id_car_model',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel', verbose_name='Model'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customer',
|
||||||
|
name='dealer',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='customers', to='inventory.dealer'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customer',
|
||||||
|
name='lead',
|
||||||
|
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='converted', to='inventory.lead', verbose_name='Lead'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customer',
|
||||||
|
name='staff',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='customer_staff', to='inventory.staff', verbose_name='Staff'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CustomCard',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('custom_number', models.CharField(max_length=255, verbose_name='Custom Number')),
|
||||||
|
('custom_date', models.DateField(verbose_name='Custom Date')),
|
||||||
|
('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='custom_cards', to='inventory.car', verbose_name='Car')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Custom Card',
|
||||||
|
'verbose_name_plural': 'Custom Cards',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CarTrim',
|
||||||
|
fields=[
|
||||||
|
('id_car_trim', models.AutoField(primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('arabic_name', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('start_production_year', models.IntegerField(blank=True, null=True)),
|
||||||
|
('end_production_year', models.IntegerField(blank=True, null=True)),
|
||||||
|
('id_car_serie', models.ForeignKey(db_column='id_car_serie', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carserie')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Trim',
|
||||||
|
},
|
||||||
|
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CarSpecificationValue',
|
||||||
|
fields=[
|
||||||
|
('id_car_specification_value', models.AutoField(primary_key=True, serialize=False)),
|
||||||
|
('value', models.CharField(max_length=500)),
|
||||||
|
('unit', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('id_car_specification', models.ForeignKey(db_column='id_car_specification', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carspecification')),
|
||||||
|
('id_car_trim', models.ForeignKey(db_column='id_car_trim', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.cartrim')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Specification Value',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CarRegistration',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('plate_number', models.IntegerField(verbose_name='Plate Number')),
|
||||||
|
('text1', models.CharField(max_length=1, verbose_name='Text 1')),
|
||||||
|
('text2', models.CharField(max_length=1, verbose_name='Text 2')),
|
||||||
|
('text3', models.CharField(max_length=1, verbose_name='Text 3')),
|
||||||
|
('registration_date', models.DateTimeField(verbose_name='Registration Date')),
|
||||||
|
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='registrations', to='inventory.car', verbose_name='Car')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Registration',
|
||||||
|
'verbose_name_plural': 'Registrations',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CarOptionValue',
|
||||||
|
fields=[
|
||||||
|
('id_car_option_value', models.AutoField(primary_key=True, serialize=False)),
|
||||||
|
('value', models.CharField(max_length=500)),
|
||||||
|
('unit', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('is_base', models.IntegerField()),
|
||||||
|
('id_car_equipment', models.ForeignKey(db_column='id_car_equipment', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carequipment')),
|
||||||
|
('id_car_option', models.ForeignKey(db_column='id_car_option', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.caroption')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Option Value',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CarLocation',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('description', models.TextField(blank=True, help_text='Optional description about the showroom placement.', null=True, verbose_name='Description')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Last Updated')),
|
||||||
|
('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='location', to='inventory.car', verbose_name='Car')),
|
||||||
|
('owner', models.ForeignKey(help_text='Dealer who owns the car.', on_delete=django.db.models.deletion.CASCADE, related_name='owned_cars', to='inventory.dealer', verbose_name='Owner')),
|
||||||
|
('showroom', models.ForeignKey(help_text='Dealer where the car is displayed (can be the owner).', on_delete=django.db.models.deletion.CASCADE, related_name='showroom_cars', to='inventory.dealer', verbose_name='Showroom')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Car Location',
|
||||||
|
'verbose_name_plural': 'Car Locations',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CarFinance',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('cost_price', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Cost Price')),
|
||||||
|
('selling_price', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Selling Price')),
|
||||||
|
('discount_amount', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Discount Amount')),
|
||||||
|
('additional_services', models.ManyToManyField(blank=True, related_name='additional_finances', to='inventory.additionalservices')),
|
||||||
|
('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='finances', to='inventory.car')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Car Financial Details',
|
||||||
|
'verbose_name_plural': 'Car Financial Details',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='carequipment',
|
||||||
|
name='id_car_trim',
|
||||||
|
field=models.ForeignKey(db_column='id_car_trim', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.cartrim'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='car',
|
||||||
|
name='dealer',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='cars', to='inventory.dealer', verbose_name='Dealer'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='car',
|
||||||
|
name='id_car_make',
|
||||||
|
field=models.ForeignKey(blank=True, db_column='id_car_make', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake', verbose_name='Make'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='car',
|
||||||
|
name='id_car_model',
|
||||||
|
field=models.ForeignKey(blank=True, db_column='id_car_model', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel', verbose_name='Model'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='car',
|
||||||
|
name='id_car_serie',
|
||||||
|
field=models.ForeignKey(blank=True, db_column='id_car_serie', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carserie', verbose_name='Series'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='car',
|
||||||
|
name='id_car_trim',
|
||||||
|
field=models.ForeignKey(blank=True, db_column='id_car_trim', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.cartrim', verbose_name='Trim'),
|
||||||
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='car',
|
model_name='car',
|
||||||
name='vendor',
|
name='vendor',
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='cars', to='inventory.vendor', verbose_name='Vendor'),
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='cars', to='inventory.vendor', verbose_name='Vendor'),
|
||||||
),
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='additionalservices',
|
||||||
|
name='dealer',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.dealer', verbose_name='Dealer'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='additionalservices',
|
||||||
|
name='item',
|
||||||
|
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='django_ledger.itemmodel', verbose_name='Item'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Activity',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('object_id', models.PositiveIntegerField()),
|
||||||
|
('activity_type', models.CharField(choices=[('call', 'Call'), ('sms', 'SMS'), ('email', 'Email'), ('whatsapp', 'WhatsApp'), ('visit', 'Visit'), ('add_car', 'Add Car'), ('reserve_car', 'Reserve Car'), ('remove_car', 'Remove Car'), ('create_quotation', 'Create Quotation'), ('cancel_quotation', 'Cancel Quotation'), ('create_order', 'Create Order'), ('cancel_order', 'Cancel Order'), ('create_invoice', 'Create Invoice'), ('cancel_invoice', 'Cancel Invoice')], max_length=50, verbose_name='Activity Type')),
|
||||||
|
('notes', models.TextField(blank=True, null=True, verbose_name='Notes')),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
|
||||||
|
('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')),
|
||||||
|
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
|
||||||
|
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='activities_created', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Activity',
|
||||||
|
'verbose_name_plural': 'Activities',
|
||||||
|
},
|
||||||
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CarReservation',
|
name='CarReservation',
|
||||||
fields=[
|
fields=[
|
||||||
|
|||||||
18
inventory/migrations/0002_alter_carmake_car_type.py
Normal file
18
inventory/migrations/0002_alter_carmake_car_type.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.17 on 2025-01-13 10:20
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='carmake',
|
||||||
|
name='car_type',
|
||||||
|
field=models.SmallIntegerField(blank=True, choices=[], null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
inventory/migrations/0003_alter_carmake_car_type.py
Normal file
18
inventory/migrations/0003_alter_carmake_car_type.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.17 on 2025-01-14 12:10
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0002_alter_carmake_car_type'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='carmake',
|
||||||
|
name='car_type',
|
||||||
|
field=models.SmallIntegerField(blank=True, choices=[(1, 'Car'), (2, 'Light Commercial'), (3, 'Heavy-Duty Tractors'), (4, 'Trailers'), (5, 'Medium Trucks'), (6, 'Buses'), (20, 'Motorcycles'), (21, 'Buggy'), (22, 'Moto ATV'), (23, 'Scooters'), (24, 'Karting'), (25, 'ATV'), (26, 'Snowmobiles')], null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -47,8 +47,7 @@ class StaffUserManager(UserManager):
|
|||||||
user = self.create_user(username=email, email=email, password=password, **extra_fields)
|
user = self.create_user(username=email, email=email, password=password, **extra_fields)
|
||||||
Staff.objects.create(user=user, name=name, arabic_name=arabic_name, phone_number=phone_number, staff_type=staff_type, **extra_fields)
|
Staff.objects.create(user=user, name=name, arabic_name=arabic_name, phone_number=phone_number, staff_type=staff_type, **extra_fields)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
class UnitOfMeasure(models.TextChoices):
|
class UnitOfMeasure(models.TextChoices):
|
||||||
EACH = 'EA', 'Each'
|
EACH = 'EA', 'Each'
|
||||||
PAIR = 'PR', 'Pair'
|
PAIR = 'PR', 'Pair'
|
||||||
@ -98,7 +97,7 @@ class CarMake(models.Model, LocalizedNameMixin):
|
|||||||
arabic_name = models.CharField(max_length=255, blank=True, null=True)
|
arabic_name = models.CharField(max_length=255, blank=True, null=True)
|
||||||
logo = models.ImageField(_("logo"), upload_to="car_make", blank=True, null=True)
|
logo = models.ImageField(_("logo"), upload_to="car_make", blank=True, null=True)
|
||||||
is_sa_import = models.BooleanField(default=False)
|
is_sa_import = models.BooleanField(default=False)
|
||||||
car_type = models.SmallIntegerField(choices=CarType.choices)
|
car_type = models.SmallIntegerField(choices=CarType.choices, blank=True, null=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|||||||
@ -132,6 +132,7 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
|||||||
asset_ca_inventory.role_default = True
|
asset_ca_inventory.role_default = True
|
||||||
asset_ca_inventory.save()
|
asset_ca_inventory.save()
|
||||||
|
|
||||||
|
|
||||||
# Prepaid Expenses Account
|
# Prepaid Expenses Account
|
||||||
asset_ca_prepaid = entity.create_account(
|
asset_ca_prepaid = entity.create_account(
|
||||||
coa_model=coa,
|
coa_model=coa,
|
||||||
@ -143,31 +144,100 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
|||||||
)
|
)
|
||||||
asset_ca_prepaid.role_default = True
|
asset_ca_prepaid.role_default = True
|
||||||
asset_ca_prepaid.save()
|
asset_ca_prepaid.save()
|
||||||
|
|
||||||
# Notes Receivable Account
|
# Employee Expenses Account
|
||||||
asset_lti_notes_receivable = entity.create_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,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Inventory Account
|
||||||
|
asset_ca_inventory = entity.create_account(
|
||||||
|
coa_model=coa,
|
||||||
|
code="1106",
|
||||||
|
role=roles.ASSET_CA_INVENTORY,
|
||||||
|
name=_("Inventory"),
|
||||||
|
balance_type="debit",
|
||||||
|
active=True,
|
||||||
|
)
|
||||||
|
asset_ca_inventory.role_default = True
|
||||||
|
asset_ca_inventory.save()
|
||||||
|
|
||||||
|
# VAT Payable Account
|
||||||
|
liability_ltl_vat_receivable = entity.create_account(
|
||||||
|
coa_model=coa,
|
||||||
|
code="1107",
|
||||||
|
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,
|
coa_model=coa,
|
||||||
code="1201",
|
code="1201",
|
||||||
role=roles.ASSET_LTI_NOTES_RECEIVABLE,
|
role=roles.ASSET_PPE_BUILDINGS_ACCUM_DEPRECIATION,
|
||||||
name=_("Notes Receivable"),
|
name=_("Buildings - Accum. Depreciation"),
|
||||||
balance_type="debit",
|
balance_type="credit",
|
||||||
active=True,
|
active=True,
|
||||||
)
|
)
|
||||||
asset_lti_notes_receivable.role_default = True
|
asset_ppe_buildings_accum_depreciation.role_default = True
|
||||||
asset_lti_notes_receivable.save()
|
asset_ppe_buildings_accum_depreciation.save()
|
||||||
|
|
||||||
# Land Account
|
# intangible Account
|
||||||
asset_lti_land = entity.create_account(
|
asset_lti_land_intangable = entity.create_account(
|
||||||
coa_model=coa,
|
coa_model=coa,
|
||||||
code="1202",
|
code="1202",
|
||||||
role=roles.ASSET_LTI_LAND,
|
role=roles.ASSET_INTANGIBLE_ASSETS,
|
||||||
name=_("Land"),
|
name=_("Intangible Assets"),
|
||||||
balance_type="debit",
|
balance_type="debit",
|
||||||
active=True,
|
active=True,
|
||||||
)
|
)
|
||||||
asset_lti_land.role_default = True
|
asset_lti_land_intangable.role_default = True
|
||||||
asset_lti_land.save()
|
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
|
# Buildings Account
|
||||||
asset_ppe_buildings = entity.create_account(
|
asset_ppe_buildings = entity.create_account(
|
||||||
coa_model=coa,
|
coa_model=coa,
|
||||||
@ -180,17 +250,7 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
|||||||
asset_ppe_buildings.role_default = True
|
asset_ppe_buildings.role_default = True
|
||||||
asset_ppe_buildings.save()
|
asset_ppe_buildings.save()
|
||||||
|
|
||||||
# Buildings Accumulated Depreciation Account
|
|
||||||
asset_ppe_buildings_accum_depreciation = entity.create_account(
|
|
||||||
coa_model=coa,
|
|
||||||
code="1302",
|
|
||||||
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()
|
|
||||||
|
|
||||||
# Accounts Payable Account
|
# Accounts Payable Account
|
||||||
liability_cl_acc_payable = entity.create_account(
|
liability_cl_acc_payable = entity.create_account(
|
||||||
@ -239,7 +299,40 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
|||||||
)
|
)
|
||||||
liability_ltl_notes_payable.role_default = True
|
liability_ltl_notes_payable.role_default = True
|
||||||
liability_ltl_notes_payable.save()
|
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
|
# Mortgage Payable Account
|
||||||
liability_ltl_mortgage_payable = entity.create_account(
|
liability_ltl_mortgage_payable = entity.create_account(
|
||||||
coa_model=coa,
|
coa_model=coa,
|
||||||
@ -252,29 +345,43 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
|||||||
liability_ltl_mortgage_payable.role_default = True
|
liability_ltl_mortgage_payable.role_default = True
|
||||||
liability_ltl_mortgage_payable.save()
|
liability_ltl_mortgage_payable.save()
|
||||||
|
|
||||||
# Common Stock Account
|
# Capital
|
||||||
equity_common_stock = entity.create_account(
|
equity_capital = entity.create_account(coa_model=coa, code="3101", role=roles.EQUITY_CAPITAL, name=_("Registered Capital"), balance_type="credit", active=True)
|
||||||
coa_model=coa,
|
equity_capital.role_default = True
|
||||||
code="3101",
|
equity_capital.save()
|
||||||
role=roles.EQUITY_COMMON_STOCK,
|
entity.create_account(coa_model=coa, code="3102", role=roles.EQUITY_CAPITAL, name=_("Additional Paid-In Capital"), balance_type="credit", active=True)
|
||||||
name=_("Common Stock"),
|
|
||||||
balance_type="credit",
|
# Other Equity
|
||||||
active=True,
|
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
|
||||||
equity_common_stock.role_default = True
|
other_equity.save()
|
||||||
equity_common_stock.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
|
# Retained Earnings Account
|
||||||
equity_retained_earnings = entity.create_account(
|
equity_retained_earnings = entity.create_account(
|
||||||
coa_model=coa,
|
coa_model=coa,
|
||||||
code="3102",
|
code="3401",
|
||||||
role=roles.EQUITY_ADJUSTMENT,
|
role=roles.EQUITY_PREFERRED_STOCK,
|
||||||
name=_("Retained Earnings"),
|
name=_("Operating Profits and Losses"),
|
||||||
balance_type="credit",
|
balance_type="credit",
|
||||||
active=True,
|
active=True,
|
||||||
)
|
)
|
||||||
equity_retained_earnings.role_default = True
|
equity_retained_earnings.role_default = True
|
||||||
equity_retained_earnings.save()
|
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
|
# Sales Revenue Account
|
||||||
income_operational = entity.create_account(
|
income_operational = entity.create_account(
|
||||||
@ -300,6 +407,23 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
|||||||
income_interest.role_default = True
|
income_interest.role_default = True
|
||||||
income_interest.save()
|
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
|
# Cost of Goods Sold (COGS) Account
|
||||||
expense_cogs = entity.create_account(
|
expense_cogs = entity.create_account(
|
||||||
coa_model=coa,
|
coa_model=coa,
|
||||||
@ -311,7 +435,28 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
|||||||
)
|
)
|
||||||
expense_cogs.role_default = True
|
expense_cogs.role_default = True
|
||||||
expense_cogs.save()
|
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
|
# Rent Expense Account
|
||||||
expense_rent = entity.create_account(
|
expense_rent = entity.create_account(
|
||||||
coa_model=coa,
|
coa_model=coa,
|
||||||
@ -323,7 +468,161 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
|||||||
)
|
)
|
||||||
expense_rent.role_default = True
|
expense_rent.role_default = True
|
||||||
expense_rent.save()
|
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 Vendor
|
# Create Vendor
|
||||||
@receiver(post_save, sender=models.Vendor)
|
@receiver(post_save, sender=models.Vendor)
|
||||||
def create_ledger_vendor(sender, instance, created, **kwargs):
|
def create_ledger_vendor(sender, instance, created, **kwargs):
|
||||||
|
|||||||
@ -164,6 +164,7 @@ urlpatterns = [
|
|||||||
path('sales/payments/<uuid:pk>/create/', views.PaymentCreateView, name='payment_create'),
|
path('sales/payments/<uuid:pk>/create/', views.PaymentCreateView, name='payment_create'),
|
||||||
path('sales/payments/create/', views.PaymentCreateView, name='payment_create'),
|
path('sales/payments/create/', views.PaymentCreateView, name='payment_create'),
|
||||||
path('sales/payments/<uuid:pk>/payment_details/', views.PaymentDetailView, name='payment_details'),
|
path('sales/payments/<uuid:pk>/payment_details/', views.PaymentDetailView, name='payment_details'),
|
||||||
|
path('sales/payments/<uuid:pk>/payment_mark_as_paid/', views.payment_mark_as_paid, name='payment_mark_as_paid'),
|
||||||
# path('sales/payments/<uuid:pk>/update/', views.JournalEntryUpdateView.as_view(), name='payment_update'),
|
# path('sales/payments/<uuid:pk>/update/', views.JournalEntryUpdateView.as_view(), name='payment_update'),
|
||||||
# path('sales/payments/<uuid:pk>/delete/', views.JournalEntryDeleteView.as_view(), name='payment_delete'),
|
# path('sales/payments/<uuid:pk>/delete/', views.JournalEntryDeleteView.as_view(), name='payment_delete'),
|
||||||
# path('sales/payments/<uuid:pk>/preview/', views.JournalEntryPreviewView.as_view(), name='payment_preview'),
|
# path('sales/payments/<uuid:pk>/preview/', views.JournalEntryPreviewView.as_view(), name='payment_preview'),
|
||||||
@ -173,6 +174,7 @@ urlpatterns = [
|
|||||||
# Items
|
# Items
|
||||||
path('items/services/', views.ItemServiceListView.as_view(), name='item_service_list'),
|
path('items/services/', views.ItemServiceListView.as_view(), name='item_service_list'),
|
||||||
path('items/services/create/', views.ItemServiceCreateView.as_view(), name='item_service_create'),
|
path('items/services/create/', views.ItemServiceCreateView.as_view(), name='item_service_create'),
|
||||||
|
path('items/services/<int:pk>/update/', views.ItemServiceUpdateView.as_view(), name='item_service_update'),
|
||||||
# Expanese
|
# Expanese
|
||||||
path('items/expeneses/', views.ItemExpenseListView.as_view(), name='item_expense_list'),
|
path('items/expeneses/', views.ItemExpenseListView.as_view(), name='item_expense_list'),
|
||||||
path('items/expeneses/create/', views.ItemExpenseCreateView.as_view(), name='item_expense_create'),
|
path('items/expeneses/create/', views.ItemExpenseCreateView.as_view(), name='item_expense_create'),
|
||||||
|
|||||||
@ -9,6 +9,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from inventory.utilities.financials import get_financial_value
|
from inventory.utilities.financials import get_financial_value
|
||||||
from django_ledger.models.items import ItemModel
|
from django_ledger.models.items import ItemModel
|
||||||
from django_ledger.models import InvoiceModel, EstimateModel
|
from django_ledger.models import InvoiceModel, EstimateModel
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
|
||||||
def get_jwt_token():
|
def get_jwt_token():
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
|
from django.forms import DateField, DateInput, HiddenInput, TextInput
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from django_ledger.models import (
|
from django_ledger.models import (
|
||||||
@ -340,19 +341,17 @@ class AjaxHandlerView(LoginRequiredMixin, View):
|
|||||||
|
|
||||||
# Validate inputs
|
# Validate inputs
|
||||||
if not model_id or not year:
|
if not model_id or not year:
|
||||||
return JsonResponse({"error": "Missing required parameters: model_id or year"}, status=400)
|
return JsonResponse(
|
||||||
|
{"error": "Missing required parameters: model_id or year"}, status=400
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
year = int(year)
|
year = int(year)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return JsonResponse({"error": "Invalid year format"}, status=400)
|
return JsonResponse({"error": "Invalid year format"}, status=400)
|
||||||
|
|
||||||
series = models.CarSerie.objects.filter(
|
series = models.CarSerie.objects.filter(
|
||||||
id_car_model=model_id,
|
id_car_model=model_id, year_begin__lte=year, year_end__gte=year
|
||||||
year_begin__lte=year,
|
).values("id_car_serie", "name", "arabic_name", "generation_name")
|
||||||
year_end__gte=year
|
|
||||||
).values(
|
|
||||||
"id_car_serie", "name", "arabic_name", "generation_name"
|
|
||||||
)
|
|
||||||
|
|
||||||
return JsonResponse(list(series), safe=False)
|
return JsonResponse(list(series), safe=False)
|
||||||
|
|
||||||
@ -401,15 +400,19 @@ class AjaxHandlerView(LoginRequiredMixin, View):
|
|||||||
return JsonResponse(serialized_specs, safe=False)
|
return JsonResponse(serialized_specs, safe=False)
|
||||||
|
|
||||||
def get_equipments(self, request):
|
def get_equipments(self, request):
|
||||||
trim_id = request.GET.get('trim_id')
|
trim_id = request.GET.get("trim_id")
|
||||||
equipments = models.CarEquipment.objects.filter(
|
equipments = (
|
||||||
id_car_trim=trim_id
|
models.CarEquipment.objects.filter(id_car_trim=trim_id)
|
||||||
).values('id_car_equipment', 'name').order_by('name')
|
.values("id_car_equipment", "name")
|
||||||
|
.order_by("name")
|
||||||
|
)
|
||||||
return JsonResponse(list(equipments), safe=False)
|
return JsonResponse(list(equipments), safe=False)
|
||||||
|
|
||||||
def get_options(self, request):
|
def get_options(self, request):
|
||||||
equipment_id = request.GET.get('equipment_id')
|
equipment_id = request.GET.get("equipment_id")
|
||||||
car_option_values = models.CarOptionValue.objects.filter(id_car_equipment=equipment_id)
|
car_option_values = models.CarOptionValue.objects.filter(
|
||||||
|
id_car_equipment=equipment_id
|
||||||
|
)
|
||||||
|
|
||||||
options_by_parent = {}
|
options_by_parent = {}
|
||||||
for value in car_option_values:
|
for value in car_option_values:
|
||||||
@ -418,16 +421,19 @@ class AjaxHandlerView(LoginRequiredMixin, View):
|
|||||||
parent_id = parent.id_car_option if parent else 0
|
parent_id = parent.id_car_option if parent else 0
|
||||||
parent_name = parent.name if parent else "Root"
|
parent_name = parent.name if parent else "Root"
|
||||||
if parent_id not in options_by_parent:
|
if parent_id not in options_by_parent:
|
||||||
options_by_parent[parent_id] = {'parent_name': parent_name, 'options': []}
|
options_by_parent[parent_id] = {
|
||||||
|
"parent_name": parent_name,
|
||||||
|
"options": [],
|
||||||
|
}
|
||||||
option_data = {
|
option_data = {
|
||||||
'option_id': option.id_car_option,
|
"option_id": option.id_car_option,
|
||||||
'option_name': option.name,
|
"option_name": option.name,
|
||||||
'is_base': value.is_base,
|
"is_base": value.is_base,
|
||||||
'equipment_name': value.id_car_equipment.name
|
"equipment_name": value.id_car_equipment.name,
|
||||||
}
|
}
|
||||||
options_by_parent[parent_id]['options'].append(option_data)
|
options_by_parent[parent_id]["options"].append(option_data)
|
||||||
serialized_options = [
|
serialized_options = [
|
||||||
{'parent_name': v['parent_name'], 'options': v['options']}
|
{"parent_name": v["parent_name"], "options": v["options"]}
|
||||||
for v in options_by_parent.values()
|
for v in options_by_parent.values()
|
||||||
]
|
]
|
||||||
return JsonResponse(serialized_options, safe=False)
|
return JsonResponse(serialized_options, safe=False)
|
||||||
@ -863,14 +869,18 @@ class CustomerDetailView(LoginRequiredMixin, DetailView):
|
|||||||
context["estimates"] = entity.get_estimates().filter(
|
context["estimates"] = entity.get_estimates().filter(
|
||||||
customer__customer_name=name
|
customer__customer_name=name
|
||||||
)
|
)
|
||||||
context['notes'] = models.Notes.objects.filter(content_type__model='customer', object_id=self.object.id)
|
context["notes"] = models.Notes.objects.filter(
|
||||||
context['activities'] = models.Activity.objects.filter(content_type__model='customer', object_id=self.object.id)
|
content_type__model="customer", object_id=self.object.id
|
||||||
|
)
|
||||||
|
context["activities"] = models.Activity.objects.filter(
|
||||||
|
content_type__model="customer", object_id=self.object.id
|
||||||
|
)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
def add_note_to_customer(request, pk):
|
def add_note_to_customer(request, pk):
|
||||||
customer = get_object_or_404(models.Customer, pk=pk)
|
customer = get_object_or_404(models.Customer, pk=pk)
|
||||||
if request.method == 'POST':
|
if request.method == "POST":
|
||||||
form = forms.NoteForm(request.POST)
|
form = forms.NoteForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
note = form.save(commit=False)
|
note = form.save(commit=False)
|
||||||
@ -878,24 +888,27 @@ def add_note_to_customer(request, pk):
|
|||||||
|
|
||||||
note.created_by = request.user
|
note.created_by = request.user
|
||||||
note.save()
|
note.save()
|
||||||
return redirect('customer_detail', pk=pk)
|
return redirect("customer_detail", pk=pk)
|
||||||
else:
|
else:
|
||||||
form = forms.NoteForm()
|
form = forms.NoteForm()
|
||||||
return render(request, 'crm/add_note.html', {'form': form, 'customer': customer})
|
return render(request, "crm/add_note.html", {"form": form, "customer": customer})
|
||||||
|
|
||||||
|
|
||||||
def add_activity_to_customer(request, pk):
|
def add_activity_to_customer(request, pk):
|
||||||
customer = get_object_or_404(models.Customer, pk=pk)
|
customer = get_object_or_404(models.Customer, pk=pk)
|
||||||
if request.method == 'POST':
|
if request.method == "POST":
|
||||||
form = forms.ActivityForm(request.POST)
|
form = forms.ActivityForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
activity = form.save(commit=False)
|
activity = form.save(commit=False)
|
||||||
activity.content_object = customer
|
activity.content_object = customer
|
||||||
activity.created_by = request.user
|
activity.created_by = request.user
|
||||||
activity.save()
|
activity.save()
|
||||||
return redirect('customer_detail', pk=pk)
|
return redirect("customer_detail", pk=pk)
|
||||||
else:
|
else:
|
||||||
form = forms.ActivityForm()
|
form = forms.ActivityForm()
|
||||||
return render(request, 'crm/add_activity.html', {'form': form, 'customer': customer})
|
return render(
|
||||||
|
request, "crm/add_activity.html", {"form": form, "customer": customer}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CustomerCreateView(
|
class CustomerCreateView(
|
||||||
@ -1503,6 +1516,7 @@ class RepresentativeListView(LoginRequiredMixin, ListView):
|
|||||||
data = models.Representative.objects.filter(dealer=dealer).all()
|
data = models.Representative.objects.filter(dealer=dealer).all()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
class RepresentativeDetailView(DetailView):
|
class RepresentativeDetailView(DetailView):
|
||||||
model = models.Representative
|
model = models.Representative
|
||||||
template_name = "representatives/representative_detail.html"
|
template_name = "representatives/representative_detail.html"
|
||||||
@ -1725,9 +1739,7 @@ class BankAccountListView(LoginRequiredMixin, ListView):
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
dealer = get_user_type(self.request)
|
dealer = get_user_type(self.request)
|
||||||
return BankAccountModel.objects.filter(
|
return BankAccountModel.objects.filter(entity_model=dealer.entity)
|
||||||
entity_model=dealer.entity
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class BankAccountCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
class BankAccountCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||||
@ -1863,6 +1875,11 @@ class AccountUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
|||||||
success_url = reverse_lazy("account_list")
|
success_url = reverse_lazy("account_list")
|
||||||
success_message = "Account updated successfully."
|
success_message = "Account updated successfully."
|
||||||
|
|
||||||
|
def get_form(self, form_class=None):
|
||||||
|
form = super().get_form(form_class)
|
||||||
|
form.fields['_ref_node_id'].widget = HiddenInput()
|
||||||
|
form.fields['_position'].widget = HiddenInput()
|
||||||
|
return form
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def account_delete(request, pk):
|
def account_delete(request, pk):
|
||||||
@ -2046,6 +2063,7 @@ def create_estimate(request):
|
|||||||
for x in car_list
|
for x in car_list
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
print(context)
|
||||||
return render(request, "sales/estimates/estimate_form.html", context)
|
return render(request, "sales/estimates/estimate_form.html", context)
|
||||||
|
|
||||||
|
|
||||||
@ -2265,9 +2283,8 @@ def invoice_create(request, pk):
|
|||||||
dealer = get_user_type(request)
|
dealer = get_user_type(request)
|
||||||
entity = dealer.entity
|
entity = dealer.entity
|
||||||
|
|
||||||
form = InvoiceModelCreateForm(entity_slug=entity.slug, user_model=entity.admin)
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = InvoiceModelCreateForm(
|
form = forms.InvoiceModelCreateForm(
|
||||||
request.POST, entity_slug=entity.slug, user_model=entity.admin
|
request.POST, entity_slug=entity.slug, user_model=entity.admin
|
||||||
)
|
)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
@ -2314,7 +2331,23 @@ def invoice_create(request, pk):
|
|||||||
invoice.save()
|
invoice.save()
|
||||||
messages.success(request, "Invoice created successfully!")
|
messages.success(request, "Invoice created successfully!")
|
||||||
return redirect("invoice_detail", pk=invoice.pk)
|
return redirect("invoice_detail", pk=invoice.pk)
|
||||||
form.initial["customer"] = estimate.customer
|
form = forms.InvoiceModelCreateForm(
|
||||||
|
entity_slug=entity.slug, user_model=entity.admin
|
||||||
|
)
|
||||||
|
|
||||||
|
form.initial.update(
|
||||||
|
{
|
||||||
|
"customer": estimate.customer,
|
||||||
|
"cash_account": entity.get_default_coa_accounts().get(name="Cash"),
|
||||||
|
"prepaid_account": entity.get_default_coa_accounts().get(
|
||||||
|
name="Accounts Receivable"
|
||||||
|
),
|
||||||
|
"unearned_account": entity.get_default_coa_accounts().get(
|
||||||
|
name="Deferred Revenue"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"form": form,
|
"form": form,
|
||||||
"estimate": estimate,
|
"estimate": estimate,
|
||||||
@ -2354,23 +2387,22 @@ def PaymentCreateView(request, pk=None):
|
|||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
amount = form.cleaned_data.get("amount")
|
amount = form.cleaned_data.get("amount")
|
||||||
invoice = form.cleaned_data.get("invoice")
|
invoice = form.cleaned_data.get("invoice")
|
||||||
if amount > invoice.amount_due:
|
payment_method = form.cleaned_data.get("payment_method")
|
||||||
messages.error(
|
|
||||||
request, "Payment amount is greater than invoice amount due"
|
|
||||||
)
|
|
||||||
return redirect("payment_create", pk=invoice.pk)
|
|
||||||
if amount <= 0:
|
|
||||||
messages.error(request, "Payment amount must be greater than 0")
|
|
||||||
return redirect("payment_create", pk=invoice.pk)
|
|
||||||
if (
|
|
||||||
invoice.amount_due == invoice.amount_paid
|
|
||||||
or invoice.invoice_status == "paid"
|
|
||||||
):
|
|
||||||
messages.error(request, "Invoice is already fully paid")
|
|
||||||
return redirect("invoice_detail", pk=invoice.pk)
|
|
||||||
|
|
||||||
ledger = None
|
ledger = None
|
||||||
try:
|
try:
|
||||||
|
vat_amount = 0
|
||||||
|
total_amount = 0
|
||||||
|
|
||||||
|
if invoice.terms == "on_receipt":
|
||||||
|
for x in invoice.get_itemtxs_data()[0].all():
|
||||||
|
vat_amount += models.Car.objects.get(
|
||||||
|
vin=x.item_model.name
|
||||||
|
).finances.vat_amount
|
||||||
|
total_amount += models.Car.objects.get(
|
||||||
|
vin=x.item_model.name
|
||||||
|
).finances.total_discount
|
||||||
|
|
||||||
ledger = LedgerModel.objects.filter(
|
ledger = LedgerModel.objects.filter(
|
||||||
name=str(invoice.pk), entity=entity
|
name=str(invoice.pk), entity=entity
|
||||||
).first()
|
).first()
|
||||||
@ -2381,13 +2413,29 @@ def PaymentCreateView(request, pk=None):
|
|||||||
locked=False,
|
locked=False,
|
||||||
origin="Payment",
|
origin="Payment",
|
||||||
)
|
)
|
||||||
cash_account = entity.get_default_coa_accounts().get(name="Cash")
|
credit_account = entity.get_default_coa_accounts().get(
|
||||||
accounts_receivable = entity.get_default_coa_accounts().get(
|
name="Sales Revenue"
|
||||||
name="Accounts Receivable"
|
)
|
||||||
|
debit_account = None
|
||||||
|
if payment_method == "cash":
|
||||||
|
debit_account = entity.get_default_coa_accounts().get(
|
||||||
|
name="Cash", active=True
|
||||||
|
)
|
||||||
|
elif payment_method == "credit":
|
||||||
|
debit_account = entity.get_default_coa_accounts().get(
|
||||||
|
name="Accounts Receivable", active=True
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
debit_account = entity.get_default_coa_accounts().get(
|
||||||
|
name="Cash in Bank", active=True
|
||||||
|
)
|
||||||
|
|
||||||
|
vat_payable_account = entity.get_default_coa_accounts().get(
|
||||||
|
name="VAT Payable", active=True
|
||||||
)
|
)
|
||||||
TransactionModel.objects.create(
|
TransactionModel.objects.create(
|
||||||
journal_entry=journal,
|
journal_entry=journal,
|
||||||
account=cash_account, # Debit Cash
|
account=debit_account, # Debit Cash
|
||||||
amount=amount, # Payment amount
|
amount=amount, # Payment amount
|
||||||
tx_type="debit",
|
tx_type="debit",
|
||||||
description="Payment Received",
|
description="Payment Received",
|
||||||
@ -2395,27 +2443,31 @@ def PaymentCreateView(request, pk=None):
|
|||||||
|
|
||||||
TransactionModel.objects.create(
|
TransactionModel.objects.create(
|
||||||
journal_entry=journal,
|
journal_entry=journal,
|
||||||
account=accounts_receivable, # Credit Accounts Receivable
|
account=credit_account, # Credit Accounts Receivable
|
||||||
amount=amount, # Payment amount
|
amount=total_amount, # Payment amount
|
||||||
tx_type="credit",
|
tx_type="credit",
|
||||||
description="Payment Received",
|
description="Payment Received",
|
||||||
)
|
)
|
||||||
journal.posted = True
|
|
||||||
invoice.make_payment(amount)
|
|
||||||
journal.save()
|
|
||||||
invoice.save()
|
|
||||||
|
|
||||||
if invoice.amount_due == invoice.amount_paid:
|
if vat_amount > 0:
|
||||||
invoice.mark_as_paid(
|
TransactionModel.objects.create(
|
||||||
entity_slug=entity.slug, user_model=entity.admin
|
journal_entry=journal,
|
||||||
|
account=vat_payable_account, # Credit VAT Payable
|
||||||
|
amount=vat_amount,
|
||||||
|
tx_type="credit",
|
||||||
|
description="VAT Payable on Invoice",
|
||||||
)
|
)
|
||||||
invoice.save()
|
|
||||||
ledger.post()
|
invoice.make_payment(amount)
|
||||||
ledger.save()
|
invoice.save()
|
||||||
messages.success(request, "Payment created successfully!")
|
|
||||||
return redirect("invoice_detail", pk=invoice.pk)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
messages.error(request, f"Error creating payment: {str(e)}")
|
messages.error(request, f"Error creating payment: {str(e)}")
|
||||||
|
else:
|
||||||
|
messages.error(request, f"Invalid form data: {str(form.errors)}")
|
||||||
|
return redirect("invoice_detail", pk=invoice.pk)
|
||||||
|
form = forms.PaymentForm()
|
||||||
|
form.initial["amount"] = invoice.amount_due
|
||||||
|
|
||||||
if invoice:
|
if invoice:
|
||||||
form.initial["invoice"] = invoice
|
form.initial["invoice"] = invoice
|
||||||
return render(
|
return render(
|
||||||
@ -2433,7 +2485,39 @@ def PaymentListView(request):
|
|||||||
|
|
||||||
def PaymentDetailView(request, pk):
|
def PaymentDetailView(request, pk):
|
||||||
journal = JournalEntryModel.objects.filter(pk=pk).first()
|
journal = JournalEntryModel.objects.filter(pk=pk).first()
|
||||||
return render(request, "sales/payments/payment_details.html", {"journal": journal})
|
transactions = (
|
||||||
|
TransactionModel.objects.filter(journal_entry=journal)
|
||||||
|
.order_by("account__code")
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"sales/payments/payment_details.html",
|
||||||
|
{"journal": journal, "transactions": transactions},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def payment_mark_as_paid(request, pk):
|
||||||
|
invoice = get_object_or_404(InvoiceModel, pk=pk)
|
||||||
|
if request.method == "POST":
|
||||||
|
try:
|
||||||
|
if invoice.amount_due == invoice.amount_paid:
|
||||||
|
if not invoice.is_paid() and invoice.can_pay():
|
||||||
|
invoice.mark_as_paid(
|
||||||
|
entity_slug=invoice.ledger.entity.slug,
|
||||||
|
user_model=invoice.ledger.entity.admin,
|
||||||
|
)
|
||||||
|
invoice.save()
|
||||||
|
|
||||||
|
invoice.ledger.lock_journal_entries()
|
||||||
|
invoice.ledger.post_journal_entries()
|
||||||
|
|
||||||
|
invoice.ledger.post()
|
||||||
|
invoice.ledger.save()
|
||||||
|
messages.success(request, "Payment created successfully!")
|
||||||
|
except Exception as e:
|
||||||
|
messages.error(request, f"Error: {str(e)}")
|
||||||
|
return redirect("invoice_detail", pk=invoice.pk)
|
||||||
|
|
||||||
|
|
||||||
# activity log
|
# activity log
|
||||||
@ -2453,8 +2537,8 @@ class UserActivityLogListView(ListView):
|
|||||||
# CRM RELATED VIEWS
|
# CRM RELATED VIEWS
|
||||||
class LeadListView(ListView):
|
class LeadListView(ListView):
|
||||||
model = models.Lead
|
model = models.Lead
|
||||||
template_name = 'crm/leads/lead_list.html'
|
template_name = "crm/leads/lead_list.html"
|
||||||
context_object_name = 'leads'
|
context_object_name = "leads"
|
||||||
paginate_by = 10
|
paginate_by = 10
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
@ -2462,17 +2546,25 @@ class LeadListView(ListView):
|
|||||||
leads = models.Lead.objects.filter(dealer=dealer).all()
|
leads = models.Lead.objects.filter(dealer=dealer).all()
|
||||||
return leads
|
return leads
|
||||||
|
|
||||||
|
|
||||||
class LeadDetailView(DetailView):
|
class LeadDetailView(DetailView):
|
||||||
model = models.Lead
|
model = models.Lead
|
||||||
template_name = 'crm/leads/lead_detail.html'
|
template_name = "crm/leads/lead_detail.html"
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['notes'] = models.Notes.objects.filter(content_type__model='lead', object_id=self.object.id)
|
context["notes"] = models.Notes.objects.filter(
|
||||||
context['activities'] = models.Activity.objects.filter(content_type__model='lead', object_id=self.object.id)
|
content_type__model="lead", object_id=self.object.id
|
||||||
context['status_history'] = models.LeadStatusHistory.objects.filter(lead=self.object)
|
)
|
||||||
|
context["activities"] = models.Activity.objects.filter(
|
||||||
|
content_type__model="lead", object_id=self.object.id
|
||||||
|
)
|
||||||
|
context["status_history"] = models.LeadStatusHistory.objects.filter(
|
||||||
|
lead=self.object
|
||||||
|
)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class LeadCreateView(CreateView, SuccessMessageMixin, LoginRequiredMixin):
|
class LeadCreateView(CreateView, SuccessMessageMixin, LoginRequiredMixin):
|
||||||
model = models.Lead
|
model = models.Lead
|
||||||
form_class = forms.LeadForm
|
form_class = forms.LeadForm
|
||||||
@ -2503,13 +2595,13 @@ class LeadUpdateView(UpdateView):
|
|||||||
|
|
||||||
class LeadDeleteView(DeleteView):
|
class LeadDeleteView(DeleteView):
|
||||||
model = models.Lead
|
model = models.Lead
|
||||||
template_name = 'crm/leads/lead_confirm_delete.html'
|
template_name = "crm/leads/lead_confirm_delete.html"
|
||||||
success_url = reverse_lazy('lead_list')
|
success_url = reverse_lazy("lead_list")
|
||||||
|
|
||||||
|
|
||||||
def add_note_to_lead(request, pk):
|
def add_note_to_lead(request, pk):
|
||||||
lead = get_object_or_404(models.Lead, pk=pk)
|
lead = get_object_or_404(models.Lead, pk=pk)
|
||||||
if request.method == 'POST':
|
if request.method == "POST":
|
||||||
form = forms.NoteForm(request.POST)
|
form = forms.NoteForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
note = form.save(commit=False)
|
note = form.save(commit=False)
|
||||||
@ -2517,24 +2609,25 @@ def add_note_to_lead(request, pk):
|
|||||||
|
|
||||||
note.created_by = request.user
|
note.created_by = request.user
|
||||||
note.save()
|
note.save()
|
||||||
return redirect('lead_detail', pk=pk)
|
return redirect("lead_detail", pk=pk)
|
||||||
else:
|
else:
|
||||||
form = forms.NoteForm()
|
form = forms.NoteForm()
|
||||||
return render(request, 'crm/add_note.html', {'form': form, 'lead': lead})
|
return render(request, "crm/add_note.html", {"form": form, "lead": lead})
|
||||||
|
|
||||||
|
|
||||||
def add_activity_to_lead(request, pk):
|
def add_activity_to_lead(request, pk):
|
||||||
lead = get_object_or_404(models.Lead, pk=pk)
|
lead = get_object_or_404(models.Lead, pk=pk)
|
||||||
if request.method == 'POST':
|
if request.method == "POST":
|
||||||
form = forms.ActivityForm(request.POST)
|
form = forms.ActivityForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
activity = form.save(commit=False)
|
activity = form.save(commit=False)
|
||||||
activity.content_object = lead
|
activity.content_object = lead
|
||||||
activity.created_by = request.user
|
activity.created_by = request.user
|
||||||
activity.save()
|
activity.save()
|
||||||
return redirect('lead_detail', pk=pk)
|
return redirect("lead_detail", pk=pk)
|
||||||
else:
|
else:
|
||||||
form = forms.ActivityForm()
|
form = forms.ActivityForm()
|
||||||
return render(request, 'crm/add_activity.html', {'form': form, 'lead': lead})
|
return render(request, "crm/add_activity.html", {"form": form, "lead": lead})
|
||||||
|
|
||||||
|
|
||||||
class OpportunityCreateView(CreateView):
|
class OpportunityCreateView(CreateView):
|
||||||
@ -2650,11 +2743,28 @@ def fetch_notifications(request):
|
|||||||
return JsonResponse({"notifications": notifications_data})
|
return JsonResponse({"notifications": notifications_data})
|
||||||
|
|
||||||
|
|
||||||
class ItemServiceCreateView(CreateView):
|
class ItemServiceCreateView(LoginRequiredMixin,SuccessMessageMixin,CreateView):
|
||||||
model = models.AdditionalServices
|
model = models.AdditionalServices
|
||||||
form_class = forms.AdditionalServiceForm
|
form_class = forms.AdditionalServiceForm
|
||||||
template_name = "items/service/service_create.html"
|
template_name = "items/service/service_create.html"
|
||||||
success_url = reverse_lazy("item_service_list")
|
success_url = reverse_lazy("item_service_list")
|
||||||
|
success_message = _("Service created successfully.")
|
||||||
|
context_object_name = "service"
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
vat = models.VatRate.objects.get(is_active=True)
|
||||||
|
form.instance.dealer = get_user_type(self.request.user.dealer)
|
||||||
|
if form.instance.taxable:
|
||||||
|
form.instance.price = (form.instance.price * vat.rate) + form.instance.price
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
class ItemServiceUpdateView(LoginRequiredMixin,SuccessMessageMixin,UpdateView):
|
||||||
|
model = models.AdditionalServices
|
||||||
|
form_class = forms.AdditionalServiceForm
|
||||||
|
template_name = "items/service/service_create.html"
|
||||||
|
success_url = reverse_lazy("item_service_list")
|
||||||
|
success_message = _("Service updated successfully.")
|
||||||
|
context_object_name = "service"
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
vat = models.VatRate.objects.get(is_active=True)
|
vat = models.VatRate.objects.get(is_active=True)
|
||||||
@ -2665,13 +2775,13 @@ class ItemServiceCreateView(CreateView):
|
|||||||
|
|
||||||
|
|
||||||
class ItemServiceListView(ListView):
|
class ItemServiceListView(ListView):
|
||||||
model = ItemModel
|
model = models.AdditionalServices
|
||||||
template_name = "items/service/service_list.html"
|
template_name = "items/service/service_list.html"
|
||||||
context_object_name = "services"
|
context_object_name = "services"
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
dealer = get_user_type(self.request)
|
dealer = get_user_type(self.request)
|
||||||
items = dealer.entity.get_items_services()
|
items = models.AdditionalServices.objects.filter(dealer=dealer).all()
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -138,7 +138,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-item-wrapper">
|
<div class="nav-item-wrapper">
|
||||||
<a class="nav-link dropdown-indicator label-1" href="#nv-crm" role="button" data-bs-toggle="collapse" aria-expanded="false" aria-controls="nv-crm">
|
<a class="nav-link dropdown-indicator label-1" href="#nv-crm" role="button" data-bs-toggle="collapse" aria-expanded="false" aria-controls="nv-crm">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<div class="dropdown-indicator-icon-wrapper"><span class="fas fa-caret-right dropdown-indicator-icon"></span></div>
|
<div class="dropdown-indicator-icon-wrapper"><span class="fas fa-caret-right dropdown-indicator-icon"></span></div>
|
||||||
@ -189,31 +189,48 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-item-wrapper">
|
<!--Add Above -->
|
||||||
<a class="nav-link dropdown-indicator label-1" href="#nv-accounting" role="button" data-bs-toggle="collapse" aria-expanded="false" aria-controls="nv-accounting">
|
<div class="nav-item-wrapper">
|
||||||
|
<a class="nav-link dropdown-indicator label-1" href="#nv-financial" role="button" data-bs-toggle="collapse" aria-expanded="false" aria-controls="nv-financial">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<div class="dropdown-indicator-icon-wrapper"><span class="fas fa-caret-right dropdown-indicator-icon"></span></div>
|
<div class="dropdown-indicator-icon-wrapper"><span class="fas fa-caret-right dropdown-indicator-icon"></span></div>
|
||||||
<span class="nav-link-icon"><span data-feather="book"></span></span><span class="nav-link-text">{% trans 'Accounting' %}</span>
|
<span class="nav-link-icon"><span data-feather="phone"></span></span><span class="nav-link-text">{% trans 'Financials' %}</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<div class="parent-wrapper label-1">
|
<div class="parent-wrapper label-1">
|
||||||
<ul class="nav collapse parent" data-bs-parent="#navbarVerticalCollapse" id="nv-accounting">
|
<ul class="nav collapse parent" data-bs-parent="#navbarVerticalCollapse" id="nv-financial">
|
||||||
<li class="collapsed-nav-item-title d-none">{% trans 'Accounting' %}</li>
|
<li class="collapsed-nav-item-title d-none">{% trans 'crm'|upper %}</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url 'account_list' %}">
|
<a class="nav-link" href="{% url 'account_list' %}">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<span class="nav-link-icon"><span data-feather="book-open"></span></span><span class="nav-link-text">{% trans 'Chart of Accounts' %}</span>
|
<span class="nav-link-icon"><span data-feather="users"></span></span><span class="nav-link-text">{% trans 'Accounts'|capfirst %}</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url 'bank_account_list' %}">
|
<a class="nav-link" href="{% url 'bank_account_list' %}">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<span class="nav-link-icon"><span data-feather="users"></span></span><span class="nav-link-text">{% trans 'Banks' %}</span>
|
<span class="nav-link-icon"><span data-feather="users"></span></span><span class="nav-link-text">{% trans 'Bank Accounts'|capfirst %}</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{% url 'item_service_list' %}">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<span class="nav-link-icon"><span data-feather="activity"></span></span><span class="nav-link-text">{% trans "Services"|capfirst %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<!-- more inner pages-->
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{% url 'item_expense_list' %}">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<span class="nav-link-icon"><span class="fas fa-users-cog"></span></span><span class="nav-link-text">{% trans "Expenses"|capfirst %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<!-- more inner pages-->
|
||||||
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,8 +4,12 @@
|
|||||||
{% block title %}{{ _("Expenses") }}{% endblock title %}
|
{% block title %}{{ _("Expenses") }}{% endblock title %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row mt-4">
|
<div class="row mt-4 mx-4">
|
||||||
<h3 class="text-center">{% trans "Expenses" %}</h3>
|
<div class="d-flex justify-content-between mb-2 p-6">
|
||||||
|
<span></span>
|
||||||
|
<h3 class="text-center">{% trans "Expenses" %}</h3>
|
||||||
|
<a href="{% url 'item_expense_create' %}" class="btn btn-sm btn-success ">{% trans "Add Expense" %}</a>
|
||||||
|
</div>
|
||||||
<div class="mx-n4 px-4 mx-lg-n6 px-lg-6 bg-body-emphasis pt-7 border-y">
|
<div class="mx-n4 px-4 mx-lg-n6 px-lg-6 bg-body-emphasis pt-7 border-y">
|
||||||
|
|
||||||
<div class="table-responsive mx-n1 px-1 scrollbar">
|
<div class="table-responsive mx-n1 px-1 scrollbar">
|
||||||
@ -24,7 +28,7 @@
|
|||||||
<td class="align-middle product white-space-nowrap py-0">{{ expense.item_number }}</td>
|
<td class="align-middle product white-space-nowrap py-0">{{ expense.item_number }}</td>
|
||||||
<td class="align-middle product white-space-nowrap">{{ expense.name }}</td>
|
<td class="align-middle product white-space-nowrap">{{ expense.name }}</td>
|
||||||
<td class="align-middle product white-space-nowrap">{{ expense.uom }}</td>
|
<td class="align-middle product white-space-nowrap">{{ expense.uom }}</td>
|
||||||
<td class="text-center">
|
<td class="">
|
||||||
<a href="{% url 'item_expense_update' expense.pk %}"
|
<a href="{% url 'item_expense_update' expense.pk %}"
|
||||||
class="btn btn-sm btn-phoenix-success">
|
class="btn btn-sm btn-phoenix-success">
|
||||||
{% trans "Update" %}
|
{% trans "Update" %}
|
||||||
|
|||||||
@ -7,8 +7,14 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">{{ _("Add Service") }}</div>
|
<div class="card-header">
|
||||||
|
{% if service.pk %}
|
||||||
|
{{ _("UpdateService") }}
|
||||||
|
{% else %}
|
||||||
|
{{ _("Add Service") }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form method="post" action="">
|
<form method="post" action="">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|||||||
@ -1,106 +1,60 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load i18n %}
|
{% load i18n static %}
|
||||||
{% load render_table from django_tables2 %}
|
|
||||||
|
|
||||||
{% block title %}{% trans "users" %}{% endblock title %}
|
{% block title %}{{ _("Expenses") }}{% endblock title %}
|
||||||
{% block users %}<a class="nav-link active">{% trans "users"|capfirst %}</a>{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="d-flex flex-column min-vh-100">
|
<div class="row mt-4 mx-4">
|
||||||
<div class="d-flex flex-column flex-sm-grow-1 ms-sm-14 p-4">
|
<div class="d-flex justify-content-between mb-2 p-6">
|
||||||
<main class="d-grid gap-4 p-1">
|
<span></span>
|
||||||
<div class="row g-4">
|
<h3 class="text-center">{% trans "Services" %}</h3>
|
||||||
<div class="col-lg-6 col-xl-12">
|
<a href="{% url 'item_service_create' %}" class="btn btn-sm btn-success ">{% trans "Add Service" %}</a>
|
||||||
<div class="row-fluid p-2">
|
</div>
|
||||||
<form method="get">
|
<div class="mx-n4 px-4 mx-lg-n6 px-lg-6 bg-body-emphasis pt-7 border-y">
|
||||||
<div class="input-group input-group-sm">
|
|
||||||
<button id="inputGroup-sizing-sm"
|
<div class="table-responsive mx-n1 px-1 scrollbar">
|
||||||
class="btn btn-sm btn-secondary rounded-start" type="submit">
|
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||||
{% trans 'search'|capfirst %}
|
|
||||||
</button>
|
|
||||||
<input type="text"
|
|
||||||
name="q"
|
|
||||||
class="form-control form-control-sm rounded-end"
|
|
||||||
value="{{ request.GET.q }}"
|
|
||||||
aria-describedby="inputGroup-sizing-sm"/>
|
|
||||||
<!-- Clear Button -->
|
|
||||||
{% if request.GET.q %}
|
|
||||||
<a href="{% url request.resolver_match.view_name %}"
|
|
||||||
class="btn btn-sm btn-outline-danger ms-1 rounded">
|
|
||||||
<i class="bi bi-x-lg"></i>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<table class="table table-hover table-responsive-sm">
|
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans 'Item Number'|capfirst %}</th>
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Item Number" %}</th>
|
||||||
<th>{% trans 'Name'|capfirst %}</th>
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Name" %}</th>
|
||||||
<th>{% trans 'Unit of Measure'|capfirst %}</th>
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Unit of Measure" %}</th>
|
||||||
<th>{% trans 'Cost of Goods Sold'|capfirst %}</th>
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Taxable" %}</th>
|
||||||
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Account" %}</th>
|
||||||
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Action" %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody class="list">
|
||||||
{% for service in services %}
|
{% for service in services %}
|
||||||
|
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||||
|
<td class="align-middle product white-space-nowrap py-0">{{ service.item.item_number }}</td>
|
||||||
|
<td class="align-middle product white-space-nowrap">{{ service.name }}</td>
|
||||||
|
<td class="align-middle product white-space-nowrap">{{ service.uom }}</td>
|
||||||
|
<td class="align-middle product white-space-nowrap">
|
||||||
|
{% if service.taxable %}
|
||||||
|
Yes
|
||||||
|
{% else %}
|
||||||
|
No
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="align-middle product white-space-nowrap">{{ service.item.cogs_account }}</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="{% url 'item_service_update' service.pk %}"
|
||||||
|
class="btn btn-sm btn-phoenix-success">
|
||||||
|
{% trans "Update" %}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ service.item_number }}</td>
|
<td colspan="6" class="text-center">{% trans "No Invoice Found" %}</td>
|
||||||
<td>{{ service.name }}</td>
|
|
||||||
<td>{{ service.uom }}</td>
|
|
||||||
<td>{{ service.cogs_account }}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<!-- Optional: Pagination -->
|
</div>
|
||||||
{% if is_paginated %}
|
<div class="d-flex justify-content-center">
|
||||||
<nav aria-label="Page navigation">
|
</div>
|
||||||
<ul class="pagination mb-0">
|
|
||||||
{% if page_obj.has_previous %}
|
|
||||||
<li class="page-item py-0">
|
|
||||||
<a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous">
|
|
||||||
<span aria-hidden="true"><span class="fas fa-chevron-left"></span></span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% else %}
|
|
||||||
<li class="page-item disabled">
|
|
||||||
<a class="page-link" href="#" aria-label="Previous">
|
|
||||||
<span aria-hidden="true"><span class="fas fa-chevron-left"></span></span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
{% for num in page_obj.paginator.page_range %}
|
|
||||||
{% if page_obj.number == num %}
|
|
||||||
<li class="page-item active"><a class="page-link" href="?page={{ num }}">{{ num }}</a></li>
|
|
||||||
{% else %}
|
|
||||||
<li class="page-item"><a class="page-link" href="?page={{ num }}">{{ num }}</a></li>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% if page_obj.has_next %}
|
|
||||||
<li class="page-item">
|
|
||||||
<a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="Next">
|
|
||||||
<span aria-hidden="true"><span class="fas fa-chevron-right"></span></span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% else %}
|
|
||||||
<li class="page-item disabled">
|
|
||||||
<a class="page-link" href="#" aria-label="Next">
|
|
||||||
<span aria-hidden="true"><span class="fas fa-chevron-right"></span></span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@ -1,82 +1,50 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load i18n %}
|
{% load i18n static %}
|
||||||
{% block title %}{% trans "Bank Accounts" %}{% endblock title %}
|
|
||||||
{% block bank_accounts %}
|
{% block title %}{{ _("Bank Accounts") }}{% endblock title %}
|
||||||
<a class="nav-link active fw-bold">
|
|
||||||
{% trans "Bank Accounts" %}
|
|
||||||
<span class="visually-hidden">(current)</span>
|
|
||||||
</a>
|
|
||||||
{% endblock %}
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<div class="row mt-4 mx-4">
|
||||||
<!-- Search Bar -->
|
<div class="d-flex justify-content-between mb-2 p-6">
|
||||||
<div class="row g-3">
|
<span></span>
|
||||||
<div class="col-12">
|
<h3 class="text-center">{% trans "Bank Accounts" %}</h3>
|
||||||
<form method="get">
|
<a href="{% url 'bank_account_create' %}" class="btn btn-sm btn-success ">{% trans "Add Bank Account" %}</a>
|
||||||
<div class="input-group input-group-sm">
|
</div>
|
||||||
<button class="btn btn-sm btn-secondary rounded-start" type="submit">
|
<div class="mx-n4 px-4 mx-lg-n6 px-lg-6 bg-body-emphasis pt-7 border-y">
|
||||||
{% trans "search" %}
|
|
||||||
</button>
|
<div class="table-responsive mx-n1 px-1 scrollbar">
|
||||||
<input type="text"
|
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||||
name="q"
|
<thead>
|
||||||
class="form-control form-control-sm rounded-end"
|
<tr>
|
||||||
value="{{ request.GET.q }}"
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Name" %}</th>
|
||||||
placeholder="{% trans 'Search accounts...' %}" />
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Account Number" %}</th>
|
||||||
{% if request.GET.q %}
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Type" %}</th>
|
||||||
<a href="{% url request.resolver_match.view_name %}"
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Action" %}</th>
|
||||||
class="btn btn-sm btn-outline-danger ms-1 rounded">
|
</tr>
|
||||||
<i class="bi bi-x-lg"></i>
|
</thead>
|
||||||
</a>
|
<tbody class="list">
|
||||||
{% endif %}
|
{% for bank in bank_accounts %}
|
||||||
</div>
|
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||||
</form>
|
<td class="align-middle product white-space-nowrap">{{ bank.name }}</td>
|
||||||
</div>
|
<td class="align-middle product white-space-nowrap py-0">{{ bank.account_number }}</td>
|
||||||
|
<td class="align-middle product white-space-nowrap py-0">{{ bank.account_type|capfirst }}</td>
|
||||||
|
<td class="">
|
||||||
|
<a href="{% url 'bank_account_update' bank.pk %}"
|
||||||
|
class="btn btn-sm btn-phoenix-success">
|
||||||
|
{% trans "Update" %}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="6" class="text-center">{% trans "No Invoice Found" %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Bank Accounts Table -->
|
|
||||||
<div class="row g-3">
|
|
||||||
<div class="col-12">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header bg-primary text-white">
|
|
||||||
<h5 class="mb-0">{% trans "Bank Accounts" %}</h5>
|
|
||||||
</div>
|
|
||||||
<div class="card-body p-0">
|
|
||||||
<table class="table table-hover table-sm mb-0">
|
|
||||||
<thead class="table-light">
|
|
||||||
<tr>
|
|
||||||
<th>{% trans "Name" %}</th>
|
|
||||||
<th>{% trans "Type" %}</th>
|
|
||||||
<th class="text-center">{% trans "Actions" %}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for account in bank_accounts %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ account.name }}</td>
|
|
||||||
<td>{{ account.cash_account }}</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<a href="{% url 'bank_account_detail' account.pk %}"
|
|
||||||
class="btn btn-sm btn-success">
|
|
||||||
{% trans "view" %}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% empty %}
|
|
||||||
<tr>
|
|
||||||
<td colspan="5" class="text-center text-muted">
|
|
||||||
{% trans "No customers found." %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<!-- Pagination -->
|
|
||||||
{% if is_paginated %}
|
|
||||||
{% include 'partials/pagination.html' %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -177,6 +177,49 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</main>
|
</main>
|
||||||
|
<div class="row mt-4 mx-4">
|
||||||
|
<div class="d-flex justify-content-between mb-2 p-6">
|
||||||
|
<span></span>
|
||||||
|
<h3 class="text-center">{% trans "Accounts" %}</h3>
|
||||||
|
<a href="{% url 'account_create' %}" class="btn btn-sm btn-success ">{% trans "Add Account" %}</a>
|
||||||
|
</div>
|
||||||
|
<div class="mx-n4 px-4 mx-lg-n6 px-lg-6 bg-body-emphasis pt-7 border-y">
|
||||||
|
|
||||||
|
<div class="table-responsive mx-n1 px-1 scrollbar">
|
||||||
|
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Name" %}</th>
|
||||||
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Account Number" %}</th>
|
||||||
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Balance Type" %}</th>
|
||||||
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Active" %}</th>
|
||||||
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Action" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="list">
|
||||||
|
{% for account in accounts %}
|
||||||
|
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||||
|
<td class="align-middle product white-space-nowrap">{{ account.name }}</td>
|
||||||
|
<td class="align-middle product white-space-nowrap py-0">{{ account.code }}</td>
|
||||||
|
<td class="align-middle product white-space-nowrap py-0">{{ account.balance_type|capfirst }}</td>
|
||||||
|
<td class="align-middle product white-space-nowrap py-0">{{ account.active }}</td>
|
||||||
|
<td class="">
|
||||||
|
<a href="{% url 'account_update' account.pk %}"
|
||||||
|
class="btn btn-sm btn-phoenix-success">
|
||||||
|
{% trans "Update" %}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="6" class="text-center">{% trans "No Invoice Found" %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
<!-- Buttons -->
|
<!-- Buttons -->
|
||||||
<div class="mt-5 text-center">
|
<div class="mt-5 text-center">
|
||||||
<button type="submit" class="btn btn-success me-2">{% trans "Save" %}</button>
|
<button type="submit" class="btn btn-success me-2" {% if not items %}disabled{% endif %}>{% trans "Save" %}</button>
|
||||||
<a href="{% url 'estimate_list' %}" class="btn btn-secondary">{% trans "Cancel" %}</a>
|
<a href="{% url 'estimate_list' %}" class="btn btn-secondary">{% trans "Cancel" %}</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -27,6 +27,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- ============================================-->
|
||||||
|
<div class="modal fade" id="mark_as_paid_Modal" tabindex="-1" aria-labelledby="confirmModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-sm">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header bg-primary">
|
||||||
|
<h5 class="modal-title text-light" id="confirmModalLabel">{% trans 'Confirm' %}</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
{% trans 'Are you sure' %}
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-sm btn-danger"
|
||||||
|
data-bs-dismiss="modal">
|
||||||
|
{% trans 'No' %}
|
||||||
|
</button>
|
||||||
|
<form id="confirmForm" method="POST" action="{% url 'payment_mark_as_paid' invoice.pk %}" class="d-inline">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button type="submit" class="btn btn-success btn-sm">{% trans "Yes" %}</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- ============================================-->
|
<!-- ============================================-->
|
||||||
<!-- <section> begin ============================-->
|
<!-- <section> begin ============================-->
|
||||||
@ -39,8 +64,11 @@
|
|||||||
<button id="accept_invoice" class="btn btn-phoenix-secondary" data-bs-toggle="modal" data-bs-target="#confirmModal"><span class="d-none d-sm-inline-block">{% trans 'Accept' %}</span></button>
|
<button id="accept_invoice" class="btn btn-phoenix-secondary" data-bs-toggle="modal" data-bs-target="#confirmModal"><span class="d-none d-sm-inline-block">{% trans 'Accept' %}</span></button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if invoice.invoice_status == 'approved' %}
|
{% if invoice.invoice_status == 'approved' %}
|
||||||
<a href="{% url 'payment_create' invoice.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Record Payment' %}</span></a>
|
<a href="{% url 'payment_create' invoice.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Record Payment' %}</span></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if not invoice.is_paid %}
|
||||||
|
<button id="mark_invoice_as_paid" class="btn btn-phoenix-secondary" data-bs-toggle="modal" data-bs-target="#mark_as_paid_Modal"><span class="d-none d-sm-inline-block">{% trans 'Mark as Paid' %}</span></button>
|
||||||
|
{% endif %}
|
||||||
<a href="{% url 'invoice_preview' invoice.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Preview' %}</span></a>
|
<a href="{% url 'invoice_preview' invoice.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Preview' %}</span></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -54,20 +82,63 @@
|
|||||||
<div class="d-flex bg-success-subtle rounded flex-center me-3 mb-sm-3 mb-md-0 mb-xl-3 mb-xxl-0" style="width:32px; height:32px"><span class="text-success-dark" data-feather="dollar-sign" style="width:24px; height:24px"></span></div>
|
<div class="d-flex bg-success-subtle rounded flex-center me-3 mb-sm-3 mb-md-0 mb-xl-3 mb-xxl-0" style="width:32px; height:32px"><span class="text-success-dark" data-feather="dollar-sign" style="width:24px; height:24px"></span></div>
|
||||||
<div>
|
<div>
|
||||||
<p class="fw-bold mb-1">{% trans 'Paid Amount' %}</p>
|
<p class="fw-bold mb-1">{% trans 'Paid Amount' %}</p>
|
||||||
<h4 class="fw-bolder text-nowrap">${{invoice.amount_paid}}</h4>
|
<h4 class="fw-bolder text-nowrap {% if invoice.is_paid %}text-success{% endif %}">${{invoice.amount_paid}}</h4>
|
||||||
<h6 class="fw-bolder text-nowrap">Owned <span class="fw-semibold text-nowrap text-success">${{invoice.get_amount_open}}</span></h6>
|
<h6 class="fw-bolder text-nowrap">Owned <span class="fw-semibold text-nowrap text-success">${{invoice.get_amount_open}}</span></h6>
|
||||||
<div class="progress" style="height:17px">
|
<div class="progress" style="height:17px">
|
||||||
<div class="progress-bar fw-semibold bg-{% if invoice.get_progress_percent < 100 %}secondary{% else %}success{% endif %} rounded-2" role="progressbar" style="width: {{invoice.get_progress_percent}}%"" aria-valuenow="{{invoice.get_progress_percent}}" aria-valuemin="0" aria-valuemax="100">{{invoice.get_progress_percent}}%</div>
|
<div class="progress-bar fw-semibold bg-{% if invoice.get_progress_percent < 100 %}secondary{% else %}success{% endif %} rounded-2" role="progressbar" style="width: {{invoice.get_progress_percent}}%" aria-valuenow="{{invoice.get_progress_percent}}" aria-valuemin="0" aria-valuemax="100">{{invoice.get_progress_percent}}%</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if 'net' in invoice.terms %}
|
||||||
|
<div class="col-sm-auto">
|
||||||
|
<div class="d-sm-block d-inline-flex d-md-flex flex-xl-column flex-xxl-row align-items-center align-items-xl-start align-items-xxl-center border-start-sm ps-sm-5 border-translucent">
|
||||||
|
<div class="d-flex bg-primary-subtle rounded flex-center me-3 mb-sm-3 mb-md-0 mb-xl-3 mb-xxl-0" style="width:32px; height:32px"><span class="text-primary-dark" data-feather="layout" style="width:24px; height:24px"></span></div>
|
||||||
|
<div>
|
||||||
|
<p class="fw-bold mb-1"></p>
|
||||||
|
<div class="fs-9 text-body-secondary fw-semibold mb-0 d-sm-block d-inline-flex d-md-flex flex-xl-column ">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Terms' %}:</td>
|
||||||
|
<td><span class="badge rounded bg-success">{{invoice.terms}}</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Date Due' %}:</td>
|
||||||
|
<td><span class="badge rounded bg-success">{{invoice.date_due}}</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Due in Days' %}:</td>
|
||||||
|
<td>
|
||||||
|
<span class="badge rounded bg-success">{{invoice.due_in_days}}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Is Past Due' %}:</td>
|
||||||
|
<td>
|
||||||
|
{% if invoice.is_past_due %}
|
||||||
|
<span class="badge rounded bg-danger">{% trans 'Yes' %}</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="badge rounded bg-success">{% trans 'No' %}</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
<div class="col-sm-auto">
|
<div class="col-sm-auto">
|
||||||
<div class="d-sm-block d-inline-flex d-md-flex flex-xl-column flex-xxl-row align-items-center align-items-xl-start align-items-xxl-center border-start-sm ps-sm-5 border-translucent">
|
<div class="d-sm-block d-inline-flex d-md-flex flex-xl-column flex-xxl-row align-items-center align-items-xl-start align-items-xxl-center border-start-sm ps-sm-5 border-translucent">
|
||||||
<div class="d-flex bg-primary-subtle rounded flex-center me-3 mb-sm-3 mb-md-0 mb-xl-3 mb-xxl-0" style="width:32px; height:32px"><span class="text-primary-dark" data-feather="layout" style="width:24px; height:24px"></span></div>
|
<div class="d-flex bg-primary-subtle rounded flex-center me-3 mb-sm-3 mb-md-0 mb-xl-3 mb-xxl-0" style="width:32px; height:32px"><span class="text-primary-dark" data-feather="layout" style="width:24px; height:24px"></span></div>
|
||||||
<div>
|
<div>
|
||||||
<p class="fw-bold mb-1">{% trans 'Due Amount' %}</p>
|
<p class="fw-bold mb-1">{% trans 'Due Amount' %}</p>
|
||||||
<h4 class="fw-bolder text-nowrap">${{invoice.amount_due}} </h4>
|
{% if invoice.is_paid %}
|
||||||
|
<s><h4 class="fw-bolder text-nowrap">${{invoice.amount_due}} </h4></s>
|
||||||
|
{% else %}
|
||||||
|
<h4 class="fw-bolder text-nowrap">${{invoice.amount_due}} </h4>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -75,7 +146,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <section> begin ============================-->
|
<!-- <section> begin ============================-->
|
||||||
<div class="bg-body dark__bg-gray-1100 p-4 mb-4 rounded-2">
|
<div class="bg-body dark__bg-gray-1100 p-4 mb-4 rounded-2 text-body-tertiary">
|
||||||
<div class="row g-4">
|
<div class="row g-4">
|
||||||
<div class="col-12 col-lg-3">
|
<div class="col-12 col-lg-3">
|
||||||
<div class="row g-4 g-lg-2">
|
<div class="row g-4 g-lg-2">
|
||||||
@ -188,7 +259,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extra_js %}
|
{% block extra_js %}
|
||||||
|
|||||||
@ -16,21 +16,21 @@
|
|||||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Timestamp" %}</th>
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Timestamp" %}</th>
|
||||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Account Name" %}</th>
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Account Name" %}</th>
|
||||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Account Code" %}</th>
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Account Code" %}</th>
|
||||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Description" %}</th>
|
|
||||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Credit" %}</th>
|
|
||||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Debit" %}</th>
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Debit" %}</th>
|
||||||
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Credit" %}</th>
|
||||||
|
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Description" %}</th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="list">
|
<tbody class="list">
|
||||||
{% for transaction in journal.transactionmodel_set.all %}
|
{% for transaction in transactions %}
|
||||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||||
|
|
||||||
<td>{{ forloop.counter }}</td>
|
<td>{{ forloop.counter }}</td>
|
||||||
<td class="align-middle product white-space-nowrap py-0">{{ transaction.created|date}}</td>
|
<td class="align-middle product white-space-nowrap py-0">{{ transaction.created|date}}</td>
|
||||||
<td class="align-middle product white-space-nowrap">{{ transaction.account.name }}</td>
|
<td class="align-middle product white-space-nowrap">{{ transaction.account.name }}</td>
|
||||||
<td class="align-middle product white-space-nowrap">{{ transaction.account.code }}</td>
|
<td class="align-middle product white-space-nowrap">{{ transaction.account.code }}</td>
|
||||||
<td class="align-middle product white-space-nowrap">{{ transaction.description }}</td>
|
|
||||||
<td class="align-middle product white-space-nowrap">{% if transaction.tx_type == "credit" %}${{ transaction.amount }}{% endif %}</td>
|
|
||||||
<td class="align-middle product white-space-nowrap">{% if transaction.tx_type == "debit" %}${{ transaction.amount }}{% endif %}</td>
|
<td class="align-middle product white-space-nowrap">{% if transaction.tx_type == "debit" %}${{ transaction.amount }}{% endif %}</td>
|
||||||
|
<td class="align-middle product white-space-nowrap">{% if transaction.tx_type == "credit" %}${{ transaction.amount }}{% endif %}</td>
|
||||||
|
<td class="align-middle product white-space-nowrap">{{ transaction.description }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@ -37,4 +37,3 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
||||||
|
|||||||
@ -5,8 +5,10 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row mt-4">
|
<div class="row mt-4">
|
||||||
<a href="{% url 'payment_create' %}" class="btn btn-sm btn-success align-right">{% trans "Add Payment" %}</a>
|
<div>
|
||||||
<h3 class="text-center">{% trans "Payments" %}</h3>
|
<a href="{% url 'payment_create' %}" class="btn btn-sm btn-success ">{% trans "Add Payment" %}</a>
|
||||||
|
<h3 class="text-center">{% trans "Payments" %}</h3>
|
||||||
|
</div>
|
||||||
<div class="mx-n4 px-4 mx-lg-n6 px-lg-6 bg-body-emphasis pt-7 border-y">
|
<div class="mx-n4 px-4 mx-lg-n6 px-lg-6 bg-body-emphasis pt-7 border-y">
|
||||||
|
|
||||||
<div class="table-responsive mx-n1 px-1 scrollbar">
|
<div class="table-responsive mx-n1 px-1 scrollbar">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user