update in notifications and bills

This commit is contained in:
gitea 2025-01-22 11:43:34 +00:00
parent 042d1ac34b
commit 64f76c0f1a
34 changed files with 166 additions and 736 deletions

Binary file not shown.

View File

BIN
db.sqlite3.backup Normal file

Binary file not shown.

View File

@ -1,4 +1,4 @@
# Generated by Django 5.1.4 on 2025-01-12 17:20
# Generated by Django 4.2.17 on 2025-01-21 13:59
from django.db import migrations, models

View File

@ -1,4 +1,4 @@
# Generated by Django 5.1.4 on 2025-01-12 17:20
# Generated by Django 4.2.17 on 2025-01-21 13:59
from django.db import migrations, models
import django.db.models.deletion
@ -9,8 +9,8 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('inventory', '0001_initial'),
('haikalbot', '0001_initial'),
('inventory', '0001_initial'),
]
operations = [

View File

@ -1,4 +1,4 @@
# Generated by Django 5.1.4 on 2025-01-12 17:20
# Generated by Django 4.2.17 on 2025-01-21 13:59
from decimal import Decimal
from django.conf import settings
@ -15,6 +15,7 @@ class Migration(migrations.Migration):
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
]
@ -42,7 +43,7 @@ class Migration(migrations.Migration):
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('vin', models.CharField(max_length=17, unique=True, verbose_name='VIN')),
('year', models.IntegerField(verbose_name='Year')),
('status', models.CharField(choices=[('available', 'Available'), ('sold', 'Sold'), ('hold', 'Hold'), ('damaged', 'Damaged'), ('reserved', 'Reserved')], default='available', max_length=10, verbose_name='Status')),
('status', models.CharField(choices=[('available', 'Available'), ('sold', 'Sold'), ('hold', 'Hold'), ('damaged', 'Damaged'), ('reserved', 'Reserved'), ('transfer', 'Transfer')], default='available', max_length=10, verbose_name='Status')),
('stock_type', models.CharField(choices=[('new', 'New'), ('used', 'Used')], default='new', max_length=10, verbose_name='Stock Type')),
('remarks', models.TextField(blank=True, null=True, verbose_name='Remarks')),
('mileage', models.IntegerField(blank=True, null=True, verbose_name='Mileage')),
@ -74,138 +75,13 @@ class Migration(migrations.Migration):
('arabic_name', models.CharField(blank=True, max_length=255, null=True)),
('logo', models.ImageField(blank=True, null=True, upload_to='car_make', verbose_name='logo')),
('is_sa_import', models.BooleanField(default=False)),
('car_type', models.SmallIntegerField(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')])),
('car_type', 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)),
],
options={
'verbose_name': 'Make',
},
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
),
migrations.CreateModel(
name='ExteriorColors',
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')),
('rgb', models.CharField(blank=True, max_length=24, null=True, verbose_name='RGB')),
],
options={
'verbose_name': 'Exterior Colors',
'verbose_name_plural': 'Exterior Colors',
},
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
),
migrations.CreateModel(
name='InteriorColors',
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')),
('rgb', models.CharField(blank=True, max_length=24, null=True, verbose_name='RGB')),
],
options={
'verbose_name': 'Interior Colors',
'verbose_name_plural': 'Interior Colors',
},
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
),
migrations.CreateModel(
name='Payment',
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')),
('payment_method', models.CharField(choices=[('cash', 'cash'), ('credit', 'credit'), ('transfer', 'transfer'), ('debit', 'debit'), ('SADAD', 'SADAD')], max_length=50, verbose_name='method')),
('reference_number', models.CharField(blank=True, max_length=100, null=True, verbose_name='reference number')),
('payment_date', models.DateField(auto_now_add=True, verbose_name='date')),
],
options={
'verbose_name': 'payment',
'verbose_name_plural': 'payments',
},
),
migrations.CreateModel(
name='SubscriptionPlan',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(help_text='Name of the subscription plan', max_length=100, unique=True)),
('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={
'verbose_name': 'Subscription Plan',
'verbose_name_plural': 'Subscription Plans',
},
),
migrations.CreateModel(
name='VatRate',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('rate', models.DecimalField(decimal_places=2, default=Decimal('0.15'), max_digits=5)),
('is_active', models.BooleanField(default=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
],
),
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(
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')),
('item', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='django_ledger.itemmodel', verbose_name='Item')),
],
options={
'verbose_name': 'Additional Services',
'verbose_name_plural': 'Additional Services',
},
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
),
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='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.CreateModel(
name='CarModel',
fields=[
@ -274,7 +150,6 @@ class Migration(migrations.Migration):
('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')),
@ -310,51 +185,46 @@ class Migration(migrations.Migration):
],
),
migrations.CreateModel(
name='CarLocation',
name='ExteriorColors',
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')),
('name', models.CharField(max_length=255, verbose_name='Name')),
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
('rgb', models.CharField(blank=True, max_length=24, null=True, verbose_name='RGB')),
],
options={
'verbose_name': 'Car Location',
'verbose_name_plural': 'Car Locations',
'verbose_name': 'Exterior Colors',
'verbose_name_plural': 'Exterior Colors',
},
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
),
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='additionalservices',
name='dealer',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.dealer', verbose_name='Dealer'),
migrations.CreateModel(
name='InteriorColors',
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')),
('rgb', models.CharField(blank=True, max_length=24, null=True, verbose_name='RGB')),
],
options={
'verbose_name': 'Interior Colors',
'verbose_name_plural': 'Interior Colors',
},
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
),
migrations.CreateModel(
name='Lead',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('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')], max_length=20, verbose_name='Title')),
('first_name', models.CharField(max_length=50, verbose_name='First Name')),
('last_name', models.CharField(max_length=50, verbose_name='Last Name')),
('email', models.EmailField(db_index=True, max_length=254, unique=True, verbose_name='Email')),
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')),
('salary', models.PositiveIntegerField(blank=True, null=True, verbose_name='Salary')),
('obligations', models.PositiveIntegerField(blank=True, null=True, verbose_name='Obligations')),
('year', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Year')),
('source', models.CharField(choices=[('referrals', 'Referrals'), ('whatsapp', 'WhatsApp'), ('showroom', 'Showroom'), ('tiktok', 'TikTok'), ('instagram', 'Instagram'), ('x', 'X'), ('facebook', 'Facebook'), ('motory', 'Motory'), ('influencers', 'Influencers'), ('youtube', 'Youtube'), ('campaign', 'Campaign')], max_length=50, verbose_name='Source')),
('channel', models.CharField(choices=[('walk_in', 'Walk In'), ('toll_free', 'Toll Free'), ('website', 'Website'), ('email', 'Email'), ('form', 'Form')], max_length=50, verbose_name='Channel')),
('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')),
('city', models.CharField(max_length=50, verbose_name='City')),
('priority', models.CharField(choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High')], default='medium', max_length=10, verbose_name='Priority')),
('status', models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('canceled', 'Canceled')], db_index=True, default='new', max_length=50, verbose_name='Status')),
('created', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Created')),
('updated', models.DateTimeField(auto_now=True, verbose_name='Updated')),
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='leads', to='inventory.customer')),
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='leads', to='inventory.dealer')),
('id_car_make', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake', verbose_name='Make')),
('id_car_model', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel', verbose_name='Model')),
@ -364,62 +234,6 @@ class Migration(migrations.Migration):
'verbose_name_plural': 'Leads',
},
),
migrations.CreateModel(
name='Customer',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('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')),
('first_name', models.CharField(max_length=50, verbose_name='First Name')),
('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')),
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='customers', to='inventory.dealer')),
('lead', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='converted', to='inventory.lead', verbose_name='Lead')),
],
options={
'verbose_name': 'Customer',
'verbose_name_plural': 'Customers',
},
),
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='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='Organization',
fields=[
@ -431,7 +245,8 @@ class Migration(migrations.Migration):
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')),
('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')),
('logo', models.ImageField(blank=True, null=True, upload_to='logos', verbose_name='Logo')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')),
('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='organizations', to='inventory.dealer')),
],
options={
@ -478,98 +293,6 @@ class Migration(migrations.Migration):
('dealer', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sales', to='inventory.dealer')),
],
),
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='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='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='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='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='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')),
('lead', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='status_history', to='inventory.lead')),
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='status_changes', to='inventory.staff')),
],
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='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='Subscription',
fields=[
@ -719,7 +442,7 @@ class Migration(migrations.Migration):
('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')),
('id_number', models.CharField(max_length=10, unique=True, 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')),
@ -820,39 +543,14 @@ class Migration(migrations.Migration):
),
migrations.AddField(
model_name='lead',
name='assigned',
name='staff',
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=[
@ -881,6 +579,27 @@ class Migration(migrations.Migration):
},
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
),
migrations.CreateModel(
name='CarTransfer',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('transfer_date', models.DateTimeField(auto_now_add=True, verbose_name='Transfer Date')),
('quantity', models.IntegerField(default=1, verbose_name='Quantity')),
('remarks', models.TextField(blank=True, null=True, verbose_name='Remarks')),
('status', models.CharField(default='draft', max_length=10, verbose_name=[('draft', 'Draft'), ('approved', 'Approved'), ('pending', 'Pending'), ('accepted', 'Accepted'), ('success', 'Success'), ('reject', 'Reject')])),
('is_approved', models.BooleanField(default=False)),
('active', models.BooleanField(default=True)),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')),
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transfer_logs', to='inventory.car', verbose_name='Car')),
('from_dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transfers_out', to='inventory.dealer', verbose_name='From Dealer')),
('to_dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transfers_in', to='inventory.dealer', verbose_name='To Dealer')),
],
options={
'verbose_name': 'Car Transfer Log',
'verbose_name_plural': 'Car Transfer Logs',
},
),
migrations.CreateModel(
name='CarSpecificationValue',
fields=[

View File

@ -1,18 +0,0 @@
# 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),
),
]

