update
This commit is contained in:
parent
a24e5bc82b
commit
b48d0600f4
2
.gitignore
vendored
2
.gitignore
vendored
@ -4,7 +4,7 @@
|
||||
*.pyc
|
||||
__pycache__
|
||||
**/*__pycache__
|
||||
db.sqlite3
|
||||
db.sqlite
|
||||
media
|
||||
car_inventory/settings.py
|
||||
# Backup files #
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Generated by Django 5.1.4 on 2024-12-26 10:09
|
||||
# Generated by Django 5.1.4 on 2024-12-26 16:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -1,4 +1,4 @@
|
||||
# Generated by Django 4.2.17 on 2024-12-26 10:12
|
||||
# Generated by Django 5.1.4 on 2024-12-26 16:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Generated by Django 4.2.17 on 2024-12-26 10:12
|
||||
# Generated by Django 5.1.4 on 2024-12-26 16:17
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -43,7 +43,8 @@ class PaymentForm(forms.ModelForm):
|
||||
class UserForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Dealer
|
||||
fields = ['name', 'arabic_name', 'phone_number', 'address','dealer_type']
|
||||
fields = ['name', 'arabic_name', 'phone_number', 'address','dealer_type']
|
||||
|
||||
# Dealer Form
|
||||
class DealerForm(forms.ModelForm):
|
||||
class Meta:
|
||||
@ -265,134 +266,125 @@ class CarSelectionTable(tables.Table):
|
||||
|
||||
|
||||
class WizardForm1(forms.Form):
|
||||
name = forms.CharField(
|
||||
label="Name",
|
||||
widget=forms.TextInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'Name',
|
||||
'required': 'required',
|
||||
}),
|
||||
error_messages={
|
||||
'required': 'Please choose a username.',
|
||||
}
|
||||
)
|
||||
arabic_name = forms.CharField(
|
||||
label="Arabic Name",
|
||||
widget=forms.TextInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'Arabic Name',
|
||||
'required': 'required',
|
||||
}),
|
||||
error_messages={
|
||||
'required': 'Please choose an Arabic name.',
|
||||
}
|
||||
)
|
||||
|
||||
email = forms.EmailField(
|
||||
label="Email*",
|
||||
widget=forms.EmailInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'Email address',
|
||||
'pattern': '^([a-zA-Z0-9_.-])+@(([a-zA-Z0-9-])+.)+([a-zA-Z0-9]{2,4})+$',
|
||||
'required': 'required',
|
||||
}),
|
||||
error_messages={
|
||||
'required': 'You must add an email.',
|
||||
'required': _('You must add an email.'),
|
||||
}
|
||||
)
|
||||
password = forms.CharField(
|
||||
widget=forms.PasswordInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': _('Password'),
|
||||
'required': 'required',
|
||||
}),
|
||||
error_messages={
|
||||
'required': _('This field is required.'),
|
||||
}
|
||||
)
|
||||
confirm_password = forms.CharField(
|
||||
widget=forms.PasswordInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': _('Confirm Password'),
|
||||
'required': 'required',
|
||||
}),
|
||||
error_messages={
|
||||
'required': _('This field is required.'),
|
||||
}
|
||||
)
|
||||
terms = forms.BooleanField(
|
||||
label="I accept the <a href='#!'>terms</a> and <a href='#!'>privacy policy</a>",
|
||||
|
||||
widget=forms.CheckboxInput(attrs={
|
||||
'class': 'form-check-input',
|
||||
'required': 'required',
|
||||
'checked': 'checked',
|
||||
|
||||
}),
|
||||
error_messages={
|
||||
'required': 'You must accept the terms and privacy policy.',
|
||||
'required': _('You must accept the terms and privacy policy.'),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class WizardForm2(forms.Form):
|
||||
# Phone field with SA region validation
|
||||
name = forms.CharField(
|
||||
widget=forms.TextInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': _('English Name'),
|
||||
'required': 'required',
|
||||
}),
|
||||
error_messages={
|
||||
'required': _('Please enter an English Name.'),
|
||||
}
|
||||
)
|
||||
arabic_name = forms.CharField(
|
||||
widget=forms.TextInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': _('Arabic Name'),
|
||||
'required': 'required',
|
||||
}),
|
||||
error_messages={
|
||||
'required': _('Please enter an Arabic name.'),
|
||||
}
|
||||
)
|
||||
phone_number = PhoneNumberField(
|
||||
label="Phone",
|
||||
min_length=10,
|
||||
max_length=10,
|
||||
widget=forms.TextInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'Phone',
|
||||
'placeholder': _('Phone'),
|
||||
'required': 'required',
|
||||
}),
|
||||
region='SA', # Enforces SA region validation
|
||||
region='SA',
|
||||
error_messages={
|
||||
'required': 'This field is required.',
|
||||
'invalid': 'Phone number must be in the format +966XXXXXXXXX (Saudi Arabia).',
|
||||
'required': _('This field is required.'),
|
||||
'invalid': _('Phone number must be in the format 05xxxxxxxx'),
|
||||
}
|
||||
)
|
||||
|
||||
class WizardForm3(forms.Form):
|
||||
# CRN field with max length of 10
|
||||
crn = forms.CharField(
|
||||
label="CRN",
|
||||
widget=forms.TextInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'CRN',
|
||||
'placeholder': _("Commercial Registration Number"),
|
||||
'required': 'required',
|
||||
'maxlength': '10', # HTML maxlength attribute
|
||||
'maxlength': '10',
|
||||
}),
|
||||
max_length=10, # Django max_length validation
|
||||
max_length=10,
|
||||
error_messages={
|
||||
'required': 'This field is required.',
|
||||
'max_length': 'CRN must be at most 10 characters long.',
|
||||
'max_length': 'Commercial Registration Number must be 10 characters.',
|
||||
}
|
||||
)
|
||||
|
||||
# VRN field with max length of 15
|
||||
vrn = forms.CharField(
|
||||
label="VRN",
|
||||
widget=forms.TextInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'VRN',
|
||||
'placeholder': _("VAT Registration Number"),
|
||||
'required': 'required',
|
||||
'maxlength': '15', # HTML maxlength attribute
|
||||
'maxlength': '15',
|
||||
}),
|
||||
max_length=15, # Django max_length validation
|
||||
max_length=15, #
|
||||
error_messages={
|
||||
'required': 'This field is required.',
|
||||
'max_length': 'VRN must be at most 15 characters long.',
|
||||
'required': _('This field is required.'),
|
||||
'max_length': _('VAT Registration Number must be 15 characters.'),
|
||||
}
|
||||
)
|
||||
|
||||
address = forms.CharField(
|
||||
label="Address",
|
||||
widget=forms.Textarea(attrs={
|
||||
'class': 'form-control',
|
||||
'rows': '4',
|
||||
'rows': '3',
|
||||
'required': 'required',
|
||||
}),
|
||||
error_messages={
|
||||
'required': 'This field is required.',
|
||||
}
|
||||
)
|
||||
class WizardForm3(forms.Form):
|
||||
password = forms.CharField(
|
||||
label="Password*",
|
||||
widget=forms.PasswordInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'Password',
|
||||
'required': 'required',
|
||||
}),
|
||||
error_messages={
|
||||
'required': 'This field is required.',
|
||||
}
|
||||
)
|
||||
confirm_password = forms.CharField(
|
||||
label="Confirm Password*",
|
||||
widget=forms.PasswordInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'Confirm Password',
|
||||
'required': 'required',
|
||||
}),
|
||||
error_messages={
|
||||
'required': 'This field is required.',
|
||||
'required': _('This field is required.'),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
# Generated by Django 4.2.17 on 2024-12-26 10:12
|
||||
# Generated by Django 5.1.4 on 2024-12-26 16:17
|
||||
|
||||
from decimal import Decimal
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import inventory.mixins
|
||||
import phonenumber_field.modelfields
|
||||
from decimal import Decimal
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
@ -13,8 +13,8 @@ class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
@ -66,90 +66,6 @@ class Migration(migrations.Migration):
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CarModel',
|
||||
fields=[
|
||||
('id_car_model', models.AutoField(primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('arabic_name', models.CharField(max_length=255)),
|
||||
('id_car_make', models.ForeignKey(db_column='id_car_make', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Model',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CarSerie',
|
||||
fields=[
|
||||
('id_car_serie', models.AutoField(primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('arabic_name', models.CharField(max_length=255)),
|
||||
('year_begin', models.IntegerField(blank=True, null=True)),
|
||||
('year_end', models.IntegerField(blank=True, null=True)),
|
||||
('id_car_model', models.ForeignKey(db_column='id_car_model', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Series',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CarSpecification',
|
||||
fields=[
|
||||
('id_car_specification', models.AutoField(primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('arabic_name', models.CharField(max_length=255)),
|
||||
('id_parent', models.ForeignKey(blank=True, db_column='id_parent', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carspecification')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Specification',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Customer',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('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')),
|
||||
('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')),
|
||||
('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')),
|
||||
('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Customer',
|
||||
'verbose_name_plural': 'Customers',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Dealer',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('crn', models.CharField(blank=True, max_length=10, null=True, verbose_name='Commercial Registration Number')),
|
||||
('vrn', models.CharField(blank=True, max_length=15, null=True, verbose_name='VAT Registration Number')),
|
||||
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
|
||||
('name', models.CharField(max_length=255, verbose_name='English Name')),
|
||||
('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/users', verbose_name='Logo')),
|
||||
('joined_at', models.DateTimeField(auto_now_add=True, verbose_name='Joined At')),
|
||||
('email', models.EmailField(max_length=254, unique=True, verbose_name='Email')),
|
||||
('dealer_type', models.CharField(choices=[('Owner', 'Owner'), ('Inventory', 'Inventory'), ('Accountent', 'Accountent'), ('sales', 'Sales')], default='Owner', max_length=255, verbose_name='Dealer Type')),
|
||||
('entity', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='django_ledger.entitymodel')),
|
||||
('parent_dealer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sub_dealers', to='inventory.dealer', verbose_name='Parent Dealer')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='dealer', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Dealer',
|
||||
'verbose_name_plural': 'Dealers',
|
||||
'permissions': [('change_dealer_type', 'Can change dealer type')],
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ExteriorColors',
|
||||
fields=[
|
||||
@ -178,25 +94,6 @@ class Migration(migrations.Migration):
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Organization',
|
||||
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')),
|
||||
('crn', models.CharField(max_length=15, verbose_name='Commercial Registration Number')),
|
||||
('vrn', models.CharField(max_length=15, verbose_name='VAT Registration Number')),
|
||||
('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')),
|
||||
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='organizations', to='inventory.dealer')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Organization',
|
||||
'verbose_name_plural': 'Organizations',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Payment',
|
||||
fields=[
|
||||
@ -211,30 +108,6 @@ class Migration(migrations.Migration):
|
||||
'verbose_name_plural': 'payments',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SaleQuotation',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('quotation_number', models.CharField(max_length=10, unique=True)),
|
||||
('amount', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=10, verbose_name='Amount')),
|
||||
('remarks', models.TextField(blank=True, null=True, verbose_name='Remarks')),
|
||||
('is_approved', models.BooleanField(default=False)),
|
||||
('status', models.CharField(choices=[('Draft', 'Draft'), ('Approved', 'Approved'), ('In Review', 'In Review'), ('Paid', 'Paid')], default='Draft', max_length=10, verbose_name='Status')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')),
|
||||
('posted', models.BooleanField(default=False)),
|
||||
('payment_id', models.CharField(blank=True, max_length=255, null=True, verbose_name='Payment ID')),
|
||||
('is_paid', models.BooleanField(default=False)),
|
||||
('date_draft', models.DateTimeField(blank=True, null=True, verbose_name='Draft Date')),
|
||||
('date_in_review', models.DateTimeField(blank=True, null=True, verbose_name='In Review Date')),
|
||||
('date_approved', models.DateTimeField(blank=True, null=True, verbose_name='Approved Date')),
|
||||
('date_paid', models.DateTimeField(blank=True, null=True, verbose_name='Paid Date')),
|
||||
('date_void', models.DateTimeField(blank=True, null=True, verbose_name='Void Date')),
|
||||
('date_canceled', models.DateTimeField(blank=True, null=True, verbose_name='Canceled Date')),
|
||||
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='quotations', to='inventory.customer', verbose_name='Customer')),
|
||||
('dealer', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sales', to='inventory.dealer')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Subscription',
|
||||
fields=[
|
||||
@ -257,110 +130,91 @@ class Migration(migrations.Migration):
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Vendor',
|
||||
name='CarFinance',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('crn', models.CharField(max_length=10, unique=True, verbose_name='Commercial Registration Number')),
|
||||
('vrn', models.CharField(max_length=15, unique=True, verbose_name='VAT Registration Number')),
|
||||
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
|
||||
('name', models.CharField(max_length=255, verbose_name='English Name')),
|
||||
('contact_person', models.CharField(max_length=100, verbose_name='Contact Person')),
|
||||
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')),
|
||||
('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')),
|
||||
('logo', models.ImageField(blank=True, null=True, upload_to='logos/vendors', verbose_name='Logo')),
|
||||
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vendors', to='inventory.dealer')),
|
||||
('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': 'Vendor',
|
||||
'verbose_name_plural': 'Vendors',
|
||||
'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=[
|
||||
('id_car_model', models.AutoField(primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('arabic_name', models.CharField(max_length=255)),
|
||||
('id_car_make', models.ForeignKey(db_column='id_car_make', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Model',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SubscriptionUser',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('subscription', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.subscription')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='users',
|
||||
field=models.ManyToManyField(through='inventory.SubscriptionUser', to=settings.AUTH_USER_MODEL),
|
||||
model_name='car',
|
||||
name='id_car_model',
|
||||
field=models.ForeignKey(blank=True, db_column='id_car_model', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel', verbose_name='Model'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SalesOrder',
|
||||
name='CarRegistration',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')),
|
||||
('total_amount', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Total Amount')),
|
||||
('quotation', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='sales_order', to='inventory.salequotation', verbose_name='Quotation')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SaleQuotationCar',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('quantity', models.PositiveIntegerField(default=1, verbose_name='Quantity')),
|
||||
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.car', verbose_name='Car')),
|
||||
('quotation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='quotation_cars', to='inventory.salequotation', verbose_name='Quotation')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Representative',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Name')),
|
||||
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
|
||||
('id_number', models.CharField(max_length=10, verbose_name='ID Number')),
|
||||
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')),
|
||||
('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')),
|
||||
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='representatives', to='inventory.dealer')),
|
||||
('organization', models.ManyToManyField(related_name='representatives', to='inventory.organization')),
|
||||
('plate_number', models.IntegerField(verbose_name='Plate Number')),
|
||||
('text1', models.CharField(max_length=1, verbose_name='Text 1')),
|
||||
('text2', models.CharField(max_length=1, verbose_name='Text 2')),
|
||||
('text3', models.CharField(max_length=1, verbose_name='Text 3')),
|
||||
('registration_date', models.DateTimeField(verbose_name='Registration Date')),
|
||||
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='registrations', to='inventory.car', verbose_name='Car')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Representative',
|
||||
'verbose_name_plural': 'Representatives',
|
||||
'verbose_name': 'Registration',
|
||||
'verbose_name_plural': 'Registrations',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CarSerie',
|
||||
fields=[
|
||||
('id_car_serie', models.AutoField(primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('arabic_name', models.CharField(max_length=255)),
|
||||
('year_begin', models.IntegerField(blank=True, null=True)),
|
||||
('year_end', models.IntegerField(blank=True, null=True)),
|
||||
('id_car_model', models.ForeignKey(db_column='id_car_model', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Series',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Refund',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='amount')),
|
||||
('reason', models.TextField(blank=True, verbose_name='reason')),
|
||||
('refund_date', models.DateField(auto_now_add=True, verbose_name='refund date')),
|
||||
('payment', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='refund', to='inventory.payment')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'refund',
|
||||
'verbose_name_plural': 'refunds',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='payment',
|
||||
name='quotation',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='inventory.salequotation'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='customer',
|
||||
name='dealer',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='customers', to='inventory.dealer'),
|
||||
model_name='car',
|
||||
name='id_car_serie',
|
||||
field=models.ForeignKey(blank=True, db_column='id_car_serie', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carserie', verbose_name='Series'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomCard',
|
||||
name='CarSpecification',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('custom_number', models.CharField(max_length=255, verbose_name='Custom Number')),
|
||||
('custom_date', models.DateField(verbose_name='Custom Date')),
|
||||
('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='custom_cards', to='inventory.car', verbose_name='Car')),
|
||||
('id_car_specification', models.AutoField(primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('arabic_name', models.CharField(max_length=255)),
|
||||
('id_parent', models.ForeignKey(blank=True, db_column='id_parent', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carspecification')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Custom Card',
|
||||
'verbose_name_plural': 'Custom Cards',
|
||||
'verbose_name': 'Specification',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CarTrim',
|
||||
@ -391,20 +245,66 @@ class Migration(migrations.Migration):
|
||||
'verbose_name': 'Specification Value',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='car',
|
||||
name='id_car_trim',
|
||||
field=models.ForeignKey(blank=True, db_column='id_car_trim', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.cartrim', verbose_name='Trim'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CarRegistration',
|
||||
name='CustomCard',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('plate_number', models.IntegerField(verbose_name='Plate Number')),
|
||||
('text1', models.CharField(max_length=1, verbose_name='Text 1')),
|
||||
('text2', models.CharField(max_length=1, verbose_name='Text 2')),
|
||||
('text3', models.CharField(max_length=1, verbose_name='Text 3')),
|
||||
('registration_date', models.DateTimeField(verbose_name='Registration Date')),
|
||||
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='registrations', to='inventory.car', verbose_name='Car')),
|
||||
('custom_number', models.CharField(max_length=255, verbose_name='Custom Number')),
|
||||
('custom_date', models.DateField(verbose_name='Custom Date')),
|
||||
('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='custom_cards', to='inventory.car', verbose_name='Car')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Registration',
|
||||
'verbose_name_plural': 'Registrations',
|
||||
'verbose_name': 'Custom Card',
|
||||
'verbose_name_plural': 'Custom Cards',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Dealer',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('crn', models.CharField(blank=True, max_length=10, null=True, verbose_name='Commercial Registration Number')),
|
||||
('vrn', models.CharField(blank=True, max_length=15, null=True, verbose_name='VAT Registration Number')),
|
||||
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
|
||||
('name', models.CharField(max_length=255, verbose_name='English Name')),
|
||||
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', unique=True, 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/users', verbose_name='Logo')),
|
||||
('joined_at', models.DateTimeField(auto_now_add=True, verbose_name='Joined At')),
|
||||
('email', models.EmailField(max_length=100, unique=True, verbose_name='Email')),
|
||||
('dealer_type', models.CharField(choices=[('Owner', 'Owner'), ('Inventory', 'Inventory'), ('Accountent', 'Accountent'), ('sales', 'Sales')], default='Owner', max_length=255, verbose_name='Dealer Type')),
|
||||
('entity', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='django_ledger.entitymodel')),
|
||||
('parent_dealer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sub_dealers', to='inventory.dealer', verbose_name='Parent Dealer')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='dealer', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Dealer',
|
||||
'verbose_name_plural': 'Dealers',
|
||||
'permissions': [('change_dealer_type', 'Can change dealer type')],
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Customer',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('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')),
|
||||
('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')),
|
||||
('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')),
|
||||
('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
|
||||
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='customers', to='inventory.dealer')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Customer',
|
||||
'verbose_name_plural': 'Customers',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
@ -423,56 +323,152 @@ class Migration(migrations.Migration):
|
||||
'verbose_name_plural': 'Car Locations',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CarFinance',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('cost_price', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Cost Price')),
|
||||
('selling_price', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Selling Price')),
|
||||
('discount_amount', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Discount Amount')),
|
||||
('additional_services', models.ManyToManyField(blank=True, related_name='additional_finances', to='inventory.additionalservices')),
|
||||
('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='finances', to='inventory.car')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Car Financial Details',
|
||||
'verbose_name_plural': 'Car Financial Details',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='car',
|
||||
name='dealer',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='cars', to='inventory.dealer', verbose_name='Dealer'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='car',
|
||||
name='id_car_make',
|
||||
field=models.ForeignKey(blank=True, db_column='id_car_make', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake', verbose_name='Make'),
|
||||
model_name='additionalservices',
|
||||
name='dealer',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.dealer', verbose_name='Dealer'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Organization',
|
||||
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')),
|
||||
('crn', models.CharField(max_length=15, verbose_name='Commercial Registration Number')),
|
||||
('vrn', models.CharField(max_length=15, verbose_name='VAT Registration Number')),
|
||||
('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')),
|
||||
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='organizations', to='inventory.dealer')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Organization',
|
||||
'verbose_name_plural': 'Organizations',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Refund',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='amount')),
|
||||
('reason', models.TextField(blank=True, verbose_name='reason')),
|
||||
('refund_date', models.DateField(auto_now_add=True, verbose_name='refund date')),
|
||||
('payment', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='refund', to='inventory.payment')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'refund',
|
||||
'verbose_name_plural': 'refunds',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Representative',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Name')),
|
||||
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
|
||||
('id_number', models.CharField(max_length=10, verbose_name='ID Number')),
|
||||
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')),
|
||||
('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')),
|
||||
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='representatives', to='inventory.dealer')),
|
||||
('organization', models.ManyToManyField(related_name='representatives', to='inventory.organization')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Representative',
|
||||
'verbose_name_plural': 'Representatives',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SaleQuotation',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('quotation_number', models.CharField(max_length=10, unique=True)),
|
||||
('amount', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=10, verbose_name='Amount')),
|
||||
('remarks', models.TextField(blank=True, null=True, verbose_name='Remarks')),
|
||||
('is_approved', models.BooleanField(default=False)),
|
||||
('status', models.CharField(choices=[('Draft', 'Draft'), ('Approved', 'Approved'), ('In Review', 'In Review'), ('Paid', 'Paid')], default='Draft', max_length=10, verbose_name='Status')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')),
|
||||
('posted', models.BooleanField(default=False)),
|
||||
('payment_id', models.CharField(blank=True, max_length=255, null=True, verbose_name='Payment ID')),
|
||||
('is_paid', models.BooleanField(default=False)),
|
||||
('date_draft', models.DateTimeField(blank=True, null=True, verbose_name='Draft Date')),
|
||||
('date_in_review', models.DateTimeField(blank=True, null=True, verbose_name='In Review Date')),
|
||||
('date_approved', models.DateTimeField(blank=True, null=True, verbose_name='Approved Date')),
|
||||
('date_paid', models.DateTimeField(blank=True, null=True, verbose_name='Paid Date')),
|
||||
('date_void', models.DateTimeField(blank=True, null=True, verbose_name='Void Date')),
|
||||
('date_canceled', models.DateTimeField(blank=True, null=True, verbose_name='Canceled Date')),
|
||||
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='quotations', to='inventory.customer', verbose_name='Customer')),
|
||||
('dealer', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sales', to='inventory.dealer')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='car',
|
||||
name='id_car_model',
|
||||
field=models.ForeignKey(blank=True, db_column='id_car_model', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel', verbose_name='Model'),
|
||||
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='SubscriptionUser',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('subscription', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.subscription')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='car',
|
||||
name='id_car_serie',
|
||||
field=models.ForeignKey(blank=True, db_column='id_car_serie', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carserie', verbose_name='Series'),
|
||||
model_name='subscription',
|
||||
name='users',
|
||||
field=models.ManyToManyField(through='inventory.SubscriptionUser', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='car',
|
||||
name='id_car_trim',
|
||||
field=models.ForeignKey(blank=True, db_column='id_car_trim', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.cartrim', verbose_name='Trim'),
|
||||
migrations.CreateModel(
|
||||
name='Vendor',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('crn', models.CharField(max_length=10, unique=True, verbose_name='Commercial Registration Number')),
|
||||
('vrn', models.CharField(max_length=15, unique=True, verbose_name='VAT Registration Number')),
|
||||
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
|
||||
('name', models.CharField(max_length=255, verbose_name='English Name')),
|
||||
('contact_person', models.CharField(max_length=100, verbose_name='Contact Person')),
|
||||
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')),
|
||||
('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')),
|
||||
('logo', models.ImageField(blank=True, null=True, upload_to='logos/vendors', verbose_name='Logo')),
|
||||
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vendors', to='inventory.dealer')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Vendor',
|
||||
'verbose_name_plural': 'Vendors',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='car',
|
||||
name='vendor',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='cars', to='inventory.vendor', verbose_name='Vendor'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='additionalservices',
|
||||
name='dealer',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.dealer', verbose_name='Dealer'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CarReservation',
|
||||
fields=[
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
# Generated by Django 4.2.17 on 2024-12-26 14:19
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('inventory', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='dealer',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='dealer', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
||||
17
inventory/migrations/0002_remove_dealer_email.py
Normal file
17
inventory/migrations/0002_remove_dealer_email.py
Normal file
@ -0,0 +1,17 @@
|
||||
# Generated by Django 5.1.4 on 2024-12-26 17:33
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='dealer',
|
||||
name='email',
|
||||
),
|
||||
]
|
||||
@ -1,18 +0,0 @@
|
||||
# Generated by Django 4.2.17 on 2024-12-26 15:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0002_alter_dealer_user'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='dealer',
|
||||
name='email',
|
||||
field=models.EmailField(max_length=100, null=True, unique=True, verbose_name='Email'),
|
||||
),
|
||||
]
|
||||
19
inventory/migrations/0003_alter_dealer_phone_number.py
Normal file
19
inventory/migrations/0003_alter_dealer_phone_number.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.1.4 on 2024-12-26 18:27
|
||||
|
||||
import phonenumber_field.modelfields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0002_remove_dealer_email'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='dealer',
|
||||
name='phone_number',
|
||||
field=phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number'),
|
||||
),
|
||||
]
|
||||
@ -1,25 +0,0 @@
|
||||
# Generated by Django 4.2.17 on 2024-12-26 15:51
|
||||
|
||||
from django.db import migrations, models
|
||||
import phonenumber_field.modelfields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0003_alter_dealer_email'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='dealer',
|
||||
name='email',
|
||||
field=models.EmailField(default='a@a.com', max_length=100, unique=True, verbose_name='Email'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='dealer',
|
||||
name='phone_number',
|
||||
field=phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', unique=True, verbose_name='Phone Number'),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,24 @@
|
||||
# Generated by Django 5.1.4 on 2024-12-26 23:03
|
||||
|
||||
import inventory.models
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0003_alter_dealer_phone_number'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelManagers(
|
||||
name='dealer',
|
||||
managers=[
|
||||
('objects', inventory.models.DealerUserManager()),
|
||||
],
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='dealer',
|
||||
name='entity',
|
||||
),
|
||||
]
|
||||
@ -1,21 +0,0 @@
|
||||
# Generated by Django 4.2.17 on 2024-12-26 15:57
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('inventory', '0004_alter_dealer_email_alter_dealer_phone_number'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='dealer',
|
||||
name='user',
|
||||
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='dealer', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
||||
20
inventory/migrations/0005_dealer_entity.py
Normal file
20
inventory/migrations/0005_dealer_entity.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Generated by Django 5.1.4 on 2024-12-26 23:57
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
|
||||
('inventory', '0004_alter_dealer_managers_remove_dealer_entity'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dealer',
|
||||
name='entity',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='django_ledger.entitymodel'),
|
||||
),
|
||||
]
|
||||
@ -33,11 +33,11 @@ class LocalizedNameMixin:
|
||||
return getattr(self, 'name', None)
|
||||
|
||||
|
||||
class AddDealerInstanceMixin:
|
||||
def form_valid(self, form):
|
||||
if form.is_valid():
|
||||
form.instance.dealer = self.request.user.dealer.get_root_dealer
|
||||
form.save()
|
||||
return super().form_valid(form)
|
||||
else:
|
||||
return form.errors
|
||||
# class AddDealerInstanceMixin:
|
||||
# def form_valid(self, form):
|
||||
# if form.is_valid():
|
||||
# form.instance.dealer = self.request.user.dealer.get_root_dealer
|
||||
# form.save()
|
||||
# return super().form_valid(form)
|
||||
# else:
|
||||
# return form.errors
|
||||
@ -3,7 +3,7 @@ from uuid import uuid4
|
||||
from django.conf import settings
|
||||
from django.db import models, transaction
|
||||
from django.db.models import Sum, F, Count
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.models import User, UserManager
|
||||
from django.db.models.signals import pre_save, post_save
|
||||
from django.dispatch import receiver
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
@ -29,6 +29,15 @@ from django.db.models import FloatField
|
||||
from .mixins import LocalizedNameMixin
|
||||
from django_ledger.models import EntityModel
|
||||
|
||||
|
||||
class DealerUserManager(UserManager):
|
||||
def create_user_with_dealer(self, email, password, dealer_name, arabic_name, crn, vrn, address, **extra_fields):
|
||||
user = self.create_user(email=email, password=password, **extra_fields)
|
||||
Dealer.objects.create(user=user, name=dealer_name, )
|
||||
|
||||
return user
|
||||
|
||||
|
||||
UNIT_CHOICES = (
|
||||
("Kg", _("Kg")),
|
||||
("L", _("L")),
|
||||
@ -147,10 +156,10 @@ class CarStockTypeChoices(models.TextChoices):
|
||||
|
||||
|
||||
class DEALER_TYPES(models.TextChoices):
|
||||
Owner = "Owner", _("Owner")
|
||||
Inventory = "Inventory", _("Inventory")
|
||||
Accountent = "Accountent", _("Accountent")
|
||||
Sales = "sales", _("Sales")
|
||||
OWNER = "Owner", _("Owner")
|
||||
INVENTORY = "Inventory", _("Inventory")
|
||||
ACCOUNTANT = "Accountent", _("Accountent")
|
||||
SALES = "sales", _("Sales")
|
||||
|
||||
|
||||
class AdditionalServices(models.Model, LocalizedNameMixin):
|
||||
@ -482,12 +491,15 @@ class Subscription(models.Model):
|
||||
is_active = models.BooleanField(default=True)
|
||||
def __str__(self):
|
||||
return self.plan
|
||||
|
||||
|
||||
class SubscriptionUser(models.Model):
|
||||
subscription = models.ForeignKey(Subscription, on_delete=models.CASCADE)
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
def __str__(self):
|
||||
return f"{self.subscription} - {self.user}"
|
||||
|
||||
|
||||
|
||||
class SubscriptionPlan(models.Model):
|
||||
name = models.CharField(max_length=255)
|
||||
description = models.TextField()
|
||||
@ -495,41 +507,44 @@ class SubscriptionPlan(models.Model):
|
||||
max_users = models.IntegerField() # maximum number of users per account
|
||||
def __str__(self):
|
||||
return f"{self.name} - {self.price}"
|
||||
|
||||
|
||||
# Dealer Model
|
||||
class Dealer(models.Model, LocalizedNameMixin):
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="dealer")
|
||||
crn = models.CharField(
|
||||
max_length=10, verbose_name=_("Commercial Registration Number"),null=True,blank=True
|
||||
)
|
||||
vrn = models.CharField(max_length=15, verbose_name=_("VAT Registration Number"),null=True,blank=True)
|
||||
crn = models.CharField(max_length=10,
|
||||
verbose_name=_("Commercial Registration Number")
|
||||
,null=True
|
||||
,blank=True)
|
||||
vrn = models.CharField(max_length=15,
|
||||
verbose_name=_("VAT Registration Number"),
|
||||
null=True,
|
||||
blank=True)
|
||||
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
|
||||
name = models.CharField(max_length=255, verbose_name=_("English Name"))
|
||||
phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number"),unique=True)
|
||||
address = models.CharField(
|
||||
max_length=200, blank=True, null=True, verbose_name=_("Address")
|
||||
)
|
||||
logo = models.ImageField(
|
||||
upload_to="logos/users", blank=True, null=True, verbose_name=_("Logo")
|
||||
)
|
||||
phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number"))
|
||||
address = models.CharField(max_length=200,
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name=_("Address"))
|
||||
logo = models.ImageField(upload_to="logos/users",
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name=_("Logo"))
|
||||
entity = models.ForeignKey(EntityModel, on_delete=models.SET_NULL, null=True, blank=True)
|
||||
joined_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Joined At"))
|
||||
email = models.EmailField(verbose_name="Email", unique=True, max_length=100)
|
||||
parent_dealer = models.ForeignKey(
|
||||
"self",
|
||||
on_delete=models.SET_NULL,
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name=_("Parent Dealer"),
|
||||
related_name="sub_dealers",
|
||||
)
|
||||
dealer_type = models.CharField(
|
||||
max_length=255,
|
||||
choices=DEALER_TYPES.choices,
|
||||
verbose_name=_("Dealer Type"),
|
||||
default=DEALER_TYPES.Owner,
|
||||
)
|
||||
parent_dealer = models.ForeignKey( "self",
|
||||
on_delete=models.SET_NULL,
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name=_("Parent Dealer"),
|
||||
related_name="sub_dealers",)
|
||||
dealer_type = models.CharField(max_length=255,
|
||||
choices=DEALER_TYPES.choices,
|
||||
verbose_name=_("Dealer Type"),
|
||||
default=DEALER_TYPES.OWNER,)
|
||||
objects = DealerUserManager()
|
||||
|
||||
|
||||
@property
|
||||
def get_active_plan(self):
|
||||
try:
|
||||
@ -549,9 +564,6 @@ class Dealer(models.Model, LocalizedNameMixin):
|
||||
return subscription_plan
|
||||
return None
|
||||
|
||||
|
||||
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Dealer")
|
||||
verbose_name_plural = _("Dealers")
|
||||
@ -564,21 +576,47 @@ class Dealer(models.Model, LocalizedNameMixin):
|
||||
|
||||
@property
|
||||
def get_sub_dealers(self):
|
||||
if self.dealer_type == "Owner":
|
||||
if self.dealer_type == "OWNER":
|
||||
return self.sub_dealers.all()
|
||||
return None
|
||||
|
||||
|
||||
@property
|
||||
def is_parent(self):
|
||||
return self.dealer_type == "Owner"
|
||||
return self.dealer_type == "OWNER"
|
||||
@property
|
||||
def get_root_dealer(self):
|
||||
return self.parent_dealer if self.parent_dealer else self
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def create_dealer(instance, created, *args, **kwargs):
|
||||
if created:
|
||||
Dealer.objects.create(user=instance)
|
||||
# @receiver(post_save, sender=User)
|
||||
# def create_dealer(instance, created, *args, **kwargs):
|
||||
# if created:
|
||||
# Dealer.objects.create(user=instance)
|
||||
|
||||
|
||||
# class STAFF_TYPES(models.TextChoices):
|
||||
# # Owner = "Owner", _("Owner")
|
||||
# MANAGER = "manager", _("Manager")
|
||||
# INVENTORY = "inventory", _("Inventory")
|
||||
# ACCOUNTANT = "accountant", _("Accountant")
|
||||
# SALES = "sales", _("Sales")
|
||||
# RECEPTIONIST = "receptionist", _("Receptionist")
|
||||
# TECHNICIAN = "technician", _("Technician")
|
||||
# DRIVER = "driver", _("Driver")
|
||||
#
|
||||
#
|
||||
# class Staff(models.Model, LocalizedNameMixin):
|
||||
# user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="staff")
|
||||
# dealer = models.ForeignKey(Dealer, on_delete=models.SET_NULL, null=True, blank=True)
|
||||
# name = models.CharField(max_length=255, verbose_name=_("Name"))
|
||||
# arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
|
||||
# staff_type = models.CharField(choices=STAFF_TYPES.choices, max_length=255, verbose_name=_("Staff Type"))
|
||||
# created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At"))
|
||||
# updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At"))
|
||||
#
|
||||
# class Meta:
|
||||
# verbose_name = _("Staff")
|
||||
# verbose_name_plural = _("Staff")
|
||||
# permissions = []
|
||||
|
||||
|
||||
# Vendor Model
|
||||
@ -611,9 +649,9 @@ class Vendor(models.Model, LocalizedNameMixin):
|
||||
|
||||
# Customer Model
|
||||
class Customer(models.Model):
|
||||
dealer = models.ForeignKey(
|
||||
Dealer, on_delete=models.CASCADE, related_name="customers"
|
||||
)
|
||||
dealer = models.ForeignKey(Dealer,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="customers")
|
||||
first_name = models.CharField(max_length=50, verbose_name=_("First Name"))
|
||||
middle_name = models.CharField(
|
||||
max_length=50, blank=True, null=True, verbose_name=_("Middle Name")
|
||||
@ -644,8 +682,6 @@ class Customer(models.Model):
|
||||
return f"{self.first_name} {self.middle_name} {self.last_name}"
|
||||
|
||||
|
||||
|
||||
|
||||
class Organization(models.Model, LocalizedNameMixin):
|
||||
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name='organizations')
|
||||
name = models.CharField(max_length=255, verbose_name=_("Name"))
|
||||
|
||||
@ -80,6 +80,9 @@ def elm(vin):
|
||||
|
||||
|
||||
def get_ledger_data(request):
|
||||
entity = EntityModel.objects.filter(name="Marwan2").first()
|
||||
data = entity.get_items_all()
|
||||
print(data)
|
||||
data = {}
|
||||
entity = EntityModel.objects.filter(name=request.user.dealer.name).first()
|
||||
data['bills'] = entity.get_bills()
|
||||
data['invoices'] = entity.get_invoices()
|
||||
data['income'] = entity.get_income_statement()
|
||||
return data
|
||||
@ -1,9 +1,5 @@
|
||||
from random import randint
|
||||
|
||||
from django.db.models.signals import post_save, post_delete, pre_delete, pre_save
|
||||
from django.dispatch import receiver
|
||||
from django.utils import timezone
|
||||
from django_ledger.models import EntityModel
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.contrib.auth import get_user_model
|
||||
from django_ledger.io import roles
|
||||
@ -24,14 +20,14 @@ User = get_user_model()
|
||||
# user = instance.user
|
||||
# if user:
|
||||
# user.delete()
|
||||
|
||||
#
|
||||
# @receiver(post_save, sender=User)
|
||||
# def create_dealer(instance, created, *args, **kwargs):
|
||||
# if created:
|
||||
# if created:
|
||||
# if not instance.dealer:
|
||||
|
||||
#
|
||||
# models.Dealer.objects.create(user=instance,name=instance.username,email=instance.email)
|
||||
|
||||
#
|
||||
# @receiver(post_save, sender=models.Dealer)
|
||||
# def create_user_account(sender, instance, created, **kwargs):
|
||||
# if created:
|
||||
@ -45,6 +41,7 @@ User = get_user_model()
|
||||
# instance.user = user
|
||||
# instance.save()
|
||||
|
||||
|
||||
@receiver(post_save, sender=models.Car)
|
||||
def create_car_location(sender, instance, created, **kwargs):
|
||||
"""
|
||||
@ -83,114 +80,108 @@ def update_car_status_on_reservation_delete(sender, instance, **kwargs):
|
||||
|
||||
|
||||
# Create Entity
|
||||
# @receiver(post_save, sender=models.Dealer)
|
||||
# def create_ledger_entity(sender, instance, created, **kwargs):
|
||||
# if created:
|
||||
# root_dealer = instance.get_root_dealer
|
||||
# if not root_dealer.entity:
|
||||
# entity_name = f"{root_dealer.name}-{root_dealer.joined_at.date()}"
|
||||
# entity = EntityModel.create_entity(
|
||||
# name=entity_name,
|
||||
# admin=root_dealer.user,
|
||||
# use_accrual_method=False,
|
||||
# fy_start_month=1,
|
||||
# )
|
||||
@receiver(post_save, sender=models.Dealer)
|
||||
def create_ledger_entity(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
root_dealer = instance.get_root_dealer
|
||||
if not root_dealer.entity:
|
||||
entity_name = f"{root_dealer.name}-{root_dealer.joined_at.date()}"
|
||||
entity = EntityModel.create_entity(
|
||||
name=entity_name,
|
||||
admin=root_dealer.user,
|
||||
use_accrual_method=False,
|
||||
fy_start_month=1,
|
||||
)
|
||||
|
||||
# if entity:
|
||||
# instance.entity = entity
|
||||
# instance.save()
|
||||
# coa = entity.create_chart_of_accounts(
|
||||
# assign_as_default=True, commit=True, coa_name=_(f"{entity_name}-COA")
|
||||
# )
|
||||
# if coa:
|
||||
# # entity.populate_default_coa(activate_accounts=True, coa_model=coa)
|
||||
# print(f"Ledger entity created for Dealer: {instance.name}")
|
||||
if entity:
|
||||
instance.entity = entity
|
||||
instance.save()
|
||||
coa = entity.create_chart_of_accounts(
|
||||
assign_as_default=True, commit=True, coa_name=_(f"{entity_name}-COA")
|
||||
)
|
||||
if coa:
|
||||
# entity.populate_default_coa(activate_accounts=True, coa_model=coa)
|
||||
print(f"Ledger entity created for Dealer: {instance.name}")
|
||||
|
||||
# # Create Cash Account
|
||||
# entity.create_account(
|
||||
# coa_model=coa,
|
||||
# code="1010",
|
||||
# role=roles.ASSET_CA_CASH,
|
||||
# name=_("Cash"),
|
||||
# balance_type="debit",
|
||||
# active=True,
|
||||
# )
|
||||
# Create Cash Account
|
||||
entity.create_account(
|
||||
coa_model=coa,
|
||||
code="1010",
|
||||
role=roles.ASSET_CA_CASH,
|
||||
name=_("Cash"),
|
||||
balance_type="debit",
|
||||
active=True,
|
||||
)
|
||||
|
||||
# # Create Accounts Receivable Account
|
||||
# entity.create_account(
|
||||
# coa_model=coa,
|
||||
# code="1020",
|
||||
# role=roles.ASSET_CA_RECEIVABLES,
|
||||
# name=_("Accounts Receivable"),
|
||||
# balance_type="debit",
|
||||
# active=True,
|
||||
# )
|
||||
# Create Accounts Receivable Account
|
||||
entity.create_account(
|
||||
coa_model=coa,
|
||||
code="1020",
|
||||
role=roles.ASSET_CA_RECEIVABLES,
|
||||
name=_("Accounts Receivable"),
|
||||
balance_type="debit",
|
||||
active=True,
|
||||
)
|
||||
|
||||
# # Create Inventory Account
|
||||
# entity.create_account(
|
||||
# coa_model=coa,
|
||||
# code="1030",
|
||||
# role=roles.ASSET_CA_INVENTORY,
|
||||
# name=_("Inventory"),
|
||||
# balance_type="debit",
|
||||
# active=True,
|
||||
# )
|
||||
# Create Inventory Account
|
||||
entity.create_account(
|
||||
coa_model=coa,
|
||||
code="1030",
|
||||
role=roles.ASSET_CA_INVENTORY,
|
||||
name=_("Inventory"),
|
||||
balance_type="debit",
|
||||
active=True,
|
||||
)
|
||||
|
||||
# # Create Accounts Payable Account
|
||||
# entity.create_account(
|
||||
# coa_model=coa,
|
||||
# code="2010",
|
||||
# role=roles.LIABILITY_CL_ACC_PAYABLE,
|
||||
# name=_("Accounts Payable"),
|
||||
# balance_type="credit",
|
||||
# active=True,
|
||||
# )
|
||||
# Create Accounts Payable Account
|
||||
entity.create_account(
|
||||
coa_model=coa,
|
||||
code="2010",
|
||||
role=roles.LIABILITY_CL_ACC_PAYABLE,
|
||||
name=_("Accounts Payable"),
|
||||
balance_type="credit",
|
||||
active=True,
|
||||
)
|
||||
|
||||
# # Create Sales Revenue Account
|
||||
# entity.create_account(
|
||||
# coa_model=coa,
|
||||
# code="4010",
|
||||
# role=roles.INCOME_OPERATIONAL,
|
||||
# name=_("Sales Revenue"),
|
||||
# balance_type="credit",
|
||||
# active=True,
|
||||
# )
|
||||
# Create Sales Revenue Account
|
||||
entity.create_account(
|
||||
coa_model=coa,
|
||||
code="4010",
|
||||
role=roles.INCOME_OPERATIONAL,
|
||||
name=_("Sales Revenue"),
|
||||
balance_type="credit",
|
||||
active=True,
|
||||
)
|
||||
|
||||
# # Create Cost of Goods Sold Account
|
||||
# entity.create_account(
|
||||
# coa_model=coa,
|
||||
# code="5010",
|
||||
# role=roles.COGS,
|
||||
# name=_("Cost of Goods Sold"),
|
||||
# balance_type="debit",
|
||||
# active=True,
|
||||
# )
|
||||
# Create Cost of Goods Sold Account
|
||||
entity.create_account(
|
||||
coa_model=coa,
|
||||
code="5010",
|
||||
role=roles.COGS,
|
||||
name=_("Cost of Goods Sold"),
|
||||
balance_type="debit",
|
||||
active=True,
|
||||
)
|
||||
|
||||
# # Create Rent Expense Account
|
||||
# entity.create_account(
|
||||
# coa_model=coa,
|
||||
# code="6010",
|
||||
# role=roles.EXPENSE_OPERATIONAL,
|
||||
# name=_("Rent Expense"),
|
||||
# balance_type="debit",
|
||||
# active=True,
|
||||
# )
|
||||
|
||||
# # Create Utilities Expense Account
|
||||
# entity.create_account(
|
||||
# coa_model=coa,
|
||||
# code="6020",
|
||||
# role=roles.EXPENSE_OPERATIONAL,
|
||||
# name=_("Utilities Expense"),
|
||||
# balance_type="debit",
|
||||
# active=True,
|
||||
# )
|
||||
|
||||
# uom_name = _("Unit")
|
||||
# unit_abbr = _("U")
|
||||
#
|
||||
# entity.create_uom(uom_name, unit_abbr)
|
||||
# Create Rent Expense Account
|
||||
entity.create_account(
|
||||
coa_model=coa,
|
||||
code="6010",
|
||||
role=roles.EXPENSE_OPERATIONAL,
|
||||
name=_("Rent Expense"),
|
||||
balance_type="debit",
|
||||
active=True,
|
||||
)
|
||||
|
||||
# Create Utilities Expense Account
|
||||
entity.create_account(
|
||||
coa_model=coa,
|
||||
code="6020",
|
||||
role=roles.EXPENSE_OPERATIONAL,
|
||||
name=_("Utilities Expense"),
|
||||
balance_type="debit",
|
||||
active=True,
|
||||
)
|
||||
|
||||
|
||||
# Create Vendor
|
||||
|
||||
Binary file not shown.
@ -3,13 +3,10 @@ from django import template
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter
|
||||
@register.filter(name='percentage')
|
||||
def percentage(value):
|
||||
try:
|
||||
value = float(value) * 100
|
||||
return f'{value:.0f}%'
|
||||
except (ValueError, TypeError):
|
||||
return value
|
||||
if value is not None:
|
||||
return '{0:,.2f}%'.format(value * 100)
|
||||
|
||||
|
||||
@register.filter
|
||||
|
||||
@ -14,7 +14,7 @@ urlpatterns = [
|
||||
path('login/', allauth_views.LoginView.as_view(template_name='account/login.html'), name='account_login'),
|
||||
path('logout/', allauth_views.LogoutView.as_view(template_name='account/logout.html'), name='account_logout'),
|
||||
# path('signup/', allauth_views.SignupView.as_view(template_name='account/signup.html'), name='account_signup'),
|
||||
path('signup/', views.SignupView, name='account_signup'),
|
||||
path('signup/', views.dealer_signup, name='account_signup'),
|
||||
path('password/change/',
|
||||
allauth_views.PasswordChangeView.as_view(template_name='account/password_change.html'),
|
||||
name='account_change_password'),
|
||||
@ -27,11 +27,9 @@ urlpatterns = [
|
||||
path('login/code/', allauth_views.RequestLoginCodeView.as_view(template_name='account/request_login_code.html')),
|
||||
|
||||
# Dealer URLs
|
||||
path('dealers/', views.DealerListView.as_view(), name='dealer_list'),
|
||||
path('dealers/<int:pk>/', views.DealerDetailView.as_view(), name='dealer_detail'),
|
||||
path('dealers/create/', views.DealerCreateView.as_view(), name='dealer_create'),
|
||||
path('dealers/<int:pk>/update/', views.DealerUpdateView.as_view(), name='dealer_update'),
|
||||
path('dealers/<int:pk>/delete/', views.DealerDeleteView.as_view(), name='dealer_delete'),
|
||||
# path('dealers/<int:pk>/delete/', views.DealerDeleteView.as_view(), name='dealer_delete'),
|
||||
|
||||
# Customer URLs
|
||||
path('customers/', views.CustomerListView.as_view(), name='customer_list'),
|
||||
|
||||
@ -2,15 +2,8 @@ from django_ledger.models import EntityModel, InvoiceModel
|
||||
import logging
|
||||
import json
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
from django_ledger.models import TransactionModel, AccountModel,JournalEntryModel
|
||||
from django.shortcuts import HttpResponse
|
||||
from django.template.loader import render_to_string
|
||||
# from weasyprint import HTML
|
||||
# from weasyprint.fonts import FontConfiguration
|
||||
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from vin import VIN
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import JsonResponse
|
||||
@ -29,14 +22,11 @@ from django.views.generic import (
|
||||
from django.utils import timezone, translation
|
||||
from django.conf import settings
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
from django.forms import ChoiceField, ModelForm, RadioSelect
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.contrib import messages
|
||||
from django.db.models import Sum, F, Count
|
||||
from django.db import transaction
|
||||
from inventory.mixins import AddDealerInstanceMixin
|
||||
|
||||
from .services import elm, decodevin, get_make, get_model, normalize_name
|
||||
from .services import (
|
||||
elm,
|
||||
decodevin,
|
||||
@ -46,12 +36,9 @@ from .services import (
|
||||
get_ledger_data,
|
||||
)
|
||||
from . import models, forms
|
||||
from django_tables2.export.views import ExportMixin
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||
from django.contrib.auth.decorators import user_passes_test
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.auth import get_user_model
|
||||
from .utils import get_calculations
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
@ -94,64 +81,62 @@ def switch_language(request):
|
||||
logger.warning(f"Invalid language code: {language}")
|
||||
return redirect("/")
|
||||
|
||||
def SignupView(request):
|
||||
|
||||
def dealer_signup(request, *args, **kwargs):
|
||||
if request.method == "POST":
|
||||
data = json.loads(request.body)
|
||||
wf1 = data.get("wizardValidationForm1")
|
||||
wf1 = data.get("wizardValidationForm1")
|
||||
wf2 = data.get("wizardValidationForm2")
|
||||
wf3 = data.get("wizardValidationForm3")
|
||||
name = wf1.get("name")
|
||||
arabic_name = wf1.get("arabic_name")
|
||||
email = wf1.get("email")
|
||||
email = wf1.get("email")
|
||||
phone = wf2.get("phone_number")
|
||||
crn = wf2.get("crn")
|
||||
vrn = wf2.get("vrn")
|
||||
address = wf2.get("address")
|
||||
password = wf3.get("password")
|
||||
password_confirm = wf3.get("confirm_password")
|
||||
|
||||
|
||||
if password != password_confirm:
|
||||
return JsonResponse({"error": "Passwords do not match."}, status=400)
|
||||
|
||||
|
||||
try:
|
||||
with transaction.atomic():
|
||||
user = User.objects.create(username=name, email=email)
|
||||
user = User.objects.create(email=email, password=password)
|
||||
user.set_password(password)
|
||||
user.save()
|
||||
models.Dealer.objects.create_or_update(
|
||||
user=user,
|
||||
name=name,
|
||||
arabic_name=arabic_name,
|
||||
crn=crn,
|
||||
vrn=vrn,
|
||||
email=email,
|
||||
phone_number=phone,
|
||||
address=address,
|
||||
dealer_type="Owner",
|
||||
)
|
||||
print("User created successfully.")
|
||||
models.Dealer.objects.create(user=user,
|
||||
name=name,
|
||||
arabic_name=arabic_name,
|
||||
crn=crn,
|
||||
vrn=vrn,
|
||||
phone_number=phone,
|
||||
address=address,
|
||||
dealer_type="OWNER",)
|
||||
return JsonResponse({"message": "User created successfully."}, status=200)
|
||||
except Exception as e:
|
||||
print("Error creating user:", e)
|
||||
return JsonResponse({"error": str(e)}, status=400)
|
||||
|
||||
|
||||
form1 = forms.WizardForm1()
|
||||
form2 = forms.WizardForm2()
|
||||
form3 = forms.WizardForm3()
|
||||
return render(request, "account/signup-wizard.html",{"form1": form1, "form2": form2, "form3": form3})
|
||||
|
||||
|
||||
class HomeView(LoginRequiredMixin, TemplateView):
|
||||
template_name = "dashboards/accounting.html"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if (
|
||||
not any(hasattr(request.user, attr) for attr in ["dealer", "subdealer"])
|
||||
or not request.user.is_authenticated
|
||||
not any(hasattr(request.user, attr) for attr in ["dealer", "subdealer"])
|
||||
or not request.user.is_authenticated
|
||||
):
|
||||
messages.error(request, _("You are not associated with any dealer."))
|
||||
# messages.error(request, _("You are not associated with any dealer."))
|
||||
return redirect("welcome")
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
@ -534,7 +519,7 @@ class CarLocationCreateView(CreateView):
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
||||
form.instance.owner = self.request.user.dealer
|
||||
form.instance.OWNER = self.request.user.dealer
|
||||
form.save()
|
||||
messages.success(self.request, "Car saved successfully.")
|
||||
return super().form_valid(form)
|
||||
@ -621,26 +606,12 @@ def manage_reservation(request, reservation_id):
|
||||
)
|
||||
|
||||
|
||||
class DealerListView(LoginRequiredMixin, ListView):
|
||||
model = models.Dealer
|
||||
template_name = "dealer_list.html"
|
||||
context_object_name = "dealers"
|
||||
|
||||
|
||||
class DealerDetailView(LoginRequiredMixin, DetailView):
|
||||
model = models.Dealer
|
||||
template_name = "dealers/dealer_detail.html"
|
||||
context_object_name = "dealer"
|
||||
|
||||
|
||||
class DealerCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
model = models.Dealer
|
||||
form_class = forms.DealerForm
|
||||
template_name = "dealer_form.html"
|
||||
success_url = reverse_lazy("dealer_list")
|
||||
success_message = _("Dealer created successfully.")
|
||||
|
||||
|
||||
class DealerUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
model = models.Dealer
|
||||
form_class = forms.DealerForm
|
||||
@ -658,19 +629,12 @@ class DealerUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
return form
|
||||
|
||||
def get_form_class(self):
|
||||
if self.request.user.dealer.dealer_type == "Owner":
|
||||
if self.request.user.dealer.dealer_type == "OWNER":
|
||||
return forms.DealerForm
|
||||
else:
|
||||
return forms.UserForm
|
||||
|
||||
|
||||
class DealerDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
||||
model = models.Dealer
|
||||
template_name = "dealer_confirm_delete.html"
|
||||
success_url = reverse_lazy("dealer_list")
|
||||
success_message = _("Dealer deleted successfully.")
|
||||
|
||||
|
||||
class CustomerListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||||
model = models.Customer
|
||||
home_label = _("customers")
|
||||
@ -710,7 +674,6 @@ class CustomerCreateView(
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
SuccessMessageMixin,
|
||||
AddDealerInstanceMixin,
|
||||
CreateView,
|
||||
):
|
||||
model = models.Customer
|
||||
@ -725,7 +688,6 @@ class CustomerUpdateView(
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
SuccessMessageMixin,
|
||||
AddDealerInstanceMixin,
|
||||
UpdateView,
|
||||
):
|
||||
model = models.Customer
|
||||
@ -762,7 +724,6 @@ class VendorCreateView(
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
SuccessMessageMixin,
|
||||
AddDealerInstanceMixin,
|
||||
CreateView,
|
||||
):
|
||||
model = models.Vendor
|
||||
@ -777,7 +738,6 @@ class VendorUpdateView(
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
SuccessMessageMixin,
|
||||
AddDealerInstanceMixin,
|
||||
UpdateView,
|
||||
):
|
||||
model = models.Vendor
|
||||
@ -1168,7 +1128,6 @@ class UserCreateView(
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
SuccessMessageMixin,
|
||||
AddDealerInstanceMixin,
|
||||
CreateView,
|
||||
):
|
||||
model = models.Dealer
|
||||
@ -1181,7 +1140,7 @@ class UserCreateView(
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
form.fields["dealer_type"].choices = [
|
||||
t for t in form.fields["dealer_type"].choices if t[0] != "Owner"
|
||||
t for t in form.fields["dealer_type"].choices if t[0] != "OWNER"
|
||||
]
|
||||
return form
|
||||
|
||||
@ -1211,7 +1170,6 @@ class UserUpdateView(
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
SuccessMessageMixin,
|
||||
AddDealerInstanceMixin,
|
||||
UpdateView,
|
||||
):
|
||||
model = models.Dealer
|
||||
@ -1398,7 +1356,8 @@ def download_quotation_pdf(request, quotation_id):
|
||||
return response
|
||||
except models.SaleQuotation.DoesNotExist:
|
||||
return HttpResponse("Quotation not found", status=404)
|
||||
|
||||
|
||||
|
||||
@login_required
|
||||
def invoice_detail(request,pk):
|
||||
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||
@ -1410,6 +1369,8 @@ def invoice_detail(request,pk):
|
||||
invoice = invoice_model.filter(customer=customer,date_draft=quotation.date_draft).first()
|
||||
|
||||
return redirect('quotation_detail', pk=pk)
|
||||
|
||||
|
||||
@login_required
|
||||
def payment_invoice(request,pk):
|
||||
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||
@ -1451,7 +1412,7 @@ def payment_create(request, pk):
|
||||
insatnce = form.save()
|
||||
|
||||
dealer = request.user.dealer.get_root_dealer
|
||||
entity = dealer.entity
|
||||
entity = dealer.entity
|
||||
customer = entity.get_customers().filter(customer_name=quotation.customer.get_full_name).first()
|
||||
coa_qs, coa_map = entity.get_all_coa_accounts()
|
||||
cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash")
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
BIN
static/.DS_Store
vendored
BIN
static/.DS_Store
vendored
Binary file not shown.
1
static/css/sweetalert2.min.css
vendored
Normal file
1
static/css/sweetalert2.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -22988,7 +22988,7 @@ input:-webkit-autofill {
|
||||
box-shadow: 0 0 0 30px var(--phoenix-emphasis-bg) inset;
|
||||
}
|
||||
|
||||
input::-webkit-contacts-auto-fill-button {
|
||||
input:-webkit-contacts-auto-fill-button {
|
||||
background-color: var(--phoenix-body-color);
|
||||
}
|
||||
|
||||
|
||||
BIN
static/images/logos/users/appicon_m3vX9GA.png
Normal file
BIN
static/images/logos/users/appicon_m3vX9GA.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
@ -112,7 +112,7 @@
|
||||
|
||||
return config;
|
||||
|
||||
}));
|
||||
|
||||
//# sourceMappingURL=config.js.map
|
||||
var phoenixIsRTL = window.config.config.phoenixIsRTL;
|
||||
if (phoenixIsRTL) {
|
||||
@ -127,3 +127,4 @@
|
||||
linkRTL.setAttribute('disabled', true);
|
||||
userLinkRTL.setAttribute('disabled', true);
|
||||
}
|
||||
}));
|
||||
File diff suppressed because one or more lines are too long
6
static/js/sweetalert2.all.min.js
vendored
Normal file
6
static/js/sweetalert2.all.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -754,7 +754,7 @@
|
||||
color:
|
||||
getItemFromStore('phoenixTheme') === 'dark'
|
||||
? getColor('primary')
|
||||
: getColor('primary-light')
|
||||
: getColor('primary')
|
||||
},
|
||||
data: profitData[0]
|
||||
},
|
||||
@ -772,7 +772,7 @@
|
||||
color:
|
||||
getItemFromStore('phoenixTheme') === 'dark'
|
||||
? getColor('success')
|
||||
: getColor('success-light')
|
||||
: getColor('success')
|
||||
},
|
||||
data: revenueData[0]
|
||||
},
|
||||
@ -788,7 +788,7 @@
|
||||
color:
|
||||
getItemFromStore('phoenixTheme') === 'dark'
|
||||
? getColor('info')
|
||||
: getColor('info-light')
|
||||
: getColor('info')
|
||||
},
|
||||
data: expansesData[0]
|
||||
}
|
||||
|
||||
45512
static/vendors/echarts/echarts.min.js
vendored
45512
static/vendors/echarts/echarts.min.js
vendored
File diff suppressed because one or more lines are too long
@ -6,16 +6,16 @@
|
||||
{% block title %}{{ _("Sign In") }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row flex-center min-vh-100 py-5">
|
||||
<div class="col-sm-10 col-md-8 col-lg-5 col-xl-5 col-xxl-3"><a class="d-flex flex-center text-decoration-none mb-4" href="{% url 'landing_page' %}">
|
||||
<div class="d-flex align-items-center fw-bolder fs-3 d-inline-block"><img src="{% static 'images/logos/logo.png' %}" alt="{% trans 'Haikal' %}" width="58" />
|
||||
</div>
|
||||
</a>
|
||||
<div class="text-center">
|
||||
<div class="container">
|
||||
<div class="row flex-center min-vh-50">
|
||||
<div class="col-sm-10 col-md-8 col-lg-5 col-xl-5 col-xxl-3">
|
||||
<a class="d-flex flex-center text-decoration-none mb-4" href="{% url 'landing_page' %}">
|
||||
<div class="d-flex align-items-center fw-bolder fs-3 d-inline-block"><img src="{% static 'images/logos/logo.png' %}" alt="{% trans 'home' %}" width="58" /></div>
|
||||
</a>
|
||||
<div class="text-center">
|
||||
<h3 class="text-body-highlight">{{ _("Sign In") }}</h3>
|
||||
{% if SOCIALACCOUNT_ENABLED %}
|
||||
<p class="text-body-tertiary">Get access to your account</p>
|
||||
<p class="text-body-tertiary">Login to your account</p>
|
||||
</div>
|
||||
|
||||
{% include "socialaccount/snippets/login.html" with page_layout="entrance" %}
|
||||
@ -34,7 +34,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block extra_body %}
|
||||
|
||||
@ -1,38 +1,34 @@
|
||||
{% extends "base.html" %}
|
||||
{% load crispy_forms_filters %}
|
||||
{% load i18n %}
|
||||
{% load i18n static crispy_forms_filters %}
|
||||
{% block title %}{{ _("Change Password") }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<!-- Page Header -->
|
||||
<div class="text-center mb-4">
|
||||
<h1 class="fw-bold">{{ _("Change Password") }}</h1>
|
||||
<p class="text-muted">{{ _("Ensure your account is using a strong, unique password.") }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Change Password Form -->
|
||||
<form method="post" action="{% url 'account_change_password' %}" class="needs-validation" novalidate>
|
||||
{% csrf_token %}
|
||||
{{ redirect_field }}
|
||||
{{ form|crispy }}
|
||||
|
||||
<div class="d-grid gap-2 mt-3">
|
||||
<button type="submit" class="btn btn-primary btn-lg">
|
||||
<i class="bi bi-key"></i> {{ _("Change Password") }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Forgot Password Link -->
|
||||
<div class="text-center mt-3">
|
||||
<a href="{% url 'account_reset_password' %}" class="text-decoration-none">
|
||||
{{ _("Forgot Password?") }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row min-vh-100">
|
||||
<div class="col-sm-10 col-md-8 col-lg-5 col-xl-5 col-xxl-3">
|
||||
<a class="d-flex flex-center text-decoration-none mb-4" href="{% url 'landing_page' %}">
|
||||
<div class="d-flex align-items-center fw-bolder fs-3 d-inline-block"><img src="{% static 'images/logos/logo.png' %}" alt="{% trans 'home' %}" width="58" /></div>
|
||||
</a>
|
||||
<div class="text-center">
|
||||
<h1 class="fw-bold">{{ _("Change Password") }}</h1>
|
||||
<p class="text-muted">{{ _("Ensure your account is using a strong, unique password.") }}</p>
|
||||
</div>
|
||||
<form method="post" action="{% url 'account_change_password' %}" class="needs-validation" novalidate>
|
||||
{% csrf_token %}
|
||||
{{ redirect_field }}
|
||||
{{ form|crispy }}
|
||||
<div class="text-start">
|
||||
<a href="{% url 'account_reset_password' %}" class="fs-sm-9">
|
||||
{{ _("Forgot Password?") }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="d-grid gap-2 mt-3">
|
||||
<button type="submit" class="btn btn-primary w-100 mb-3"><i class="bi bi-key"></i> {{ _("Change Password") }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@ -5,12 +5,12 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row flex-center min-vh-100 py-5">
|
||||
<div class="row min-vh-100">
|
||||
<div class="col-sm-10 col-md-8 col-lg-5 col-xl-5 col-xxl-3"><a class="d-flex flex-center text-decoration-none mb-4" href="{% url 'landing_page' %}">
|
||||
<div class="d-flex align-items-center fw-bolder fs-3 d-inline-block"><img src="{% static 'images/logos/logo.png' %}" alt="{% trans 'home' %}" width="58" />
|
||||
</div>
|
||||
</a>
|
||||
<div class="text-center mb-6">
|
||||
<div class="text-center">
|
||||
<h4 class="text-body-highlight">{{ _("Password Reset") }}</h4>
|
||||
<p class="text-body-tertiary">{{ _("Type your new password") }}</p>
|
||||
<form method="post" action="{% url 'account_reset_password' %}" class="needs-validation" novalidate>
|
||||
@ -28,9 +28,9 @@
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100 mb-3">{{ _("Reset My Password") }}</button>
|
||||
</form>
|
||||
<p class="text-muted">
|
||||
<small class="text-muted mt-4">
|
||||
{{ _("Please contact us if you have any trouble resetting your password.") }}
|
||||
</p>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,23 +1,36 @@
|
||||
{% extends "base.html" %}
|
||||
{% load crispy_forms_filters %}
|
||||
{% load i18n %}
|
||||
{% load i18n static %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="card theme-wizard mb-5" data-theme-wizard="data-theme-wizard">
|
||||
<div class="card-header bg-body-highlight pt-3 pb-2 border-bottom-0">
|
||||
<div class="container">
|
||||
<div class="row d-flex-center min-vh-50">
|
||||
<div class="col-12 "><a class="d-flex flex-center text-decoration-none mb-4" href="{% url 'landing_page' %}">
|
||||
<div class="d-flex align-items-center fw-bolder fs-3 d-inline-block">
|
||||
<img class="d-dark-none" src="{% static 'images/logos/logo.png' %}" alt="{% trans 'home' %}" width="58" />
|
||||
<img class="d-light-none" src="{% static 'images/logos/logo-d.png' %}" alt="{% trans 'home' %}" width="58" />
|
||||
</div>
|
||||
</a>
|
||||
<div class="text-center">
|
||||
<h3 class="text-body-highlight">{% trans 'Sign Up' %}</h3>
|
||||
<p class="text-body-tertiary">{% trans 'Create your account today' %}</p>
|
||||
</div>
|
||||
<div class="text-start">
|
||||
<div class="card theme-wizard" data-theme-wizard="data-theme-wizard">
|
||||
<div class="card-header pt-3 pb-2 border-bottom-0">
|
||||
<ul class="nav justify-content-between nav-wizard nav-wizard-success" role="tablist">
|
||||
<li class="nav-item" role="presentation"><a class="nav-link active fw-semibold" href="#bootstrap-wizard-validation-tab1" data-bs-toggle="tab" data-wizard-step="1" aria-selected="true" role="tab">
|
||||
<div class="text-center d-inline-block"><span class="nav-item-circle-parent"><span class="nav-item-circle"><svg class="svg-inline--fa fa-lock" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="lock" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" data-fa-i2svg=""><path fill="currentColor" d="M144 144v48H304V144c0-44.2-35.8-80-80-80s-80 35.8-80 80zM80 192V144C80 64.5 144.5 0 224 0s144 64.5 144 144v48h16c35.3 0 64 28.7 64 64V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V256c0-35.3 28.7-64 64-64H80z"></path></svg><!-- <span class="fas fa-lock"></span> Font Awesome fontawesome.com --></span></span><span class="d-none d-md-block mt-1 fs-9">Account</span></div>
|
||||
<div class="text-center d-inline-block"><span class="nav-item-circle-parent"><span class="nav-item-circle"><svg class="svg-inline--fa fa-lock" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="lock" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" data-fa-i2svg=""><path fill="currentColor" d="M144 144v48H304V144c0-44.2-35.8-80-80-80s-80 35.8-80 80zM80 192V144C80 64.5 144.5 0 224 0s144 64.5 144 144v48h16c35.3 0 64 28.7 64 64V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V256c0-35.3 28.7-64 64-64H80z"></path></svg></span></span><span class="d-none d-md-block mt-1 fs-9">{% trans 'Access' %}</span></div>
|
||||
</a></li>
|
||||
<li class="nav-item" role="presentation"><a class="nav-link fw-semibold" href="#bootstrap-wizard-validation-tab2" data-bs-toggle="tab" data-wizard-step="2" aria-selected="false" tabindex="-1" role="tab">
|
||||
<div class="text-center d-inline-block"><span class="nav-item-circle-parent"><span class="nav-item-circle"><svg class="svg-inline--fa fa-user" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="user" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" data-fa-i2svg=""><path fill="currentColor" d="M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512H418.3c16.4 0 29.7-13.3 29.7-29.7C448 383.8 368.2 304 269.7 304H178.3z"></path></svg><!-- <span class="fas fa-user"></span> Font Awesome fontawesome.com --></span></span><span class="d-none d-md-block mt-1 fs-9">Personal</span></div>
|
||||
<div class="text-center d-inline-block"><span class="nav-item-circle-parent"><span class="nav-item-circle"><svg class="svg-inline--fa fa-user" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="user" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" data-fa-i2svg=""><path fill="currentColor" d="M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512H418.3c16.4 0 29.7-13.3 29.7-29.7C448 383.8 368.2 304 269.7 304H178.3z"></path></svg></span></span><span class="d-none d-md-block mt-1 fs-9">{% trans 'Account' %}</span></div>
|
||||
</a></li>
|
||||
<li class="nav-item" role="presentation"><a class="nav-link fw-semibold" href="#bootstrap-wizard-validation-tab3" data-bs-toggle="tab" data-wizard-step="3" aria-selected="false" tabindex="-1" role="tab">
|
||||
<div class="text-center d-inline-block"><span class="nav-item-circle-parent"><span class="nav-item-circle"><svg class="svg-inline--fa fa-file-lines" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="file-lines" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" data-fa-i2svg=""><path fill="currentColor" d="M64 0C28.7 0 0 28.7 0 64V448c0 35.3 28.7 64 64 64H320c35.3 0 64-28.7 64-64V160H256c-17.7 0-32-14.3-32-32V0H64zM256 0V128H384L256 0zM112 256H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16zm0 64H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16zm0 64H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16z"></path></svg><!-- <span class="fas fa-file-alt"></span> Font Awesome fontawesome.com --></span></span><span class="d-none d-md-block mt-1 fs-9">Password</span></div>
|
||||
<div class="text-center d-inline-block"><span class="nav-item-circle-parent"><span class="nav-item-circle"><svg class="svg-inline--fa fa-file-lines" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="file-lines" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" data-fa-i2svg=""><path fill="currentColor" d="M64 0C28.7 0 0 28.7 0 64V448c0 35.3 28.7 64 64 64H320c35.3 0 64-28.7 64-64V160H256c-17.7 0-32-14.3-32-32V0H64zM256 0V128H384L256 0zM112 256H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16zm0 64H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16zm0 64H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16z"></path></svg></span></span><span class="d-none d-md-block mt-1 fs-9">{% trans 'Extra' %}</span></div>
|
||||
</a></li>
|
||||
<li class="nav-item" role="presentation"><a class="nav-link fw-semibold" href="#bootstrap-wizard-validation-tab4" data-bs-toggle="tab" data-wizard-step="4" aria-selected="false" tabindex="-1" role="tab">
|
||||
<div class="text-center d-inline-block"><span class="nav-item-circle-parent"><span class="nav-item-circle"><svg class="svg-inline--fa fa-check" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="check" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" data-fa-i2svg=""><path fill="currentColor" d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"></path></svg><!-- <span class="fas fa-check"></span> Font Awesome fontawesome.com --></span></span><span class="d-none d-md-block mt-1 fs-9">Done</span></div>
|
||||
<div class="text-center d-inline-block"><span class="nav-item-circle-parent"><span class="nav-item-circle"><svg class="svg-inline--fa fa-check" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="check" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" data-fa-i2svg=""><path fill="currentColor" d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"></path></svg></span></span><span class="d-none d-md-block mt-1 fs-9">{% trans 'Done' %}</span></div>
|
||||
</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -41,12 +54,12 @@
|
||||
<div class="tab-pane" role="tabpanel" aria-labelledby="bootstrap-wizard-validation-tab4" id="bootstrap-wizard-validation-tab4">
|
||||
<div class="row flex-center pb-8 pt-4 gx-3 gy-4">
|
||||
<div class="col-12 col-sm-auto">
|
||||
<div class="text-center text-sm-start"><img class="d-dark-none" src="../../assets/img/spot-illustrations/38.webp" alt="" width="220"><img class="d-light-none" src="../../assets/img/spot-illustrations/dark_38.webp" alt="" width="220"></div>
|
||||
<div class="text-center text-sm-start"><img class="d-dark-none" src="{% static 'images/spot-illustrations/38.webp' %}" alt="" width="220"><img class="d-light-none" src="{% static 'images/spot-illustrations/dark_38.webp' %}" alt="" width="220"></div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-auto">
|
||||
<div class="text-center text-sm-start">
|
||||
<h5 class="mb-3">You are all set!</h5>
|
||||
<p class="text-body-emphasis fs-9">Now you can access your account<br>anytime anywhere</p><button class="btn btn-primary px-6" id='submit_btn'>Submit</button>
|
||||
<h5 class="mb-3">{% trans 'You are all set!' %}</h5>
|
||||
<p class="text-body-emphasis fs-9">{% trans 'Now you can access your account' %}<br>{% trans 'anytime' %} {% trans 'anywhere' %}</p><button class="btn btn-primary px-6" id='submit_btn'>{% trans 'Submit' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -55,11 +68,104 @@
|
||||
</div>
|
||||
<div class="card-footer border-top-0" data-wizard-footer="data-wizard-footer">
|
||||
<div class="d-flex pager wizard list-inline mb-0">
|
||||
<button class="d-none btn btn-link ps-0" type="button" data-wizard-prev-btn="data-wizard-prev-btn"><svg class="svg-inline--fa fa-chevron-left me-1" data-fa-transform="shrink-3" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-left" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" data-fa-i2svg="" style="transform-origin: 0.3125em 0.5em;"><g transform="translate(160 256)"><g transform="translate(0, 0) scale(0.8125, 0.8125) rotate(0 0 0)"><path fill="currentColor" d="M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l192 192c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256 246.6 86.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-192 192z" transform="translate(-160 -256)"></path></g></g></svg><!-- <span class="fas fa-chevron-left me-1" data-fa-transform="shrink-3"></span> Font Awesome fontawesome.com -->Previous</button>
|
||||
<button class="d-none btn btn-link ps-0" type="button" data-wizard-prev-btn="data-wizard-prev-btn">{% trans 'Previous' %}</button>
|
||||
<div class="flex-1 text-end">
|
||||
<button class="btn btn-primary px-6 px-sm-6" type="submit" data-wizard-next-btn="data-wizard-next-btn">Next<svg class="svg-inline--fa fa-chevron-right ms-1" data-fa-transform="shrink-3" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-right" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" data-fa-i2svg="" style="transform-origin: 0.3125em 0.5em;"><g transform="translate(160 256)"><g transform="translate(0, 0) scale(0.8125, 0.8125) rotate(0 0 0)"><path fill="currentColor" d="M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L242.7 256 73.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z" transform="translate(-160 -256)"></path></g></g></svg><!-- <span class="fas fa-chevron-right ms-1" data-fa-transform="shrink-3"> </span> Font Awesome fontawesome.com --></button>
|
||||
<button class="btn btn-primary px-6 px-sm-6" type="submit" data-wizard-next-btn="data-wizard-next-btn">{% trans 'Next' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
const cookies = document.cookie.split(';');
|
||||
for (let cookie of cookies) {
|
||||
cookie = cookie.trim();
|
||||
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
}
|
||||
|
||||
const url = "{% url 'account_signup' %}";
|
||||
let submit_btn = document.getElementById('submit_btn');
|
||||
const csrfToken = getCookie('csrftoken');
|
||||
|
||||
submit_btn.addEventListener('click', async () => {
|
||||
const allFormData = getAllFormData();
|
||||
console.log(allFormData);
|
||||
|
||||
try {
|
||||
showLoading();
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRFToken': csrfToken,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(allFormData),
|
||||
});
|
||||
hideLoading();
|
||||
const data = await response.json();
|
||||
if (response.ok) {
|
||||
notify("success","Account created successfully");
|
||||
} else {
|
||||
notify("error",data.error);
|
||||
}
|
||||
} catch (error) {
|
||||
notify("error",error);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function getAllFormData() {
|
||||
const forms = document.querySelectorAll('form');
|
||||
const formData = {};
|
||||
|
||||
forms.forEach((form, index) => {
|
||||
const formId = form.id || `form${index + 1}`;
|
||||
formData[formId] = {};
|
||||
|
||||
const formElements = form.elements;
|
||||
for (let element of formElements) {
|
||||
if (element.name) {
|
||||
formData[formId][element.name] = element.value;
|
||||
}
|
||||
}
|
||||
});
|
||||
return formData;
|
||||
}
|
||||
|
||||
function showLoading() {
|
||||
Swal.fire({
|
||||
title: "{% trans 'Please Wait' %}",
|
||||
text: "{% trans 'Loading' %}...",
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => {
|
||||
Swal.showLoading();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function hideLoading() {
|
||||
Swal.close();
|
||||
}
|
||||
|
||||
function notify(tag,msg){
|
||||
Swal.fire({
|
||||
icon: tag,
|
||||
titleText: msg
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock content %}
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row flex-center min-vh-100 py-5">
|
||||
<div class="row min-vh-100 text-center">
|
||||
<div class="col-sm-10 col-md-8 col-lg-5 col-xl-5 col-xxl-3"><a class="d-flex flex-center text-decoration-none mb-4" href="{% url 'landing_page' %}">
|
||||
<div class="d-flex align-items-center fw-bolder fs-3 d-inline-block"><img src="{% static 'images/logos/logo.png' %}" alt="{% trans 'Haikal' %}" width="58" />
|
||||
</div>
|
||||
@ -69,7 +69,7 @@
|
||||
<div class="form-check mb-3">
|
||||
|
||||
<input class="form-check-input" id="termsService" type="checkbox" />
|
||||
<label class="form-label fs-9 text-transform-none" for="termsService">I accept the <a href="#!">terms </a>and <a href="#!">privacy policy</a></label>
|
||||
<label class="form-label fs-9 text-transform-none" for="termsService">I accept the <a href="">terms </a>and <a href="">privacy policy</a></label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100 mb-3">{{ _("Sign Up") }}</button>
|
||||
<div class="text-center">{% trans 'Already have an account?' %}<a class="fw-bold" href="{% url 'account_login' %}"> {{ _("Sign In") }}</a></div>
|
||||
|
||||
@ -13,15 +13,9 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="Haikal - The Backbone of Car Qar: An innovative car inventory management system designed to streamline dealership operations. Manage inventory, sales, transfers, and accounting seamlessly with advanced analytics and intuitive tools. Inspired by Arabic origins, Haikal empowers businesses with precision and efficiency.">
|
||||
|
||||
<!-- ===============================================-->
|
||||
<!-- Document Title-->
|
||||
<!-- ===============================================-->
|
||||
<title>{% block title %}{% trans 'HAIKAL' %}{% endblock %}</title>
|
||||
|
||||
|
||||
<!-- ===============================================-->
|
||||
<!-- Favicons-->
|
||||
<!-- ===============================================-->
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{% static 'images/favicons/apple-touch-icon.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{% static 'images/favicons/favicon-32x32.png' %}">
|
||||
@ -30,8 +24,11 @@
|
||||
<link rel="manifest" href="{% static 'images/favicons/manifest.json' %}">
|
||||
<meta name="msapplication-TileImage" content="{% static 'images/logos/logo-d.png' %}">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<script src="{% static 'vendors/simplebar/simplebar.min.js' %}"></script>
|
||||
<script src="{% static 'js/config.js' %}"></script>
|
||||
<script src="{% static 'js/config.js' %}"></script>
|
||||
<script src="{% static 'js/sweetalert2.all.min.js' %}"></script>
|
||||
|
||||
|
||||
|
||||
<!-- ===============================================-->
|
||||
@ -45,7 +42,7 @@
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;600;700;800;900&display=swap" rel="stylesheet">
|
||||
<link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/sweetalert2@11.15.3/dist/sweetalert2.min.css" rel="stylesheet">
|
||||
<link href="{% static 'css/sweetalert2.min.css' %}" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css">
|
||||
{% if LANGUAGE_CODE == 'en' %}
|
||||
<link href="{% static 'css/theme.min.css' %}" type="text/css" rel="stylesheet" id="style-default">
|
||||
@ -57,8 +54,8 @@
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
{% include 'messages.html' %}
|
||||
<main class="main" id="top">
|
||||
<nav class="navbar navbar-vertical navbar-expand-lg">
|
||||
<div class="collapse navbar-collapse" id="navbarVerticalCollapse">
|
||||
@ -103,9 +100,9 @@
|
||||
</li>
|
||||
<hr class="my-0" />
|
||||
<li class="nav-item">
|
||||
|
||||
<!-- label-->
|
||||
<p class="navbar-vertical-label">Apps
|
||||
</p>
|
||||
<p class="navbar-vertical-label">Apps</p>
|
||||
<hr class="navbar-vertical-line" />
|
||||
<!-- parent pages-->
|
||||
<div class="nav-item-wrapper"><a class="nav-link dropdown-indicator label-1" href="#nv-inventory" role="button" data-bs-toggle="collapse" aria-expanded="false" aria-controls="nv-inventory">
|
||||
@ -254,14 +251,9 @@
|
||||
<button class="btn navbar-toggler navbar-toggler-humburger-icon hover-bg-transparent" type="button" data-bs-toggle="collapse" data-bs-target="#navbarVerticalCollapse" aria-controls="navbarVerticalCollapse" aria-expanded="false" aria-label="Toggle Navigation"><span class="navbar-toggle-icon"><span class="toggle-line"></span></span></button>
|
||||
<a class="navbar-brand me-1 me-sm-3" href="{% url 'landing_page' %}">
|
||||
<div class="d-flex align-items-center">
|
||||
{% if data_bs_theme == 'dark' %}
|
||||
<img src="{% static 'images/logos/logo.png' %}" alt="haikal" width="27" />
|
||||
{% else %}
|
||||
<img src="{% static 'images/logos/logo-d.png' %}" alt="haikal" width="27" />
|
||||
{% endif %}
|
||||
<img class="d-dark-none" src="{% static 'images/logos/logo.png' %}" alt="haikal" width="27" /><img class="d-light-none" src="{% static 'images/logos/logo-d.png' %}" alt="haikal" width="27" />
|
||||
<h5 class="logo-text ms-2 d-none d-sm-block">{% trans 'Haikal' %}</h5>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@ -472,11 +464,10 @@
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
<div class="content">
|
||||
|
||||
{% include 'messages.html' %}
|
||||
{% block content %}
|
||||
<!-- Main content goes here -->
|
||||
{% endblock %}
|
||||
@ -493,146 +484,7 @@
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="modal fade" id="searchBoxModal" tabindex="-1" aria-hidden="true" data-bs-backdrop="true" data-phoenix-modal="data-phoenix-modal" style="--phoenix-backdrop-opacity: 1;">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content mt-15 rounded-pill">
|
||||
<div class="modal-body p-0">
|
||||
<div class="search-box navbar-top-search-box" data-list='{"valueNames":["title"]}' style="width: auto;">
|
||||
<form class="position-relative" data-bs-toggle="search" data-bs-display="static">
|
||||
<input class="form-control search-input fuzzy-search rounded-pill form-control-lg" type="search" placeholder="Search..." aria-label="Search" />
|
||||
<span class="fas fa-search search-box-icon"></span>
|
||||
|
||||
</form>
|
||||
<div class="btn-close position-absolute end-0 top-50 translate-middle cursor-pointer shadow-none" data-bs-dismiss="search">
|
||||
<button class="btn btn-link p-0" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="dropdown-menu border start-0 py-0 overflow-hidden w-100">
|
||||
<div class="scrollbar-overlay" style="max-height: 30rem;">
|
||||
<div class="list pb-3">
|
||||
<h6 class="dropdown-header text-body-highlight fs-10 py-2">24 <span class="text-body-quaternary">results</span></h6>
|
||||
<hr class="my-0" />
|
||||
<h6 class="dropdown-header text-body-highlight fs-9 border-bottom border-translucent py-2 lh-sm">Recently Searched </h6>
|
||||
<div class="py-2"><a class="dropdown-item" href="../apps/e-commerce/landing/product-details.html">
|
||||
<div class="d-flex align-items-center">
|
||||
|
||||
<div class="fw-normal text-body-highlight title"><span class="fa-solid fa-clock-rotate-left" data-fa-transform="shrink-2"></span> Store Macbook</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item" href="../apps/e-commerce/landing/product-details.html">
|
||||
<div class="d-flex align-items-center">
|
||||
|
||||
<div class="fw-normal text-body-highlight title"> <span class="fa-solid fa-clock-rotate-left" data-fa-transform="shrink-2"></span> MacBook Air - 13″</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<hr class="my-0" />
|
||||
<h6 class="dropdown-header text-body-highlight fs-9 border-bottom border-translucent py-2 lh-sm">Products</h6>
|
||||
<div class="py-2"><a class="dropdown-item py-2 d-flex align-items-center" href="../apps/e-commerce/landing/product-details.html">
|
||||
<div class="file-thumbnail me-2"><img class="h-100 w-100 object-fit-cover rounded-3" src="{% static 'images/products/60x60/3.png' %}" alt="" /></div>
|
||||
<div class="flex-1">
|
||||
<h6 class="mb-0 text-body-highlight title">MacBook Air - 13″</h6>
|
||||
<p class="fs-10 mb-0 d-flex text-body-tertiary"><span class="fw-medium text-body-tertiary text-opactity-85">8GB Memory - 1.6GHz - 128GB Storage</span></p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item py-2 d-flex align-items-center" href="../apps/e-commerce/landing/product-details.html">
|
||||
<div class="file-thumbnail me-2"><img class="img-fluid" src="{% static 'images/products/60x60/3.png' %}" alt="" /></div>
|
||||
<div class="flex-1">
|
||||
<h6 class="mb-0 text-body-highlight title">MacBook Pro - 13″</h6>
|
||||
<p class="fs-10 mb-0 d-flex text-body-tertiary"><span class="fw-medium text-body-tertiary text-opactity-85">30 Sep at 12:30 PM</span></p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<hr class="my-0" />
|
||||
<h6 class="dropdown-header text-body-highlight fs-9 border-bottom border-translucent py-2 lh-sm">Quick Links</h6>
|
||||
<div class="py-2"><a class="dropdown-item" href="../apps/e-commerce/landing/product-details.html">
|
||||
<div class="d-flex align-items-center">
|
||||
|
||||
<div class="fw-normal text-body-highlight title"><span class="fa-solid fa-link text-body" data-fa-transform="shrink-2"></span> Support MacBook House</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item" href="../apps/e-commerce/landing/product-details.html">
|
||||
<div class="d-flex align-items-center">
|
||||
|
||||
<div class="fw-normal text-body-highlight title"> <span class="fa-solid fa-link text-body" data-fa-transform="shrink-2"></span> Store MacBook″</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<hr class="my-0" />
|
||||
<h6 class="dropdown-header text-body-highlight fs-9 border-bottom border-translucent py-2 lh-sm">Files</h6>
|
||||
<div class="py-2"><a class="dropdown-item" href="../apps/e-commerce/landing/product-details.html">
|
||||
<div class="d-flex align-items-center">
|
||||
|
||||
<div class="fw-normal text-body-highlight title"><span class="fa-solid fa-file-zipper text-body" data-fa-transform="shrink-2"></span> Library MacBook folder.rar</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item" href="../apps/e-commerce/landing/product-details.html">
|
||||
<div class="d-flex align-items-center">
|
||||
|
||||
<div class="fw-normal text-body-highlight title"> <span class="fa-solid fa-file-lines text-body" data-fa-transform="shrink-2"></span> Feature MacBook extensions.txt</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item" href="../apps/e-commerce/landing/product-details.html">
|
||||
<div class="d-flex align-items-center">
|
||||
|
||||
<div class="fw-normal text-body-highlight title"> <span class="fa-solid fa-image text-body" data-fa-transform="shrink-2"></span> MacBook Pro_13.jpg</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<hr class="my-0" />
|
||||
<h6 class="dropdown-header text-body-highlight fs-9 border-bottom border-translucent py-2 lh-sm">Members</h6>
|
||||
<div class="py-2"><a class="dropdown-item py-2 d-flex align-items-center" href="../pages/members.html">
|
||||
<div class="avatar avatar-l status-online me-2 text-body">
|
||||
<img class="rounded-circle " src="{% static 'images/team/40x40/10.webp' %}" alt="" />
|
||||
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<h6 class="mb-0 text-body-highlight title">Carry Anna</h6>
|
||||
<p class="fs-10 mb-0 d-flex text-body-tertiary">anna@technext.it</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item py-2 d-flex align-items-center" href="../pages/members.html">
|
||||
<div class="avatar avatar-l me-2 text-body">
|
||||
<img class="rounded-circle " src="{% static 'images/team/40x40/12.webp' %}" alt="" />
|
||||
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<h6 class="mb-0 text-body-highlight title">John Smith</h6>
|
||||
<p class="fs-10 mb-0 d-flex text-body-tertiary">smith@technext.it</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<hr class="my-0" />
|
||||
<h6 class="dropdown-header text-body-highlight fs-9 border-bottom border-translucent py-2 lh-sm">Related Searches</h6>
|
||||
<div class="py-2"><a class="dropdown-item" href="../apps/e-commerce/landing/product-details.html">
|
||||
<div class="d-flex align-items-center">
|
||||
|
||||
<div class="fw-normal text-body-highlight title"><span class="fa-brands fa-firefox-browser text-body" data-fa-transform="shrink-2"></span> Search in the Web MacBook</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item" href="../apps/e-commerce/landing/product-details.html">
|
||||
<div class="d-flex align-items-center">
|
||||
|
||||
<div class="fw-normal text-body-highlight title"> <span class="fa-brands fa-chrome text-body" data-fa-transform="shrink-2"></span> Store MacBook″</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="fallback fw-bold fs-7 d-none">No Result Found.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="support-chat-container">
|
||||
<div class="container-fluid support-chat">
|
||||
@ -681,127 +533,11 @@
|
||||
<!-- ===============================================-->
|
||||
<!-- End of Main Content-->
|
||||
<!-- ===============================================-->
|
||||
<script>
|
||||
|
||||
|
||||
<script src="
|
||||
https://cdn.jsdelivr.net/npm/sweetalert2@11.15.3/dist/sweetalert2.all.min.js
|
||||
"></script>
|
||||
<script>
|
||||
const Toast = Swal.mixin({
|
||||
toast: true,
|
||||
position: "top-end",
|
||||
showConfirmButton: false,
|
||||
timer: 3000,
|
||||
timerProgressBar: true,
|
||||
didOpen: (toast) => {
|
||||
toast.onmouseenter = Swal.stopTimer;
|
||||
toast.onmouseleave = Swal.resumeTimer;
|
||||
}
|
||||
});
|
||||
function notify(tag,msg){
|
||||
Toast.fire({
|
||||
icon: tag,
|
||||
titleText: msg
|
||||
});
|
||||
}
|
||||
var navbarTopStyle = window.config.config.phoenixNavbarTopStyle;
|
||||
var navbarTop = document.querySelector('.navbar-top');
|
||||
if (navbarTopStyle === 'darker') {
|
||||
navbarTop.setAttribute('data-navbar-appearance', 'darker');
|
||||
}
|
||||
|
||||
var navbarVerticalStyle = window.config.config.phoenixNavbarVerticalStyle;
|
||||
var navbarVertical = document.querySelector('.navbar-vertical');
|
||||
if (navbarVertical && navbarVerticalStyle === 'darker') {
|
||||
navbarVertical.setAttribute('data-navbar-appearance', 'darker');
|
||||
}
|
||||
function save_as_pdf(){
|
||||
const quotationHtml = document.getElementById('quotation-html').outerHTML;
|
||||
const printWindow = window.open('', '', 'height=500,width=800');
|
||||
printWindow.document.write(quotationHtml);
|
||||
printWindow.document.close();
|
||||
printWindow.print();
|
||||
printWindow.close();
|
||||
}
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
const cookies = document.cookie.split(';');
|
||||
for (let cookie of cookies) {
|
||||
cookie = cookie.trim();
|
||||
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
}
|
||||
|
||||
let submit_btn = document.getElementById('submit_btn');
|
||||
const csrfToken = getCookie('csrftoken');
|
||||
|
||||
submit_btn.addEventListener('click', async () => {
|
||||
const allFormData = getAllFormData();
|
||||
console.log(allFormData);
|
||||
|
||||
try {
|
||||
showLoading();
|
||||
const response = await fetch('http://10.10.1.120:8888/en/signup/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRFToken': csrfToken,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(allFormData),
|
||||
});
|
||||
hideLoading();
|
||||
const data = await response.json();
|
||||
if (response.ok) {
|
||||
notify("success","Account created successfully");
|
||||
} else {
|
||||
notify("error",data.error);
|
||||
}
|
||||
} catch (error) {
|
||||
notify("error",error);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function getAllFormData() {
|
||||
const forms = document.querySelectorAll('form');
|
||||
const formData = {};
|
||||
|
||||
forms.forEach((form, index) => {
|
||||
const formId = form.id || `form${index + 1}`;
|
||||
formData[formId] = {};
|
||||
|
||||
|
||||
const formElements = form.elements;
|
||||
for (let element of formElements) {
|
||||
if (element.name) {
|
||||
formData[formId][element.name] = element.value;
|
||||
}
|
||||
}
|
||||
});
|
||||
return formData;
|
||||
}
|
||||
|
||||
function showLoading() {
|
||||
Swal.fire({
|
||||
title: "{% trans 'Please Wait' %}",
|
||||
text: "{% trans 'Loading' %}...",
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => {
|
||||
Swal.showLoading();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function hideLoading() {
|
||||
Swal.close();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<!-- ===============================================-->
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -4,10 +4,7 @@
|
||||
{% block content %}
|
||||
|
||||
<div class="container">
|
||||
<!-- ============================================-->
|
||||
<!-- <section> begin ============================-->
|
||||
<section class="py-0">
|
||||
|
||||
<div class="container-small">
|
||||
<div class="ecommerce-topbar">
|
||||
|
||||
@ -16,15 +13,7 @@
|
||||
<!-- end of .container-->
|
||||
|
||||
</section>
|
||||
<!-- <section> close ============================-->
|
||||
<!-- ============================================-->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- ============================================-->
|
||||
<!-- <section> begin ============================-->
|
||||
<section class="pt-5 pb-9">
|
||||
|
||||
<div class="container-small">
|
||||
@ -37,7 +26,7 @@
|
||||
<div class="row g-2 g-sm-3">
|
||||
|
||||
<div class="col-auto">
|
||||
<button class="btn btn-phoenix-secondary"><span class="fas fa-key me-2"></span>Reset password</button>
|
||||
<a href="{% url 'account_change_password' %}" class="btn btn-phoenix-secondary"><span class="fas fa-key me-2"></span>{{ _("Change Password") }}</a>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<a class="btn btn-phoenix-secondary " href="#!"><span class="fa-solid fa-user-gear me-2 mb-2 mb-xxl-0"></span>Settings </a>
|
||||
@ -144,7 +133,7 @@
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="mb-5 mb-md-0 mb-lg-5 me-3">
|
||||
<div class="d-sm-flex d-md-block d-lg-flex align-items-center mb-3">
|
||||
<h3 class="mb-0">{{ dealer.get_active_plan.plan }}</h3><span class="badge ms-sm-3 ms-md-0 ms-lg-3 fs-10 text-bg-warning">{% trans 'most valuable'|upper %}</span>
|
||||
<h3 class="mb-0">{{ dealer.get_active_plan.plan|capfirst }}</h3><span class="badge ms-sm-3 ms-md-0 ms-lg-3 fs-10 text-bg-warning">{% trans 'most valuable'|upper %}</span>
|
||||
</div>
|
||||
<p class="fs-9 text-body-tertiary">{% trans 'Active until' %}: {{ dealer.get_active_plan.end_date|date}}</p>
|
||||
<div class="d-flex align-items-end mb-md-5 mb-lg-0">
|
||||
@ -156,9 +145,9 @@
|
||||
<div class="row flex-1 justify-content-end">
|
||||
<div class="col-sm-8 col-md-12">
|
||||
<div class="d-sm-flex d-md-block d-lg-flex justify-content-end align-items-end h-100">
|
||||
<ul class="list-unstyled mb-0 border-start-sm border-start-md-0 border-start-lg ps-sm-5 ps-md-0 ps-lg-5 border-warning-subtle">
|
||||
<li class="d-flex align-items-center"><span class="uil uil-check-circle text-success me-2"></span><span class="text-body-tertiary fw-semibold">{{ dealer.get_plan.description}}</span></li>
|
||||
</ul>
|
||||
<div class="list-unstyled mb-0 border-start-sm border-start-md-0 border-start-lg ps-sm-5 ps-md-0 ps-lg-5 border-warning-subtle">
|
||||
<div class="d-flex align-items-center"><span class="uil uil-check-circle text-success me-2"></span><span class="text-body-tertiary fw-semibold">{{ dealer.get_plan.description}}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -51,7 +51,7 @@ function getCookie(name) {
|
||||
return cookieValue;
|
||||
}
|
||||
|
||||
const csrfToken = getCookie('csrftoken');
|
||||
const csrfToken = getCookie('token');
|
||||
|
||||
|
||||
async function sendMessage() {
|
||||
|
||||
@ -11,44 +11,40 @@
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="Haikal - The Backbone of Car Qar: An innovative car inventory management system designed to streamline dealership operations. Manage inventory, sales, transfers, and accounting seamlessly with advanced analytics and intuitive tools. Inspired by Arabic origins, Haikal empowers businesses with precision and efficiency.">
|
||||
|
||||
|
||||
<!-- ===============================================-->
|
||||
<!-- Document Title-->
|
||||
<!-- ===============================================-->
|
||||
<title>{% block title %}{% trans 'HAIKAL' %}{% endblock %}</title>
|
||||
|
||||
|
||||
<!-- ===============================================-->
|
||||
<!-- Favicons-->
|
||||
<!-- ===============================================-->
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{% static 'images/favicons/apple-touch-icon.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{% static 'images/favicons/favicon-32x32.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="{% static 'images/favicons/favicon-16x16.png' %}">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="{% static 'images/favicons/favicon.ico' %}">
|
||||
<link rel="manifest" href="{% static 'images/favicons/manifest.json' %}">
|
||||
<meta name="msapplication-TileImage" content="{% static 'images/favicons/mstile-150x150.png' %}">
|
||||
<meta name="msapplication-TileImage" content="{% static 'images/logos/logo-d.png' %}">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<script src="{% static 'vendors/simplebar/simplebar.min.js' %}"></script>
|
||||
<script src="{% static 'js/config.js' %}"></script>
|
||||
<script src="{% static 'js/config.js' %}"></script>
|
||||
<script src="{% static 'js/sweetalert2.all.min.js' %}"></script>
|
||||
|
||||
|
||||
<!-- ===============================================-->
|
||||
<!-- Stylesheets-->
|
||||
<!-- ===============================================-->
|
||||
<link href="{% static 'vendors/mapbox-gl/mapbox-gl.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'vendors/swiper/swiper-bundle.min.css' %}" rel="stylesheet">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;600;700;800;900&display=swap" rel="stylesheet">
|
||||
<link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'css/sweetalert2.min.css' %}" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css">
|
||||
{% if LANGUAGE_CODE == 'ar' %}
|
||||
<link href="{% static 'css/theme-rtl.min.css' %}" type="text/css" rel="stylesheet" id="style-rtl">
|
||||
<link href="{% static 'css/user-rtl.min.css' %}" type="text/css" rel="stylesheet" id="user-style-rtl">
|
||||
{% else %}
|
||||
<link href="{% static 'css/theme.min.css' %}" type="text/css" rel="stylesheet" id="style-default">
|
||||
<link href="{% static 'css/user.min.css' %}" type="text/css" rel="stylesheet" id="user-style-default">
|
||||
{% endif %}
|
||||
{% if LANGUAGE_CODE == 'en' %}
|
||||
<link href="{% static 'css/theme.min.css' %}" type="text/css" rel="stylesheet" id="style-default">
|
||||
<link href="{% static 'css/user.min.css' %}" type="text/css" rel="stylesheet" id="user-style-default">
|
||||
{% else %}
|
||||
<link href="{% static 'css/theme-rtl.min.css' %}" type="text/css" rel="stylesheet" id="style-rtl">
|
||||
<link href="{% static 'css/user-rtl.min.css' %}" type="text/css" rel="stylesheet" id="user-style-rtl">
|
||||
{% endif %}
|
||||
|
||||
</head>
|
||||
|
||||
@ -61,18 +57,14 @@
|
||||
<div class="bg-body-emphasis sticky-top" data-navbar-shadow-on-scroll="data-navbar-shadow-on-scroll">
|
||||
<nav class="navbar navbar-expand-lg container-small px-3 px-lg-7 px-xxl-3"><a class="navbar-brand me-1 me-sm-3" href="{% url 'landing_page' %}">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="d-flex align-items-center"><img src="{% static 'images/logos/logo.png' %}" alt="haikal" width="27" />
|
||||
<div class="d-flex align-items-center">
|
||||
<img class="d-light-none" src="{% static 'images/logos/logo-d.png' %}" alt="haikal" width="27" />
|
||||
<img class="d-dark-none" src="{% static 'images/logos/logo.png' %}" alt="haikal" width="27" />
|
||||
<h5 class="logo-text ms-2 d-none d-sm-block">{% trans 'Haikal' %}</h5>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<div class="d-lg-none">
|
||||
<div class="theme-control-toggle fa-icon-wait px-2">
|
||||
<input class="form-check-input ms-0 theme-control-toggle-input" type="checkbox" data-theme-control="phoenixTheme" value="dark" id="themeControlToggle" />
|
||||
<label class="mb-0 theme-control-toggle-label theme-control-toggle-light" for="themeControlToggle" data-bs-theme-value="light" data-bs-toggle="tooltip" data-bs-placement="left" data-bs-title="Switch theme" style="height:32px;width:32px;"><span class="icon" data-feather="moon"></span></label>
|
||||
<label class="mb-0 theme-control-toggle-label theme-control-toggle-dark" for="themeControlToggle" data-bs-theme-value="dark" data-bs-toggle="tooltip" data-bs-placement="left" data-bs-title="Switch theme" style="height:32px;width:32px;"><span class="icon" data-feather="sun"></span></label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<div class="border-bottom border-translucent border-bottom-lg-0 mb-2">
|
||||
@ -1277,19 +1269,6 @@
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
function save_as_pdf(){
|
||||
const quotationHtml = document.getElementById('quotation-html').outerHTML;
|
||||
const printWindow = window.open('', '', 'height=500,width=800');
|
||||
printWindow.document.write(quotationHtml);
|
||||
printWindow.document.close();
|
||||
printWindow.print();
|
||||
printWindow.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- ===============================================-->
|
||||
<!-- JavaScripts-->
|
||||
<!-- ===============================================-->
|
||||
@ -1303,6 +1282,11 @@
|
||||
<script src="{% static 'vendors/feather-icons/feather.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/dayjs/dayjs.min.js' %}"></script>
|
||||
<script src="{% static 'js/phoenix.js' %}"></script>
|
||||
<script src="{% static 'vendors/echarts/echarts.min.js' %}"></script>
|
||||
<script src="{% static 'js/travel-agency-dashboard.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="{% static 'vendors/swiper/swiper-bundle.min.js' %}"></script>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
@ -58,28 +58,24 @@
|
||||
</div>
|
||||
|
||||
<!-- Specification Modal -->
|
||||
<div class="modal fade" id="specificationsModal" tabindex="-1" aria-labelledby="specificationsModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||
<div class="modal-content glossy-modal">
|
||||
<div class="modal-header bg-primary text-light">
|
||||
<h5 class="modal-title" id="specificationsModalLabel">
|
||||
{% trans 'specifications'|upper %}
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="specificationsContent"></div>
|
||||
<div class="d-grid gap-2">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-danger"
|
||||
data-bs-dismiss="modal">
|
||||
{% trans 'Close' %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="specificationsModal" tabindex="-1" aria-labelledby="specificationsModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="specificationsModalLabel">{% trans 'specifications'|upper %}</h5>
|
||||
<button class="btn btn-close p-1" type="button" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="specificationsContent"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-outline-primary" type="button" data-bs-dismiss="modal">{% trans 'Close' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Specification Modal -->
|
||||
|
||||
<!-- Main Container -->
|
||||
<div class="d-flex flex-column min-vh-100">
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
<script src='https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js'></script>
|
||||
|
||||
|
||||
<div class="container p-2">
|
||||
<div class="container">
|
||||
|
||||
<!-- Specification Modal -->
|
||||
<div class="modal fade" id="specificationsModal"
|
||||
@ -344,10 +344,7 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
|
||||
function getCookie(name) {
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
const cookies = document.cookie.split(';');
|
||||
@ -362,7 +359,10 @@ document.addEventListener("DOMContentLoaded", function() {
|
||||
return cookieValue;
|
||||
}
|
||||
|
||||
const csrfToken = getCookie('csrftoken');
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
|
||||
|
||||
const csrfToken = getCookie('token');
|
||||
|
||||
const vinInput = document.getElementById('{{ form.vin.id_for_label }}');
|
||||
const stockTypeSelect = document.getElementById('{{ form.stock_type.id_for_label }}');
|
||||
@ -613,23 +613,29 @@ async function loadSpecifications(trimId){
|
||||
modelSelect.addEventListener("change", (e) => {loadSeries(e.target.value, yearSelect.value)})
|
||||
decodeVinBtn.addEventListener('click', decodeVin);
|
||||
});
|
||||
const Toast = Swal.mixin({
|
||||
toast: true,
|
||||
position: "top-end",
|
||||
showConfirmButton: false,
|
||||
timer: 3000,
|
||||
timerProgressBar: true,
|
||||
didOpen: (toast) => {
|
||||
toast.onmouseenter = Swal.stopTimer;
|
||||
toast.onmouseleave = Swal.resumeTimer;
|
||||
}
|
||||
});
|
||||
function notify(tag,msg){
|
||||
Toast.fire({
|
||||
function showLoading() {
|
||||
Swal.fire({
|
||||
title: "{% trans 'Please Wait' %}",
|
||||
text: "{% trans 'Loading' %}...",
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => {
|
||||
Swal.showLoading();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function hideLoading() {
|
||||
Swal.close();
|
||||
}
|
||||
|
||||
function notify(tag,msg){
|
||||
Swal.fire({
|
||||
icon: tag,
|
||||
titleText: msg
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
@ -1,7 +1,8 @@
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
|
||||
<script>
|
||||
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
|
||||
const Toast = Swal.mixin({
|
||||
toast: true,
|
||||
position: "top-end",
|
||||
@ -17,7 +18,8 @@
|
||||
icon: "{{ message.tags }}",
|
||||
titleText: "{{ message| safe }}"
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
</script>
|
||||
@ -1,14 +1,14 @@
|
||||
{% extends "base.html" %}
|
||||
{% load crispy_forms_filters %}
|
||||
{% load static %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% load i18n %}
|
||||
{% block title %}{{ _("Payyment Create") }}{% endblock title %}
|
||||
{% block title %}{{ _("Make Payment") }}{% endblock title %}
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">{{ _("Payment Create") }}</div>
|
||||
<div class="card-header">{{ _("Make Payment") }}</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="{% url 'payment_create' pk=quotation.pk %}">
|
||||
{% csrf_token %}
|
||||
|
||||
@ -138,7 +138,7 @@
|
||||
class="btn btn-success"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#confirmModal">
|
||||
{% trans 'Acccept' %}
|
||||
{% trans 'Accept' %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if quotation.status == 'Draft' and not quotation.is_approved %}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user