estimate and bank accounts

This commit is contained in:
gitea 2024-12-29 14:52:25 +00:00
commit 6ad4350183
70 changed files with 52103 additions and 3587 deletions

2
.gitignore vendored
View File

@ -4,7 +4,7 @@
*.pyc
__pycache__
**/*__pycache__
db.sqlite3
db.sqlite
media
car_inventory/settings.py
# Backup files #

View File

@ -1,4 +1,4 @@
# Generated by Django 5.0.6 on 2024-08-21 11:31
# Generated by Django 5.1.4 on 2024-12-26 16:17
from django.db import migrations, models

BIN
db.sqlite Normal file

Binary file not shown.

View File

@ -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

View File

@ -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):

View File

@ -44,7 +44,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:
@ -176,7 +177,8 @@ class CarRegistrationForm(forms.ModelForm):
class VendorForm(forms.ModelForm):
class Meta:
model = Vendor
exclude = ['dealer']
fields = ['name', 'arabic_name', 'crn', 'vrn', 'email', 'phone_number', 'contact_person', 'address', 'logo' ]
class CarColorsForm(forms.ModelForm):
@ -266,134 +268,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.'),
}
)

View File

@ -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=[

View File

@ -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),
),
]

View 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',
),
]

View File

@ -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'),
),
]

View 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'),
),
]

View File

@ -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'),
),
]

View File

@ -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',
),
]

View File

@ -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),
),
]

View 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'),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 5.1.4 on 2024-12-29 11:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0005_dealer_entity'),
]
operations = [
migrations.AddField(
model_name='vendor',
name='email',
field=models.EmailField(default='email@email.com', max_length=255, verbose_name='Email Address'),
preserve_default=False,
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 5.1.4 on 2024-12-29 13:04
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0006_vendor_email'),
]
operations = [
migrations.AddField(
model_name='vendor',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='Created At'),
preserve_default=False,
),
]

View File

@ -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

View File

@ -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
@ -594,12 +632,14 @@ class Vendor(models.Model, LocalizedNameMixin):
name = models.CharField(max_length=255, verbose_name=_("English Name"))
contact_person = models.CharField(max_length=100, verbose_name=_("Contact Person"))
phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number"))
email = models.EmailField(max_length=255, verbose_name=_("Email Address"))
address = models.CharField(
max_length=200, blank=True, null=True, verbose_name=_("Address")
)
logo = models.ImageField(
upload_to="logos/vendors", blank=True, null=True, verbose_name=_("Logo")
)
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At"))
class Meta:
verbose_name = _("Vendor")
@ -611,9 +651,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 +684,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"))

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -11,10 +11,10 @@ urlpatterns = [
path('welcome/', views.WelcomeView.as_view(), name='welcome'),
# Accounts URLs
path('login/', allauth_views.LoginView.as_view(template_name='account/login.html'), name='account_login'),
path('login/', views.Login.as_view(), 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'),
@ -25,13 +25,13 @@ urlpatterns = [
allauth_views.PasswordResetDoneView.as_view(template_name='account/password_reset_done.html'),
name='account_password_reset_done'),
path('login/code/', allauth_views.RequestLoginCodeView.as_view(template_name='account/request_login_code.html')),
#Dashboards
path('dashboards/accounting/', views.AccountingDashboard.as_view(), name='accounting'),
# 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'),

View File

@ -9,12 +9,6 @@ import json
import datetime
from decimal import Decimal
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
@ -33,14 +27,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,
@ -50,12 +41,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
@ -98,64 +86,71 @@ 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):
class Login(views.LoginView):
template_name = "account/login.html"
redirect_authenticated_user = True
class HomeView(TemplateView):
template_name = "index.html"
class AccountingDashboard(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)
@ -181,7 +176,7 @@ class HomeView(LoginRequiredMixin, TemplateView):
class WelcomeView(TemplateView):
template_name = "index.html"
template_name = "welcome.html"
class CarCreateView(LoginRequiredMixin, CreateView):
@ -538,7 +533,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)
@ -625,26 +620,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
@ -662,19 +643,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")
@ -714,7 +688,6 @@ class CustomerCreateView(
LoginRequiredMixin,
PermissionRequiredMixin,
SuccessMessageMixin,
AddDealerInstanceMixin,
CreateView,
):
model = models.Customer
@ -725,11 +698,11 @@ class CustomerCreateView(
success_message = _("Customer created successfully.")
class CustomerUpdateView(
LoginRequiredMixin,
PermissionRequiredMixin,
SuccessMessageMixin,
AddDealerInstanceMixin,
UpdateView,
):
model = models.Customer
@ -766,7 +739,6 @@ class VendorCreateView(
LoginRequiredMixin,
PermissionRequiredMixin,
SuccessMessageMixin,
AddDealerInstanceMixin,
CreateView,
):
model = models.Vendor
@ -776,12 +748,17 @@ class VendorCreateView(
permission_required = ("inventory.add_vendor",)
success_message = _("Vendor created successfully.")
def form_valid(self, form):
form.instance.dealer = self.request.user.dealer
return super().form_valid(form)
class VendorUpdateView(
LoginRequiredMixin,
PermissionRequiredMixin,
SuccessMessageMixin,
AddDealerInstanceMixin,
UpdateView,
):
model = models.Vendor
@ -1172,7 +1149,6 @@ class UserCreateView(
LoginRequiredMixin,
PermissionRequiredMixin,
SuccessMessageMixin,
AddDealerInstanceMixin,
CreateView,
):
model = models.Dealer
@ -1185,7 +1161,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
@ -1215,7 +1191,6 @@ class UserUpdateView(
LoginRequiredMixin,
PermissionRequiredMixin,
SuccessMessageMixin,
AddDealerInstanceMixin,
UpdateView,
):
model = models.Dealer
@ -1402,7 +1377,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)
@ -1414,6 +1390,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)
@ -1455,7 +1433,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

Binary file not shown.

1
static/css/sweetalert2.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -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);
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@ -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

File diff suppressed because one or more lines are too long

View File

@ -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]
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,68 +1,7 @@
<!DOCTYPE html>
<html lang="en-US" dir="ltr" data-navigation-type="default" data-navbar-horizontal-shape="default">
{% extends 'base.html' %}
{% load i18n static %}
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- ===============================================-->
<!-- Document Title-->
<!-- ===============================================-->
<title>Phoenix</title>
<!-- ===============================================-->
<!-- Favicons-->
<!-- ===============================================-->
<link rel="apple-touch-icon" sizes="180x180" href="../../../assets/img/favicons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="../../../assets/img/favicons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="../../../assets/img/favicons/favicon-16x16.png">
<link rel="shortcut icon" type="image/x-icon" href="../../../assets/img/favicons/favicon.ico">
<link rel="manifest" href="../../../assets/img/favicons/manifest.json">
<meta name="msapplication-TileImage" content="../../../assets/img/favicons/mstile-150x150.png">
<meta name="theme-color" content="#ffffff">
<script src="../../../vendors/simplebar/simplebar.min.js"></script>
<script src="../../../assets/js/config.js"></script>
<!-- ===============================================-->
<!-- Stylesheets-->
<!-- ===============================================-->
<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&amp;display=swap" rel="stylesheet">
<link href="../../../vendors/simplebar/simplebar.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css">
<link href="../../../assets/css/theme-rtl.min.css" type="text/css" rel="stylesheet" id="style-rtl">
<link href="../../../assets/css/theme.min.css" type="text/css" rel="stylesheet" id="style-default">
<link href="../../../assets/css/user-rtl.min.css" type="text/css" rel="stylesheet" id="user-style-rtl">
<link href="../../../assets/css/user.min.css" type="text/css" rel="stylesheet" id="user-style-default">
<script>
var phoenixIsRTL = window.config.config.phoenixIsRTL;
if (phoenixIsRTL) {
var linkDefault = document.getElementById('style-default');
var userLinkDefault = document.getElementById('user-style-default');
linkDefault.setAttribute('disabled', true);
userLinkDefault.setAttribute('disabled', true);
document.querySelector('html').setAttribute('dir', 'rtl');
} else {
var linkRTL = document.getElementById('style-rtl');
var userLinkRTL = document.getElementById('user-style-rtl');
linkRTL.setAttribute('disabled', true);
userLinkRTL.setAttribute('disabled', true);
}
</script>
</head>
<body>
<!-- ===============================================-->
<!-- Main Content-->
<!-- ===============================================-->
<main class="main" id="top">
{% 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-xxl-4"><a class="d-flex flex-center text-decoration-none mb-4" href="../../../index.html">
@ -94,206 +33,5 @@
</div>
</div>
</div>
<script>
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');
}
</script>
<div class="support-chat-container">
<div class="container-fluid support-chat">
<div class="card bg-body-emphasis">
<div class="card-header d-flex flex-between-center px-4 py-3 border-bottom border-translucent">
<h5 class="mb-0 d-flex align-items-center gap-2">Demo widget<span class="fa-solid fa-circle text-success fs-11"></span></h5>
<div class="btn-reveal-trigger">
<button class="btn btn-link p-0 dropdown-toggle dropdown-caret-none transition-none d-flex" type="button" id="support-chat-dropdown" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h text-body"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2" aria-labelledby="support-chat-dropdown"><a class="dropdown-item" href="#!">Request a callback</a><a class="dropdown-item" href="#!">Search in chat</a><a class="dropdown-item" href="#!">Show history</a><a class="dropdown-item" href="#!">Report to Admin</a><a class="dropdown-item btn-support-chat" href="#!">Close Support</a></div>
</div>
</div>
<div class="card-body chat p-0">
<div class="d-flex flex-column-reverse scrollbar h-100 p-3">
<div class="text-end mt-6"><a class="mb-2 d-inline-flex align-items-center text-decoration-none text-body-emphasis bg-body-hover rounded-pill border border-primary py-2 ps-4 pe-3" href="#!">
<p class="mb-0 fw-semibold fs-9">I need help with something</p><span class="fa-solid fa-paper-plane text-primary fs-9 ms-3"></span>
</a><a class="mb-2 d-inline-flex align-items-center text-decoration-none text-body-emphasis bg-body-hover rounded-pill border border-primary py-2 ps-4 pe-3" href="#!">
<p class="mb-0 fw-semibold fs-9">I cant reorder a product I previously ordered</p><span class="fa-solid fa-paper-plane text-primary fs-9 ms-3"></span>
</a><a class="mb-2 d-inline-flex align-items-center text-decoration-none text-body-emphasis bg-body-hover rounded-pill border border-primary py-2 ps-4 pe-3" href="#!">
<p class="mb-0 fw-semibold fs-9">How do I place an order?</p><span class="fa-solid fa-paper-plane text-primary fs-9 ms-3"></span>
</a><a class="false d-inline-flex align-items-center text-decoration-none text-body-emphasis bg-body-hover rounded-pill border border-primary py-2 ps-4 pe-3" href="#!">
<p class="mb-0 fw-semibold fs-9">My payment method not working</p><span class="fa-solid fa-paper-plane text-primary fs-9 ms-3"></span>
</a>
</div>
<div class="text-center mt-auto">
<div class="avatar avatar-3xl status-online"><img class="rounded-circle border border-3 border-light-subtle" src="../../../assets/img/team/30.webp" alt="" /></div>
<h5 class="mt-2 mb-3">Eric</h5>
<p class="text-center text-body-emphasis mb-0">Ask us anything well get back to you here or by email within 24 hours.</p>
</div>
</div>
</div>
<div class="card-footer d-flex align-items-center gap-2 border-top border-translucent ps-3 pe-4 py-3">
<div class="d-flex align-items-center flex-1 gap-3 border border-translucent rounded-pill px-4">
<input class="form-control outline-none border-0 flex-1 fs-9 px-0" type="text" placeholder="Write message" />
<label class="btn btn-link d-flex p-0 text-body-quaternary fs-9 border-0" for="supportChatPhotos"><span class="fa-solid fa-image"></span></label>
<input class="d-none" type="file" accept="image/*" id="supportChatPhotos" />
<label class="btn btn-link d-flex p-0 text-body-quaternary fs-9 border-0" for="supportChatAttachment"> <span class="fa-solid fa-paperclip"></span></label>
<input class="d-none" type="file" id="supportChatAttachment" />
</div>
<button class="btn p-0 border-0 send-btn"><span class="fa-solid fa-paper-plane fs-9"></span></button>
</div>
</div>
</div>
<button class="btn btn-support-chat p-0 border border-translucent"><span class="fs-8 btn-text text-primary text-nowrap">Chat demo</span><span class="ping-icon-wrapper mt-n4 ms-n6 mt-sm-0 ms-sm-2 position-absolute position-sm-relative"><span class="ping-icon-bg"></span><span class="fa-solid fa-circle ping-icon"></span></span><span class="fa-solid fa-headset text-primary fs-8 d-sm-none"></span><span class="fa-solid fa-chevron-down text-primary fs-7"></span></button>
</div>
</main>
<!-- ===============================================-->
<!-- End of Main Content-->
<!-- ===============================================-->
<div class="offcanvas offcanvas-end settings-panel border-0" id="settings-offcanvas" tabindex="-1" aria-labelledby="settings-offcanvas">
<div class="offcanvas-header align-items-start border-bottom flex-column border-translucent">
<div class="pt-1 w-100 mb-6 d-flex justify-content-between align-items-start">
<div>
<h5 class="mb-2 me-2 lh-sm"><span class="fas fa-palette me-2 fs-8"></span>Theme Customizer</h5>
<p class="mb-0 fs-9">Explore different styles according to your preferences</p>
</div>
<button class="btn p-1 fw-bolder" type="button" data-bs-dismiss="offcanvas" aria-label="Close"><span class="fas fa-times fs-8"> </span></button>
</div>
<button class="btn btn-phoenix-secondary w-100" data-theme-control="reset"><span class="fas fa-arrows-rotate me-2 fs-10"></span>Reset to default</button>
</div>
<div class="offcanvas-body scrollbar px-card" id="themeController">
<div class="setting-panel-item mt-0">
<h5 class="setting-panel-item-title">Color Scheme</h5>
<div class="row gx-2">
<div class="col-4">
<input class="btn-check" id="themeSwitcherLight" name="theme-color" type="radio" value="light" data-theme-control="phoenixTheme" />
<label class="btn d-inline-block btn-navbar-style fs-9" for="themeSwitcherLight"> <span class="mb-2 rounded d-block"><img class="img-fluid img-prototype mb-0" src="../../../assets/img/generic/default-light.png" alt=""/></span><span class="label-text">Light</span></label>
</div>
<div class="col-4">
<input class="btn-check" id="themeSwitcherDark" name="theme-color" type="radio" value="dark" data-theme-control="phoenixTheme" />
<label class="btn d-inline-block btn-navbar-style fs-9" for="themeSwitcherDark"> <span class="mb-2 rounded d-block"><img class="img-fluid img-prototype mb-0" src="../../../assets/img/generic/default-dark.png" alt=""/></span><span class="label-text"> Dark</span></label>
</div>
<div class="col-4">
<input class="btn-check" id="themeSwitcherAuto" name="theme-color" type="radio" value="auto" data-theme-control="phoenixTheme" />
<label class="btn d-inline-block btn-navbar-style fs-9" for="themeSwitcherAuto"> <span class="mb-2 rounded d-block"><img class="img-fluid img-prototype mb-0" src="../../../assets/img/generic/auto.png" alt=""/></span><span class="label-text"> Auto</span></label>
</div>
</div>
</div>
<div class="border border-translucent rounded-3 p-4 setting-panel-item bg-body-emphasis">
<div class="d-flex justify-content-between align-items-center">
<h5 class="setting-panel-item-title mb-1">RTL </h5>
<div class="form-check form-switch mb-0">
<input class="form-check-input ms-auto" type="checkbox" data-theme-control="phoenixIsRTL" />
</div>
</div>
<p class="mb-0 text-body-tertiary">Change text direction</p>
</div>
<div class="border border-translucent rounded-3 p-4 setting-panel-item bg-body-emphasis">
<div class="d-flex justify-content-between align-items-center">
<h5 class="setting-panel-item-title mb-1">Support Chat </h5>
<div class="form-check form-switch mb-0">
<input class="form-check-input ms-auto" type="checkbox" data-theme-control="phoenixSupportChat" />
</div>
</div>
<p class="mb-0 text-body-tertiary">Toggle support chat</p>
</div>
<div class="setting-panel-item">
<h5 class="setting-panel-item-title">Navigation Type</h5>
<div class="row gx-2">
<div class="col-6">
<input class="btn-check" id="navbarPositionVertical" name="navigation-type" type="radio" value="vertical" data-theme-control="phoenixNavbarPosition" data-page-url="../../../documentation/layouts/vertical-navbar.html" disabled="disabled" />
<label class="btn d-inline-block btn-navbar-style fs-9" for="navbarPositionVertical"> <span class="rounded d-block"><img class="img-fluid img-prototype d-dark-none" src="../../../assets/img/generic/default-light.png" alt=""/><img class="img-fluid img-prototype d-light-none" src="../../../assets/img/generic/default-dark.png" alt=""/></span><span class="label-text">Vertical</span></label>
</div>
<div class="col-6">
<input class="btn-check" id="navbarPositionHorizontal" name="navigation-type" type="radio" value="horizontal" data-theme-control="phoenixNavbarPosition" data-page-url="../../../documentation/layouts/horizontal-navbar.html" disabled="disabled" />
<label class="btn d-inline-block btn-navbar-style fs-9" for="navbarPositionHorizontal"> <span class="rounded d-block"><img class="img-fluid img-prototype d-dark-none" src="../../../assets/img/generic/top-default.png" alt=""/><img class="img-fluid img-prototype d-light-none" src="../../../assets/img/generic/top-default-dark.png" alt=""/></span><span class="label-text"> Horizontal</span></label>
</div>
<div class="col-6">
<input class="btn-check" id="navbarPositionCombo" name="navigation-type" type="radio" value="combo" data-theme-control="phoenixNavbarPosition" disabled="disabled" data-page-url="../../../documentation/layouts/combo-navbar.html" />
<label class="btn d-inline-block btn-navbar-style fs-9" for="navbarPositionCombo"> <span class="rounded d-block"><img class="img-fluid img-prototype d-dark-none" src="../../../assets/img/generic/nav-combo-light.png" alt=""/><img class="img-fluid img-prototype d-light-none" src="../../../assets/img/generic/nav-combo-dark.png" alt=""/></span><span class="label-text"> Combo</span></label>
</div>
<div class="col-6">
<input class="btn-check" id="navbarPositionTopDouble" name="navigation-type" type="radio" value="dual-nav" data-theme-control="phoenixNavbarPosition" disabled="disabled" data-page-url="../../../documentation/layouts/dual-nav.html" />
<label class="btn d-inline-block btn-navbar-style fs-9" for="navbarPositionTopDouble"> <span class="rounded d-block"><img class="img-fluid img-prototype d-dark-none" src="../../../assets/img/generic/dual-light.png" alt=""/><img class="img-fluid img-prototype d-light-none" src="../../../assets/img/generic/dual-dark.png" alt=""/></span><span class="label-text"> Dual nav</span></label>
</div>
</div>
<p class="text-warning-dark font-medium"> <span class="fa-solid fa-triangle-exclamation me-2 text-warning"></span>You can't update navigation type in this page</p>
</div>
<div class="setting-panel-item">
<h5 class="setting-panel-item-title">Vertical Navbar Appearance</h5>
<div class="row gx-2">
<div class="col-6">
<input class="btn-check" id="navbar-style-default" type="radio" name="config.name" value="default" data-theme-control="phoenixNavbarVerticalStyle" disabled="disabled" />
<label class="btn d-block w-100 btn-navbar-style fs-9" for="navbar-style-default"> <img class="img-fluid img-prototype d-dark-none" src="../../../assets/img/generic/default-light.png" alt="" /><img class="img-fluid img-prototype d-light-none" src="../../../assets/img/generic/default-dark.png" alt="" /><span class="label-text d-dark-none"> Default</span><span class="label-text d-light-none">Default</span></label>
</div>
<div class="col-6">
<input class="btn-check" id="navbar-style-dark" type="radio" name="config.name" value="darker" data-theme-control="phoenixNavbarVerticalStyle" disabled="disabled" />
<label class="btn d-block w-100 btn-navbar-style fs-9" for="navbar-style-dark"> <img class="img-fluid img-prototype d-dark-none" src="../../../assets/img/generic/vertical-darker.png" alt="" /><img class="img-fluid img-prototype d-light-none" src="../../../assets/img/generic/vertical-lighter.png" alt="" /><span class="label-text d-dark-none"> Darker</span><span class="label-text d-light-none">Lighter</span></label>
</div>
</div>
<p class="text-warning-dark font-medium"> <span class="fa-solid fa-triangle-exclamation me-2 text-warning"></span>You can't update vertical navbar appearance in this page</p>
</div>
<div class="setting-panel-item">
<h5 class="setting-panel-item-title">Horizontal Navbar Shape</h5>
<div class="row gx-2">
<div class="col-6">
<input class="btn-check" id="navbarShapeDefault" name="navbar-shape" type="radio" value="default" data-theme-control="phoenixNavbarTopShape" data-page-url="../../../documentation/layouts/horizontal-navbar.html" disabled="disabled" />
<label class="btn d-inline-block btn-navbar-style fs-9" for="navbarShapeDefault"> <span class="mb-2 rounded d-block"><img class="img-fluid img-prototype d-dark-none mb-0" src="../../../assets/img/generic/top-default.png" alt=""/><img class="img-fluid img-prototype d-light-none mb-0" src="../../../assets/img/generic/top-default-dark.png" alt=""/></span><span class="label-text">Default</span></label>
</div>
<div class="col-6">
<input class="btn-check" id="navbarShapeSlim" name="navbar-shape" type="radio" value="slim" data-theme-control="phoenixNavbarTopShape" data-page-url="../../../documentation/layouts/horizontal-navbar.html#horizontal-navbar-slim" disabled="disabled" />
<label class="btn d-inline-block btn-navbar-style fs-9" for="navbarShapeSlim"> <span class="mb-2 rounded d-block"><img class="img-fluid img-prototype d-dark-none mb-0" src="../../../assets/img/generic/top-slim.png" alt=""/><img class="img-fluid img-prototype d-light-none mb-0" src="../../../assets/img/generic/top-slim-dark.png" alt=""/></span><span class="label-text"> Slim</span></label>
</div>
</div>
<p class="text-warning-dark font-medium"> <span class="fa-solid fa-triangle-exclamation me-2 text-warning"></span>You can't update horizontal navbar shape in this page</p>
</div>
<div class="setting-panel-item">
<h5 class="setting-panel-item-title">Horizontal Navbar Appearance</h5>
<div class="row gx-2">
<div class="col-6">
<input class="btn-check" id="navbarTopDefault" name="navbar-top-style" type="radio" value="default" data-theme-control="phoenixNavbarTopStyle" disabled="disabled" />
<label class="btn d-inline-block btn-navbar-style fs-9" for="navbarTopDefault"> <span class="mb-2 rounded d-block"><img class="img-fluid img-prototype d-dark-none mb-0" src="../../../assets/img/generic/top-default.png" alt=""/><img class="img-fluid img-prototype d-light-none mb-0" src="../../../assets/img/generic/top-style-darker.png" alt=""/></span><span class="label-text">Default</span></label>
</div>
<div class="col-6">
<input class="btn-check" id="navbarTopDarker" name="navbar-top-style" type="radio" value="darker" data-theme-control="phoenixNavbarTopStyle" disabled="disabled" />
<label class="btn d-inline-block btn-navbar-style fs-9" for="navbarTopDarker"> <span class="mb-2 rounded d-block"><img class="img-fluid img-prototype d-dark-none mb-0" src="../../../assets/img/generic/navbar-top-style-light.png" alt=""/><img class="img-fluid img-prototype d-light-none mb-0" src="../../../assets/img/generic/top-style-lighter.png" alt=""/></span><span class="label-text d-dark-none">Darker</span><span class="label-text d-light-none">Lighter</span></label>
</div>
</div>
<p class="text-warning-dark font-medium"> <span class="fa-solid fa-triangle-exclamation me-2 text-warning"></span>You can't update horizontal navbar appearance in this page</p>
</div><a class="bun btn-primary d-grid mb-3 text-white mt-5 btn btn-primary" href="https://themes.getbootstrap.com/product/phoenix-admin-dashboard-webapp-template/" target="_blank">Purchase template</a>
</div>
</div><a class="card setting-toggle" href="#settings-offcanvas" data-bs-toggle="offcanvas">
<div class="card-body d-flex align-items-center px-2 py-1">
<div class="position-relative rounded-start" style="height:34px;width:28px">
<div class="settings-popover"><span class="ripple"><span class="fa-spin position-absolute all-0 d-flex flex-center"><span class="icon-spin position-absolute all-0 d-flex flex-center">
<svg width="20" height="20" viewBox="0 0 20 20" fill="#ffffff" xmlns="http://www.w3.org/2000/svg">
<path d="M19.7369 12.3941L19.1989 12.1065C18.4459 11.7041 18.0843 10.8487 18.0843 9.99495C18.0843 9.14118 18.4459 8.28582 19.1989 7.88336L19.7369 7.59581C19.9474 7.47484 20.0316 7.23291 19.9474 7.03131C19.4842 5.57973 18.6843 4.28943 17.6738 3.20075C17.5053 3.03946 17.2527 2.99914 17.0422 3.12011L16.393 3.46714C15.6883 3.84379 14.8377 3.74529 14.1476 3.3427C14.0988 3.31422 14.0496 3.28621 14.0002 3.25868C13.2568 2.84453 12.7055 2.10629 12.7055 1.25525V0.70081C12.7055 0.499202 12.5371 0.297594 12.2845 0.257272C10.7266 -0.105622 9.16879 -0.0653007 7.69516 0.257272C7.44254 0.297594 7.31623 0.499202 7.31623 0.70081V1.23474C7.31623 2.09575 6.74999 2.8362 5.99824 3.25599C5.95774 3.27861 5.91747 3.30159 5.87744 3.32493C5.15643 3.74527 4.26453 3.85902 3.53534 3.45302L2.93743 3.12011C2.72691 2.99914 2.47429 3.03946 2.30587 3.20075C1.29538 4.28943 0.495411 5.57973 0.0322686 7.03131C-0.051939 7.23291 0.0322686 7.47484 0.242788 7.59581L0.784376 7.8853C1.54166 8.29007 1.92694 9.13627 1.92694 9.99495C1.92694 10.8536 1.54166 11.6998 0.784375 12.1046L0.242788 12.3941C0.0322686 12.515 -0.051939 12.757 0.0322686 12.9586C0.495411 14.4102 1.29538 15.7005 2.30587 16.7891C2.47429 16.9504 2.72691 16.9907 2.93743 16.8698L3.58669 16.5227C4.29133 16.1461 5.14131 16.2457 5.8331 16.6455C5.88713 16.6767 5.94159 16.7074 5.99648 16.7375C6.75162 17.1511 7.31623 17.8941 7.31623 18.7552V19.2891C7.31623 19.4425 7.41373 19.5959 7.55309 19.696C7.64066 19.7589 7.74815 19.7843 7.85406 19.8046C9.35884 20.0925 10.8609 20.0456 12.2845 19.7729C12.5371 19.6923 12.7055 19.4907 12.7055 19.2891V18.7346C12.7055 17.8836 13.2568 17.1454 14.0002 16.7312C14.0496 16.7037 14.0988 16.6757 14.1476 16.6472C14.8377 16.2446 15.6883 16.1461 16.393 16.5227L17.0422 16.8698C17.2527 16.9907 17.5053 16.9504 17.6738 16.7891C18.7264 15.7005 19.4842 14.4102 19.9895 12.9586C20.0316 12.757 19.9474 12.515 19.7369 12.3941ZM10.0109 13.2005C8.1162 13.2005 6.64257 11.7893 6.64257 9.97478C6.64257 8.20063 8.1162 6.74905 10.0109 6.74905C11.8634 6.74905 13.3792 8.20063 13.3792 9.97478C13.3792 11.7893 11.8634 13.2005 10.0109 13.2005Z" fill="#2A7BE4"></path>
</svg></span></span></span></div>
</div><small class="text-uppercase text-body-tertiary fw-bold py-2 pe-2 ps-1 rounded-end">customize</small>
</div>
</a>
<!-- ===============================================-->
<!-- JavaScripts-->
<!-- ===============================================-->
<script src="../../../vendors/popper/popper.min.js"></script>
<script src="../../../vendors/bootstrap/bootstrap.min.js"></script>
<script src="../../../vendors/anchorjs/anchor.min.js"></script>
<script src="../../../vendors/is/is.min.js"></script>
<script src="../../../vendors/fontawesome/all.min.js"></script>
<script src="../../../vendors/lodash/lodash.min.js"></script>
<script src="../../../vendors/list.js/list.min.js"></script>
<script src="../../../vendors/feather-icons/feather.min.js"></script>
<script src="../../../vendors/dayjs/dayjs.min.js"></script>
<script src="../../../assets/js/phoenix.js"></script>
</body>
</html>
{% endblock %}

View File

@ -6,16 +6,21 @@
{% 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">
<div class="d-flex align-items-center fw-bolder fs-3 d-inline-block">
<img class="d-dark-none" src="{% static 'images/logos/logo-d.png' %}" alt="{% trans 'home' %}" width="58" />
<img class="d-light-none" src="{% static 'images/logos/logo.png' %}" alt="{% trans 'home' %}" width="58" />
</div>
</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 +39,6 @@
</div>
</div>
</div>
{% endblock content %}
{% block extra_body %}

View File

@ -1,38 +1,39 @@
{% 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">
<div class="d-flex align-items-center fw-bolder fs-3 d-inline-block">
<img class="d-dark-none" src="{% static 'images/logos/logo-d.png' %}" alt="{% trans 'home' %}" width="58" />
<img class="d-light-none" src="{% static 'images/logos/logo.png' %}" alt="{% trans 'home' %}" width="58" />
</div>
</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 %}

View File

@ -5,12 +5,16 @@
{% 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 class="d-flex align-items-center fw-bolder fs-3 d-inline-block">
<div class="d-flex align-items-center fw-bolder fs-3 d-inline-block">
<img class="d-dark-none" src="{% static 'images/logos/logo-d.png' %}" alt="{% trans 'home' %}" width="58" />
<img class="d-light-none" src="{% static 'images/logos/logo.png' %}" alt="{% trans 'home' %}" width="58" />
</div>
</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 +32,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>

View File

@ -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-d.png' %}" alt="{% trans 'home' %}" width="58" />
<img class="d-light-none" src="{% static 'images/logos/logo.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 %}

View File

@ -6,9 +6,13 @@
{% 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 class="d-flex align-items-center fw-bolder fs-3 d-inline-block">
<div class="d-flex align-items-center fw-bolder fs-3 d-inline-block">
<img class="d-dark-none" src="{% static 'images/logos/logo-d.png' %}" alt="{% trans 'home' %}" width="58" />
<img class="d-light-none" src="{% static 'images/logos/logo.png' %}" alt="{% trans 'home' %}" width="58" />
</div>
</div>
</a>
<div class="text-center">
@ -69,7 +73,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>

View File

@ -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&amp;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">
@ -76,7 +73,7 @@
<ul class="nav collapse parent" data-bs-parent="#navbarVerticalCollapse" id="nv-dashboards">
<li class="collapsed-nav-item-title d-none">Dashboards
</li>
<li class="nav-item"><a class="nav-link" href="#">
<li class="nav-item"><a class="nav-link" href="{% url 'accounting' %}">
<div class="d-flex align-items-center"><span class="nav-link-text">Accounting</span>
</div>
</a>
@ -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">
@ -133,6 +130,30 @@
</div>
</div>
<!-- parent pages-->
<div class="nav-item-wrapper"><a class="nav-link dropdown-indicator label-1" href="#nv-vendors" role="button" data-bs-toggle="collapse" aria-expanded="false" aria-controls="nv-vendors">
<div class="d-flex align-items-center">
<div class="dropdown-indicator-icon-wrapper"><span class="fas fa-caret-right dropdown-indicator-icon"></span></div><span class="nav-link-icon"><span data-feather="package"></span></span><span class="nav-link-text">{% trans 'vendors'|capfirst %}</span>
</div>
</a>
<div class="parent-wrapper label-1">
<ul class="nav collapse parent" data-bs-parent="#navbarVerticalCollapse" id="nv-vendors">
<li class="collapsed-nav-item-title d-none">{% trans 'vendors'|capfirst %}
</li>
<li class="nav-item"><a class="nav-link" href="{% url 'vendor_create' %}">
<div class="d-flex align-items-center"><span class="nav-link-text">{% trans "add vendor"|capfirst %}</span>
</div>
</a>
<!-- more inner pages-->
</li>
<li class="nav-item"><a class="nav-link" href="{% url 'vendor_list' %}">
<div class="d-flex align-items-center"><span class="nav-link-text">{% trans 'vendors'|capfirst %}</span>
</div>
</a>
<!-- more inner pages-->
</li>
</ul>
</div>
</div> <!-- parent pages-->
<div class="nav-item-wrapper"><a class="nav-link dropdown-indicator label-1" href="#nv-customers" role="button" data-bs-toggle="collapse" aria-expanded="false" aria-controls="nv-customers">
<div class="d-flex align-items-center">
<div class="dropdown-indicator-icon-wrapper"><span class="fas fa-caret-right dropdown-indicator-icon"></span></div><span class="nav-link-icon"><span data-feather="users"></span></span><span class="nav-link-text">{% trans 'customers'|capfirst %}</span>
@ -160,7 +181,7 @@
<!-- parent pages-->
<div class="nav-item-wrapper"><a class="nav-link dropdown-indicator label-1" href="#nv-organizations" role="button" data-bs-toggle="collapse" aria-expanded="false" aria-controls="nv-organizations">
<div class="d-flex align-items-center">
<div class="dropdown-indicator-icon-wrapper"><span class="fas fa-caret-right dropdown-indicator-icon"></span></div><span class="nav-link-icon"><span data-feather="phone"></span></span><span class="nav-link-text">{% trans 'organizations'|capfirst %}</span>
<div class="dropdown-indicator-icon-wrapper"><span class="fas fa-caret-right dropdown-indicator-icon"></span></div><span class="nav-link-icon"><span data-feather="activity"></span></span><span class="nav-link-text">{% trans 'organizations'|capfirst %}</span>
</div>
</a>
<div class="parent-wrapper label-1">
@ -253,14 +274,10 @@
<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 %}
<div class="d-flex align-items-center">
<img class="logo-img d-dark-none" src="{% static 'images/logos/logo-d.png' %}" alt="haikal" width="27" />
<img class="logo-img d-light-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>
@ -472,11 +489,10 @@
</li>
</ul>
</div>
</nav>
<div class="content">
{% include 'messages.html' %}
{% block content %}
<!-- Main content goes here -->
{% endblock %}
@ -493,146 +509,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 +558,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>
{% block extra_js %}{% endblock extra_js %}

File diff suppressed because it is too large Load Diff

View File

@ -4,27 +4,8 @@
{% block content %}
<div class="container">
<!-- ============================================-->
<!-- <section> begin ============================-->
<section class="py-0">
<div class="container-small">
<div class="ecommerce-topbar">
</div>
</div>
<!-- end of .container-->
</section>
<!-- <section> close ============================-->
<!-- ============================================-->
<!-- ============================================-->
<!-- <section> begin ============================-->
<section class="pt-5 pb-9">
<div class="container-small">
@ -37,7 +18,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 +125,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 +137,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>

View File

@ -51,7 +51,7 @@ function getCookie(name) {
return cookieValue;
}
const csrfToken = getCookie('csrftoken');
const csrfToken = getCookie('token');
async function sendMessage() {

File diff suppressed because it is too large Load Diff

View File

@ -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">

View File

@ -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 %}

View File

@ -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>

View File

@ -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 %}

View File

@ -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 %}

View File

@ -4,36 +4,47 @@
{% block title %}{% trans "Vendors" %}{% endblock title %}
{% block content %}
<div class="container my-5">
<!-- Display Form Errors -->
<div class="card shadow rounded">
<div class="card-header bg-primary text-white">
<p class="mb-0">
{% if customer.created %}
<!--<i class="bi bi-pencil-square"></i>-->
<div class="container">
<div class="content">
<div class="row">
<div class="col-xl-9">
<div class="d-sm-flex justify-content-between">
<h3 class="mb-3">
{% if vendor.created %}
<!--<i class="bi bi-pencil-square"></i>-->
{{ _("Edit Vendor") }}
{% else %}
<!--<i class="bi bi-person-plus"></i> -->
{{ _("Add Vendor") }}
{% endif %}
</p>
</div>
<div class="card-body">
<form method="post" class="form" novalidate>
</h3>
</div>
</div>
</div>
<div class="row">
<div class="col-xl-9">
<form class="row g-3 mb-9" method="post" class="form" novalidate>
{% csrf_token %}
{{ redirect_field }}
{{ form|crispy }}
{% for error in form.errors %}
<div class="text-danger">{{ error }}</div>
{% endfor %}
<div class="d-flex justify-content-end">
<button class="btn btn-sm btn-success me-1" type="submit">
<div class="d-flex mb-3">
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-phoenix-primary me-2 px-6">{% trans "cancel"|capfirst %}</a>
<button class="btn btn-primary" type="submit">
<!--<i class="bi bi-save"></i> -->
{{ _("Save") }}
</button>
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-sm btn-danger">{% trans "Cancel" %}</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -5,69 +5,146 @@
{% block vendors %}<a class="nav-link active">{{ _("Vendors")|capfirst }}</a>{% endblock %}
{% block content %}
<div class="container-fluid p-3">
<div class="card shadow rounded">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0">{{ _("Vendors")|capfirst }}</h6>
<form method="get" class="d-inline-block">
<div class="input-group input-group-sm">
<button class="btn btn-secondary" type="submit">
{{ _("Search")|capfirst }}
</button>
<input type="text"
name="q"
class="form-control"
placeholder="{{ _('Enter vendor name') }}"
value="{{ request.GET.q }}">
<section class="pt-5 pb-9">
<div class="container">
<h2 class="mb-4">{{ _("Vendors")|capfirst }}</h2>
<div class="row g-3 justify-content-between mb-4">
<div class="col-auto">
<div class="d-md-flex justify-content-between">
<div>
<a href="{% url 'vendor_create' %}" class="btn btn-primary me-4"><span class="fas fa-plus me-2"></span>{{ _("Add Vendor") }}</a>
</div>
</div>
</div>
<div class="col-auto">
<div class="d-flex">
<div class="search-box me-2">
<form method="get" class="d-inline-block position-relative">
<input name="q" class="form-control search-input search" type="search" placeholder="{{ _('Enter vendor name') }}" aria-label="Search" value="{{ request.GET.q }}"/>
<span class="fas fa-search search-box-icon"></span>
{% if request.GET.q %}
<a href="{% url request.resolver_match.view_name %}" class="btn btn-outline-danger ms-1">
<i class="bi bi-x-lg"></i>
</a>
{% endif %}
</form>
</div>
</div>
</form>
</div>
<div class="card-body p-0">
<table class="table table-hover table-striped mb-0">
<thead class="table-light">
<tr>
<th>{{ _("Name")|capfirst }}</th>
<th>{{ _("Logo")|capfirst }}</th>
<th>{{ _("Address")|capfirst }}</th>
<th>{{ _("Actions")|capfirst }}</th>
</tr>
</div>
</div>
{% if page_obj.object_list %}
<div class="table-responsive scrollbar mx-n1 px-1">
<table class="table fs-9 mb-0 leads-table border-top border-translucent">
<thead>
<tr>
<th class="sort white-space-nowrap align-middle text-uppercase ps-0" scope="col" data-sort="name" style="width:25%;">{{ _("Name")|capfirst }}</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="email" style="width:15%;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center px-1 py-1 bg-success-subtle rounded me-2"><span class="text-success-dark" data-feather="mail"></span></div><span>{{ _("email")|capfirst }}</span>
</div>
</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="phone" style="width:15%; min-width: 180px;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center px-1 py-1 bg-primary-subtle rounded me-2"><span class="text-primary-dark" data-feather="phone"></span></div><span>Phone</span>
</div>
</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="contact" style="width:15%;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center px-1 py-1 bg-info-subtle rounded me-2"><span class="text-info-dark" data-feather="user"></span></div><span>{{ _("Contact name")|capfirst }}</span>
</div>
</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="company" style="width:15%;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center px-1 py-1 bg-warning-subtle rounded me-2"><span class="text-warning-dark" data-feather="grid"></span></div><span>{{ _("Address")|capfirst }}</span>
</div>
</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase" scope="col" data-sort="date" style="width:15%;">
{{ _("Create date") }}</th>
<th class="sort text-end align-middle pe-0 ps-4" scope="col"></th>
</tr>
</thead>
<tbody>
{% if page_obj.object_list %}
<tbody class="list" id="leal-tables-body">
{% for vendor in vendors %}
<tr>
<td>{{ vendor.get_local_name }}</td>
<td>
{% if vendor.logo %}
<img src="{{ vendor.logo.url }}"
alt="{{ vendor.get_local_name }}"
class="img-thumbnail"
style="max-width: 100px;">
{% endif %}
</td>
<td>{{ vendor.address }}</td>
<td>
<a href="{% url 'vendor_detail' vendor.id %}" class="btn btn-success btn-sm">
{{ _("view")|capfirst }}
</a>
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="4" class="text-center">{{ _("No vendors found")|capfirst }}</td>
</tr>
{% endif %}
</tbody>
</table>
<!-- Delete Modal -->
<div class="modal fade" id="deleteModal"
data-bs-backdrop="static"
data-bs-keyboard="false"
tabindex="-1"
aria-labelledby="deleteModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModalLabel">
{% trans "Delete Vendor" %}
<span data-feather="alert-circle"></span>
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body text-center">
<p class="mb-0 text-danger fw-bold">
{% trans "Are you sure you want to delete this vendor?" %}
</p>
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">
{% trans "No" %}
</button>
<a type="button" class="btn btn-danger btn-sm" href="{% url 'vendor_delete' vendor.id %}">
{% trans "Yes" %}
</a>
</div>
</div>
</div>
</div>
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td class="name align-middle white-space-nowrap ps-0">
<div class="d-flex align-items-center">
{% if vendor.logo %}
<div class="avatar avatar-xl me-3"><img class="rounded-circle" src="{{ vendor.logo.url }}" alt="" />
{% else %}
<div class="avatar avatar-xl me-3"><img class="rounded-circle" src="{% static 'images/icons/picture.svg' %}" alt="" />
{% endif %}
</div>
<div><a class="fs-8 fw-bold" href="{% url 'vendor_detail' vendor.id %}">{{ vendor.name }}</a>
<div class="d-flex align-items-center">
<p class="mb-0 text-body-highlight fw-semibold fs-9 me-2">{{ vendor.arabic_name }}</p><span class="badge badge-phoenix badge-phoenix-primary">{{ vendor.id}}</span>
</div>
</div>
</div>
</td>
<td class="email align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent"><a class="text-body-highlight" href="">{{ vendor.email }}</a></td>
<td class="phone align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent"><a class="text-body-highlight" href="tel:{{ vendor.phone_number }}">{{ vendor.phone_number }}</a></td>
<td class="contact align-middle white-space-nowrap ps-4 border-end border-translucent fw-semibold text-body-highlight">{{ vendor.contact_person }}</td>
<td class="company align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 border-end border-translucent fw-semibold text-body-highlight">
{{ vendor.address }}</td>
<td class="date align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 text-body-tertiary">{{ vendor.created_at|date }}</td>
<td class="align-middle white-space-nowrap text-end pe-0 ps-4">
<div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2"><a href="{% url 'vendor_update' vendor.id %}" class="dropdown-item text-success-dark">
{% trans "Edit" %}
</a>
<div class="dropdown-divider"></div><button class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">{% trans "Delete" %}</button>
</div>
</div>
</td>
</tr>
{% endfor %}
</tbody>
{% endif %}
</table>
</div>
<div class="row align-items-center justify-content-end py-4 pe-0 fs-9">
<!-- Optional: Pagination -->
{% if is_paginated %}
<nav aria-label="Page navigation">
@ -108,4 +185,7 @@
</nav>
{% endif %}
</div>
</div>
</section>
{% endblock %}

File diff suppressed because it is too large Load Diff