View File

@ -1,18 +0,0 @@
# 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),
),
]

View File

@ -1,90 +0,0 @@
# Generated by Django 5.1.4 on 2025-01-17 00:20
import django.db.models.deletion
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0003_alter_carmake_car_type'),
]
operations = [
migrations.RenameField(
model_name='lead',
old_name='assigned',
new_name='staff',
),
migrations.RemoveField(
model_name='customer',
name='city',
),
migrations.RemoveField(
model_name='customer',
name='lead',
),
migrations.RemoveField(
model_name='customer',
name='staff',
),
migrations.RemoveField(
model_name='lead',
name='address',
),
migrations.RemoveField(
model_name='lead',
name='email',
),
migrations.RemoveField(
model_name='lead',
name='first_name',
),
migrations.RemoveField(
model_name='lead',
name='last_name',
),
migrations.RemoveField(
model_name='lead',
name='obligations',
),
migrations.RemoveField(
model_name='lead',
name='phone_number',
),
migrations.RemoveField(
model_name='lead',
name='salary',
),
migrations.RemoveField(
model_name='lead',
name='title',
),
migrations.RemoveField(
model_name='organization',
name='created_at',
),
migrations.AddField(
model_name='lead',
name='customer',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='leads', to='inventory.customer'),
preserve_default=False,
),
migrations.AddField(
model_name='organization',
name='created',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='Created'),
preserve_default=False,
),
migrations.AddField(
model_name='organization',
name='updated',
field=models.DateTimeField(auto_now=True, verbose_name='Updated'),
),
migrations.AlterField(
model_name='representative',
name='id_number',
field=models.CharField(max_length=10, unique=True, verbose_name='ID Number'),
),
]

