update in notifications and bills
This commit is contained in:
parent
042d1ac34b
commit
64f76c0f1a
BIN
db.sqlite3-shm
BIN
db.sqlite3-shm
Binary file not shown.
BIN
db.sqlite3.backup
Normal file
BIN
db.sqlite3.backup
Normal file
Binary file not shown.
@ -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
|
||||
|
||||
|
||||
@ -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 = [
|
||||
|
||||
@ -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=[
|
||||
|
||||
@ -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),
|
||||
),
|
||||
]
|
||||
@ -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),
|
||||
),
|
||||
]
|
||||
@ -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'),
|
||||
),
|
||||
]
|
||||
@ -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 = [
|
||||
]
|
||||
@ -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',
|
||||
},
|
||||
),
|
||||
]
|
||||
@ -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,
|
||||
),
|
||||
]
|
||||
@ -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'),
|
||||
),
|
||||
]
|
||||
@ -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',
|
||||
),
|
||||
]
|
||||
@ -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'),
|
||||
),
|
||||
]
|
||||
@ -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')]),
|
||||
),
|
||||
]
|
||||
@ -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'),
|
||||
),
|
||||
]
|
||||
@ -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')]),
|
||||
),
|
||||
]
|
||||
@ -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')]),
|
||||
),
|
||||
]
|
||||
@ -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,
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -79,7 +79,7 @@
|
||||
|
||||
|
||||
</script>
|
||||
{% block extra_js %}{% endblock extra_js %}
|
||||
{% block customJS %}{% endblock customJS %}
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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' %}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -240,7 +240,7 @@
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
{% block customJS %}
|
||||
<script>
|
||||
function calculateTotals() {
|
||||
const table = document.getElementById('estimate-table');
|
||||
|
||||
@ -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 %}
|
||||
50
templates/notifications.html
Normal file
50
templates/notifications.html
Normal 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>
|
||||
|
||||
@ -174,7 +174,7 @@
|
||||
<!-- ============================================-->
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
{% block customJS %}
|
||||
<script>
|
||||
function calculateTotals() {
|
||||
const table = document.getElementById('estimate-table');
|
||||
|
||||
@ -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 %}
|
||||
@ -262,7 +262,7 @@
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
{% block customJS %}
|
||||
<script>
|
||||
function calculateTotals() {
|
||||
const table = document.getElementById('estimate-table');
|
||||
|
||||
@ -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 }}
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user