View File

@ -1,13 +0,0 @@
# Generated by Django 4.2.17 on 2025-01-19 12:55
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0004_rename_assigned_lead_staff_remove_customer_city_and_more'),
]
operations = [
]

View File

@ -1,29 +0,0 @@
# Generated by Django 4.2.17 on 2025-01-19 14:01
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('inventory', '0005_merge_20250119_1555'),
]
operations = [
migrations.CreateModel(
name='CarTransferLog',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('transfer_date', models.DateTimeField(auto_now_add=True, verbose_name='Transfer Date')),
('remarks', models.TextField(blank=True, null=True, verbose_name='Remarks')),
('cars', models.ManyToManyField(related_name='transfer_logs', to='inventory.car', verbose_name='Cars')),
('from_dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transfers_out', to='inventory.dealer', verbose_name='From Dealer')),
('to_dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transfers_in', to='inventory.dealer', verbose_name='To Dealer')),
],
options={
'verbose_name': 'Car Transfer Log',
'verbose_name_plural': 'Car Transfer Logs',
},
),
]

View File

@ -1,24 +0,0 @@
# Generated by Django 4.2.17 on 2025-01-19 14:15
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('inventory', '0006_cartransferlog_delete_invoicemodelbase'),
]
operations = [
migrations.RemoveField(
model_name='cartransferlog',
name='cars',
),
migrations.AddField(
model_name='cartransferlog',
name='car',
field=models.ForeignKey(default=4, on_delete=django.db.models.deletion.CASCADE, related_name='transfer_logs', to='inventory.car', verbose_name='Car'),
preserve_default=False,
),
]

View File

@ -1,35 +0,0 @@
# Generated by Django 4.2.17 on 2025-01-19 14:29
import datetime
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0007_remove_cartransferlog_cars_cartransferlog_car'),
]
operations = [
migrations.AddField(
model_name='cartransferlog',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2025, 1, 19, 14, 29, 29, 771881, tzinfo=datetime.timezone.utc), verbose_name='Created At'),
preserve_default=False,
),
migrations.AddField(
model_name='cartransferlog',
name='is_approved',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='cartransferlog',
name='status',
field=models.CharField(default='pending', max_length=10, verbose_name=[('draft', 'Draft'), ('approved', 'Approved'), ('pending', 'Pending'), ('success', 'Success'), ('failure', 'Failure')]),
),
migrations.AddField(
model_name='cartransferlog',
name='updated_at',
field=models.DateTimeField(auto_now=True, verbose_name='Updated At'),
),
]

View File

@ -1,36 +0,0 @@
# Generated by Django 4.2.17 on 2025-01-19 14:31
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('inventory', '0008_cartransferlog_created_at_cartransferlog_is_approved_and_more'),
]
operations = [
migrations.CreateModel(
name='CarTransfer',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('transfer_date', models.DateTimeField(auto_now_add=True, verbose_name='Transfer Date')),
('remarks', models.TextField(blank=True, null=True, verbose_name='Remarks')),
('status', models.CharField(default='draft', max_length=10, verbose_name=[('draft', 'Draft'), ('approved', 'Approved'), ('pending', 'Pending'), ('success', 'Success'), ('failure', 'Failure')])),
('is_approved', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')),
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transfer_logs', to='inventory.car', verbose_name='Car')),
('from_dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transfers_out', to='inventory.dealer', verbose_name='From Dealer')),
('to_dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transfers_in', to='inventory.dealer', verbose_name='To Dealer')),
],
options={
'verbose_name': 'Car Transfer Log',
'verbose_name_plural': 'Car Transfer Logs',
},
),
migrations.DeleteModel(
name='CarTransferLog',
),
]

View File

@ -1,23 +0,0 @@
# Generated by Django 4.2.17 on 2025-01-19 15:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0009_cartransfer_delete_cartransferlog'),
]
operations = [
migrations.AddField(
model_name='cartransfer',
name='active',
field=models.BooleanField(default=True),
),
migrations.AlterField(
model_name='car',
name='status',
field=models.CharField(choices=[('available', 'Available'), ('sold', 'Sold'), ('hold', 'Hold'), ('damaged', 'Damaged'), ('reserved', 'Reserved'), ('transfer', 'Transfer')], default='available', max_length=10, verbose_name='Status'),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 4.2.17 on 2025-01-20 08:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0010_cartransfer_active_alter_car_status'),
]
operations = [
migrations.AlterField(
model_name='cartransfer',
name='status',
field=models.CharField(default='draft', max_length=10, verbose_name=[('draft', 'Draft'), ('approved', 'Approved'), ('pending', 'Pending'), ('accept', 'Accept'), ('success', 'Success'), ('failure', 'Failure')]),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 4.2.17 on 2025-01-20 08:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0011_alter_cartransfer_status'),
]
operations = [
migrations.AddField(
model_name='cartransfer',
name='quantity',
field=models.IntegerField(default=1, verbose_name='Quantity'),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 4.2.17 on 2025-01-20 09:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0012_cartransfer_quantity'),
]
operations = [
migrations.AlterField(
model_name='cartransfer',
name='status',
field=models.CharField(default='draft', max_length=10, verbose_name=[('draft', 'Draft'), ('approved', 'Approved'), ('pending', 'Pending'), ('accept', 'Accept'), ('success', 'Success'), ('reject', 'Reject')]),
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 4.2.17 on 2025-01-21 08:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0013_alter_cartransfer_status'),
]
operations = [
migrations.AlterField(
model_name='cartransfer',
name='status',
field=models.CharField(default='draft', max_length=10, verbose_name=[('draft', 'Draft'), ('approved', 'Approved'), ('pending', 'Pending'), ('accepted', 'Accepted'), ('success', 'Success'), ('reject', 'Reject')]),
),
]

View File

@ -447,7 +447,7 @@ urlpatterns = [
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,

View File

@ -829,6 +829,10 @@ def car_transfer_accept_reject(request, car_pk,transfer_pk):
transfer.status = "reject"
transfer.active = False
messages.success(request, _("Car transfer rejected successfully."))
models.Notification.objects.create(
user=transfer.from_dealer.user,
message=f"Car transfer request from {transfer.to_dealer} is rejected.",
)
transfer.save()
elif status == "accepted":
transfer.status = "accept"
@ -836,6 +840,10 @@ def car_transfer_accept_reject(request, car_pk,transfer_pk):
success = transfer_car(car,transfer)
if success:
messages.success(request, _("Car Transfer Completed successfully."))
models.Notification.objects.create(
user=transfer.from_dealer.user,
message=f"Car transfer request from {transfer.to_dealer} is completed.",
)
return redirect("inventory_stats")
def CarTransferPreviewView(request, car_pk,transfer_pk):
@ -2549,11 +2557,11 @@ class InvoicePreviewView(LoginRequiredMixin, DetailView):
# payments
def PaymentCreateView(request, pk=None):
def PaymentCreateView(request, pk):
print(pk)
invoice = InvoiceModel.objects.filter(pk=pk).first()
bill = BillModel.objects.filter(pk=pk).first()
model = invoice if invoice else bill
redirect_url = 'invoice_detail' if invoice else 'bill_detail'
dealer = get_user_type(request)
entity = dealer.entity
form = forms.PaymentForm()
@ -2564,7 +2572,9 @@ def PaymentCreateView(request, pk=None):
invoice = form.cleaned_data.get("invoice")
bill = form.cleaned_data.get("bill")
payment_method = form.cleaned_data.get("payment_method")
redirect_url = 'invoice_detail' if invoice else 'bill_detail'
model = invoice if invoice else bill
if not model.is_approved():
model.mark_as_approved(user_model=entity.admin)
try:
@ -2572,6 +2582,7 @@ def PaymentCreateView(request, pk=None):
set_invoice_payment(dealer,entity,invoice,amount,payment_method)
elif bill:
set_bill_payment(dealer,entity,bill,amount,payment_method)
messages.success(request, "Payment created successfully!")
return redirect(redirect_url, pk=model.pk)
except Exception as e:
messages.error(request, f"Error creating payment: {str(e)}")
@ -2579,8 +2590,8 @@ def PaymentCreateView(request, pk=None):
messages.error(request, f"Invalid form data: {str(form.errors)}")
# return redirect(redirect_url, pk=model.pk)
form = forms.PaymentForm()
form.initial["amount"] = model.amount_due - model.amount_paid
if model:
form.initial["amount"] = model.amount_due - model.amount_paid
if isinstance(model, InvoiceModel):
form.initial["invoice"] = model
form.fields['bill'].widget = HiddenInput()
@ -2630,7 +2641,12 @@ def payment_mark_as_paid(request, pk):
invoice.ledger.post()
invoice.ledger.save()
messages.success(request, "Payment created successfully!")
messages.success(request, "Payment created successfully!")
else:
messages.error(
request,
"Invoice is not fully paid. Payment cannot be marked as paid.",
)
except Exception as e:
messages.error(request, f"Error: {str(e)}")
return redirect("invoice_detail", pk=invoice.pk)
@ -2849,15 +2865,16 @@ def fetch_notifications(request):
notifications = models.Notification.objects.filter(
user=request.user, is_read=False
).order_by("-created")
notifications_data = [
{
"id": notification.id,
"message": notification.message,
"created": notification.created.strftime("%Y-%m-%d %H:%M:%S"),
}
for notification in notifications
]
return JsonResponse({"notifications": notifications_data})
# notifications_data = [
# {
# "id": notification.id,
# "message": notification.message,
# "created": notification.created.strftime("%Y-%m-%d %H:%M:%S"),
# }
# for notification in notifications
# ]
# return JsonResponse({"notifications": notifications_data})
return render(request,'notifications.html',{'notifications_':notifications})
class ItemServiceCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
@ -3106,8 +3123,8 @@ def bill_create(request):
terms = data.get("terms")
vendor = entity.get_vendors().filter(pk=vendor_id).first()
items = data.get("item[]", [])
quantities = data.get("quantity[]", [])
items = data.get("item", [])
quantities = data.get("quantity", [])
if not all([items, quantities]):
return JsonResponse(

View File

@ -79,7 +79,7 @@
</script>
{% block extra_js %}{% endblock extra_js %}
{% block customJS %}{% endblock customJS %}
<script>

View File

@ -71,7 +71,6 @@
{% endblock %}
<!-- ===============================================-->
<!-- JavaScripts-->
<!-- ===============================================-->
@ -90,8 +89,10 @@
<script src="{% static 'js/main.js' %}"></script>
<script src="{% static 'vendors/mapbox-gl/mapbox-gl.js' %}"></script>
<script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script>
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
<script src="{% static 'vendors/swiper/swiper-bundle.min.js' %}"></script>
<script src="{% static 'vendors/flatpickr/flatpickr.min.js' %}"></script>
<script>

View File

@ -15,7 +15,7 @@
{% if not notification.is_read %}
<p class="fs-9 text-body-highlight"><span class="far fa-envelope text-success-dark fs-8 me-1"></span><span class="me-1">{{ notification.message|safe }}</span> <span class="ms-2 text-body-tertiary text-opacity-85 fw-bold fs-10 text-end">{{ notification.created|timesince }}</span></p>
{% else %}
<p class="fs-9 text-body-highlight"><span class="far fa-envelope-open text-danger-dark fs-8 me-1"></span><span>{{ notification.message }}</span> <span class="ms-2 text-body-tertiary text-opacity-85 fw-bold fs-10 text-end">{{ notification.created|timesince }}</span></p>
<p class="fs-9 text-body-highlight"><span class="far fa-envelope-open text-danger-dark fs-8 me-1"></span><span>{{ notification.message|safe }}</span> <span class="ms-2 text-body-tertiary text-opacity-85 fw-bold fs-10 text-end">{{ notification.created|timesince }}</span></p>
{% endif %}
<p class="text-body-secondary fs-9 mb-0"><span class="me-1 far fa-clock"></span>{{ notification.created }}</p>
</div>

View File

@ -300,11 +300,8 @@
</label>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link" href="{% url 'notifications_history' %}" style="min-width: 2.25rem;" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" data-bs-auto-close="outside">
<span class="d-block" style="height: 20px; width: 20px;"><span data-feather="bell" style="height: 20px; width: 20px;"></span></span>
</a>
</li>
{% include "notifications.html" %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="languageDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside" aria-haspopup="true">
{% if request.LANGUAGE_CODE == 'ar' %}

View File

@ -291,20 +291,25 @@
<tbody>
<tr>
<td>Transfer</td>
<td><span class="badge badge-phoenix badge-phoenix-info">Transfer</span></td>
<td>
{% if car.get_transfer.status == "draft" %}
<span class="badge badge-phoenix badge-phoenix-warning">
waiting for approval
</span>
{% elif car.get_transfer.status == "approved" %}
<span class="badge badge-phoenix badge-phoenix-info">
waiting for dealer acceptance
</span>
{% endif %}
</td>
<td>{{ car.get_transfer.from_dealer }}</td>
<td>{{ car.get_transfer.to_dealer }}</td>
<td>{{ car.get_transfer.transfer_date }}</td>
<td>{{ car.get_transfer.from_dealer|title }}</td>
<td>{{ car.get_transfer.to_dealer|title }}</td>
<td>{{ car.get_transfer.transfer_date|date:"Y-m-d" }}</td>
<td>
{% if car.get_transfer.status == "draft" %}
<a href="{% url 'transfer_detail' car.get_transfer.pk %}">Approve</a>
<a class="btn btn-sm btn-phoenix-success" href="{% url 'transfer_detail' car.get_transfer.pk %}">Approve</a>
{% endif %}
</td>
</tr>

View File

@ -240,7 +240,7 @@
</div>
{% endblock %}
{% block extra_js %}
{% block customJS %}
<script>
function calculateTotals() {
const table = document.getElementById('estimate-table');

View File

@ -45,7 +45,7 @@
</div>
{% endblock content %}
{% block extra_js %}
{% block customJS %}
<script>
const Toast = Swal.mixin({
toast: true,
@ -115,6 +115,7 @@
quantity: []
};
// Collect multi-value fields (e.g., item[], quantity[])
document.querySelectorAll('[name="item[]"]').forEach(input => {
formData.item.push(input.value);
@ -122,7 +123,7 @@
document.querySelectorAll('[name="quantity[]"]').forEach(input => {
formData.quantity.push(input.value);
});
console.log(formData)
try {
// Send data to the server using fetch
const response = await fetch("{% url 'bill_create' %}", {
@ -154,4 +155,4 @@
}
});
</script>
{% endblock extra_js %}
{% endblock customJS %}

View File

@ -0,0 +1,50 @@
<li class="nav-item dropdown">
<div class="notification-count" hx-get="{% url 'fetch_notifications' %}" hx-trigger="every 10s" hx-swap="innerHTML" hx-select=".notification-count">
{% if notifications_ %}
<span class="badge bg-danger rounded-pill " id="notification-counter" style="position: absolute; top: 8px; right: 3px; font-size: 0.50rem;">{{ notifications_.count }}</span>
{% endif %}
</div>
<a class="nav-link" href="{% url 'fetch_notifications' %}" hx-get="{% url 'fetch_notifications' %}" hx-swap="innerHTML" hx-target=".card-body" hx-select=".card-body" style="min-width: 2.25rem" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" data-bs-auto-close="outside"><span class="d-block" style="height:20px;width:20px;"><span data-feather="bell" style="height:20px;width:20px;"></span></span></span>
</a>
<div class="dropdown-menu dropdown-menu-end notification-dropdown-menu py-0 shadow border navbar-dropdown-caret" id="navbarDropdownNotfication" aria-labelledby="navbarDropdownNotfication">
<div class="card position-relative border-0">
<div class="card-header p-2">
<div class="d-flex justify-content-between">
<h5 class="text-body-emphasis mb-0">Notifications</h5>
<button class="btn btn-link p-0 fs-9 fw-normal" type="button">Mark all as read</button>
</div>
</div>
<div class="card-body p-0">
<div class="scrollbar-overlay" style="height: 27rem;">
{% for notification in notifications_ %}
<div class="px-2 px-sm-3 py-3 notification-card position-relative read border-bottom">
<div class="d-flex align-items-center justify-content-between position-relative">
<div class="d-flex">
<div class="flex-1 me-sm-3">
<h4 class="fs-9 text-body-emphasis">System</h4>
<p class="fs-9 text-body-highlight mb-2 mb-sm-3 fw-normal">
<span class="me-1 fs-10">💬</span>{{notification.message|safe}}<span class="ms-2 text-body-quaternary text-opacity-75 fw-bold fs-10">10m</span>
</p>
<p class="text-body-secondary fs-9 mb-0">
<span class="me-1 fas fa-clock"></span><span class="fw-bold">10:41 AM</span>{{notification.created}}
</p>
</div>
</div>
<div class="dropdown notification-dropdown">
<button class="btn fs-10 btn-sm dropdown-toggle dropdown-caret-none transition-none" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10 text-body"></span></button>
<div class="dropdown-menu py-2">
<a class="dropdown-item" href="{% url 'mark_notification_as_read' notification.id %}">Mark as read</a>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
<div class="card-footer p-0 border-top border-translucent border-0">
<div class="my-2 text-center fw-bold fs-10 text-body-tertiary text-opactity-85"><a class="fw-bolder" href="{% url 'notifications_history' %}">Notification history</a></div>
</div>
</div>
</div>
</li>

View File

@ -174,7 +174,7 @@
<!-- ============================================-->
{% endblock %}
{% block extra_js %}
{% block customJS %}
<script>
function calculateTotals() {
const table = document.getElementById('estimate-table');

View File

@ -45,7 +45,7 @@
</div>
{% endblock content %}
{% block extra_js %}
{% block customJS %}
<script>
const Toast = Swal.mixin({
toast: true,
@ -155,4 +155,4 @@
}
});
</script>
{% endblock extra_js %}
{% endblock customJS %}

View File

@ -262,7 +262,7 @@
</div>
{% endblock %}
{% block extra_js %}
{% block customJS %}
<script>
function calculateTotals() {
const table = document.getElementById('estimate-table');

View File

@ -12,20 +12,18 @@
</style>
{% endblock extra_css %}
{% block content %}
<div class="row {% if invoice.invoice_status == 'paid' %}paid{% endif %}">
<div class="row {% if model.is_paid %}paid{% endif %}">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
{% if invoice.invoice_status == 'paid' %}
{% if model.is_paid %}
<div class="card-header">{{ _("Payment Already Made") }}</div>
{% else %}
<div class="card-header">{{ _("Make Payment") }}</div>
{% endif %}
<div class="card-body">
{% if invoice %}
<form method="post" action="{% url 'payment_create' pk=invoice.pk %}">
{% else %}
<form method="post" action="{% url 'payment_create' %}">
{% if model %}
<form method="post" action="{% url 'payment_create' pk=model.pk %}">
{% endif %}
{% csrf_token %}
{{ form|crispy }}

View File

@ -7,7 +7,7 @@
<div class="row mt-4">
<div class="d-flex justify-content-between mb-2">
<h3 class="mb-3">{% trans "Payments" %}</h3>
<a href="{% url 'payment_create' %}" class="btn btn-sm btn-phoenix-success ">{% trans "Add Payment" %}</a>
{% comment %} <a href="{% url 'payment_create' %}" class="btn btn-sm btn-phoenix-success ">{% trans "Add Payment" %}</a> {% endcomment %}
</div>