This commit is contained in:
Marwan Alwali 2024-12-26 19:07:08 +03:00
commit a24e5bc82b
18 changed files with 831 additions and 399 deletions

View File

@ -1,4 +1,4 @@
# Generated by Django 5.1.4 on 2024-12-26 10:09
# Generated by Django 4.2.17 on 2024-12-26 10:12
from django.db import migrations, models

View File

@ -1,7 +1,7 @@
# Generated by Django 5.1.4 on 2024-12-26 10:09
# Generated by Django 4.2.17 on 2024-12-26 10:12
import django.db.models.deletion
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):

View File

@ -1,3 +1,5 @@
from phonenumber_field.formfields import PhoneNumberField
from django.core.validators import RegexValidator
from django import forms
from .mixins import AddClassMixin
from django.forms.models import inlineformset_factory
@ -22,7 +24,7 @@ from .models import (
AdditionalServices
)
from django.forms import ModelMultipleChoiceField
from django.forms import ModelMultipleChoiceField, ValidationError
from django.utils.translation import gettext_lazy as _
import django_tables2 as tables
from django.forms import formset_factory
@ -48,7 +50,6 @@ class DealerForm(forms.ModelForm):
model = Dealer
fields = ['name', 'arabic_name', 'crn', 'vrn', 'phone_number', 'address', 'logo']
# Customer Form
class CustomerForm(forms.ModelForm, AddClassMixin):
class Meta:
@ -257,3 +258,148 @@ class CarSelectionTable(tables.Table):
model = Car
fields = ['vin', 'year', 'id_car_make', 'id_car_model']
template_name = "django_tables2/bootstrap4.html"
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.',
}
)
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.',
}
)
class WizardForm2(forms.Form):
# Phone field with SA region validation
phone_number = PhoneNumberField(
label="Phone",
max_length=10,
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Phone',
'required': 'required',
}),
region='SA', # Enforces SA region validation
error_messages={
'required': 'This field is required.',
'invalid': 'Phone number must be in the format +966XXXXXXXXX (Saudi Arabia).',
}
)
# CRN field with max length of 10
crn = forms.CharField(
label="CRN",
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'CRN',
'required': 'required',
'maxlength': '10', # HTML maxlength attribute
}),
max_length=10, # Django max_length validation
error_messages={
'required': 'This field is required.',
'max_length': 'CRN must be at most 10 characters long.',
}
)
# VRN field with max length of 15
vrn = forms.CharField(
label="VRN",
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'VRN',
'required': 'required',
'maxlength': '15', # HTML maxlength attribute
}),
max_length=15, # Django max_length validation
error_messages={
'required': 'This field is required.',
'max_length': 'VRN must be at most 15 characters long.',
}
)
address = forms.CharField(
label="Address",
widget=forms.Textarea(attrs={
'class': 'form-control',
'rows': '4',
'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.',
}
)
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get("password")
confirm_password = cleaned_data.get("confirm_password")
if password and confirm_password and password != confirm_password:
raise forms.ValidationError("Passwords do not match.")

View File

@ -1,11 +1,11 @@
# Generated by Django 5.1.4 on 2024-12-26 10:09
# Generated by Django 4.2.17 on 2024-12-26 10:12
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
import django.db.models.deletion
import inventory.mixins
import phonenumber_field.modelfields
class Migration(migrations.Migration):
@ -13,8 +13,8 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
]
operations = [
@ -66,6 +66,90 @@ 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=[
@ -94,6 +178,25 @@ 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=[
@ -108,6 +211,30 @@ 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=[
@ -130,92 +257,111 @@ class Migration(migrations.Migration):
],
),
migrations.CreateModel(
name='CarFinance',
name='Vendor',
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')),
('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': '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',
'verbose_name': 'Vendor',
'verbose_name_plural': 'Vendors',
},
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
),
migrations.AddField(
model_name='car',
name='id_car_model',
field=models.ForeignKey(blank=True, db_column='id_car_model', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel', verbose_name='Model'),
),
migrations.CreateModel(
name='CarRegistration',
name='SubscriptionUser',
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')),
('subscription', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.subscription')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': '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.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.CreateModel(
name='CarSpecification',
name='SalesOrder',
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')),
('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')),
],
options={
'verbose_name': 'Specification',
'verbose_name': 'Representative',
'verbose_name_plural': 'Representatives',
},
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
),
migrations.CreateModel(
name='Refund',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='amount')),
('reason', models.TextField(blank=True, verbose_name='reason')),
('refund_date', models.DateField(auto_now_add=True, verbose_name='refund date')),
('payment', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='refund', to='inventory.payment')),
],
options={
'verbose_name': 'refund',
'verbose_name_plural': 'refunds',
},
),
migrations.AddField(
model_name='payment',
name='quotation',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='inventory.salequotation'),
),
migrations.AddField(
model_name='customer',
name='dealer',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='customers', to='inventory.dealer'),
),
migrations.CreateModel(
name='CustomCard',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('custom_number', models.CharField(max_length=255, verbose_name='Custom Number')),
('custom_date', models.DateField(verbose_name='Custom Date')),
('car', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='custom_cards', to='inventory.car', verbose_name='Car')),
],
options={
'verbose_name': 'Custom Card',
'verbose_name_plural': 'Custom Cards',
},
),
migrations.CreateModel(
name='CarTrim',
fields=[
@ -245,66 +391,20 @@ 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='CustomCard',
name='CarRegistration',
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')),
('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': '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', 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='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',
'verbose_name': 'Registration',
'verbose_name_plural': 'Registrations',
},
),
migrations.CreateModel(
@ -323,152 +423,56 @@ 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='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')),
],
model_name='car',
name='id_car_make',
field=models.ForeignKey(blank=True, db_column='id_car_make', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake', verbose_name='Make'),
),
migrations.AddField(
model_name='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)),
],
model_name='car',
name='id_car_model',
field=models.ForeignKey(blank=True, db_column='id_car_model', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel', verbose_name='Model'),
),
migrations.AddField(
model_name='subscription',
name='users',
field=models.ManyToManyField(through='inventory.SubscriptionUser', to=settings.AUTH_USER_MODEL),
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='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='id_car_trim',
field=models.ForeignKey(blank=True, db_column='id_car_trim', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.cartrim', verbose_name='Trim'),
),
migrations.AddField(
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

@ -0,0 +1,21 @@
# 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,18 @@
# 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,25 @@
# 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,21 @@
# 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

@ -504,7 +504,7 @@ class Dealer(models.Model, LocalizedNameMixin):
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"))
phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number"),unique=True)
address = models.CharField(
max_length=200, blank=True, null=True, verbose_name=_("Address")
)
@ -513,7 +513,7 @@ class Dealer(models.Model, LocalizedNameMixin):
)
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(unique=True, verbose_name=_("Email"))
email = models.EmailField(verbose_name="Email", unique=True, max_length=100)
parent_dealer = models.ForeignKey(
"self",
on_delete=models.SET_NULL,
@ -529,6 +529,7 @@ class Dealer(models.Model, LocalizedNameMixin):
default=DEALER_TYPES.Owner,
)
@property
def get_active_plan(self):
try:

View File

@ -25,23 +25,25 @@ User = get_user_model()
# if user:
# user.delete()
@receiver(post_save, sender=User)
def create_dealer(instance, created, *args, **kwargs):
if created:
models.Dealer.objects.create(user=instance,name=instance.username,email=instance.email)
# @receiver(post_save, sender=User)
# def create_dealer(instance, created, *args, **kwargs):
# 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:
if instance.dealer_type != "Owner":
user = User.objects.create_user(
username=instance.name,
email=instance.email,
)
user.set_password("Tenhal@123")
user.save()
instance.user = user
instance.save()
# @receiver(post_save, sender=models.Dealer)
# def create_user_account(sender, instance, created, **kwargs):
# if created:
# if instance.dealer_type != "Owner":
# user = User.objects.create_user(
# username=instance.name,
# email=instance.email,
# )
# user.set_password("Tenhal@123")
# user.save()
# instance.user = user
# instance.save()
@receiver(post_save, sender=models.Car)
def create_car_location(sender, instance, created, **kwargs):
@ -81,108 +83,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 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 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")

View File

@ -13,7 +13,8 @@ urlpatterns = [
# Accounts URLs
path('login/', allauth_views.LoginView.as_view(template_name='account/login.html'), name='account_login'),
path('logout/', allauth_views.LogoutView.as_view(template_name='account/logout.html'), name='account_logout'),
path('signup/', allauth_views.SignupView.as_view(template_name='account/signup.html'), name='account_signup'),
# path('signup/', allauth_views.SignupView.as_view(template_name='account/signup.html'), name='account_signup'),
path('signup/', views.SignupView, name='account_signup'),
path('password/change/',
allauth_views.PasswordChangeView.as_view(template_name='account/password_change.html'),
name='account_change_password'),

View File

@ -33,7 +33,7 @@ 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
@ -53,8 +53,7 @@ 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
User = get_user_model()
from django.contrib.auth.models import User
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
@ -95,6 +94,51 @@ def switch_language(request):
logger.warning(f"Invalid language code: {language}")
return redirect("/")
def SignupView(request):
if request.method == "POST":
data = json.loads(request.body)
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")
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.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.")
return JsonResponse({"message": "User created successfully."}, status=200)
except Exception as e:
print("Error creating user:", e)
return JsonResponse({"error": str(e)}, status=400)
form1 = forms.WizardForm1()
form2 = forms.WizardForm2()
form3 = forms.WizardForm3()
return render(request, "account/signup-wizard.html",{"form1": form1, "form2": form2, "form3": form3})
class HomeView(LoginRequiredMixin, TemplateView):
template_name = "dashboards/accounting.html"

View File

@ -85,6 +85,7 @@
if (!!JSON.parse(localStorage.getItem('phoenixIsNavbarVerticalCollapsed'))) {
document.documentElement.classList.add('navbar-vertical-collapsed');
}
if (localStorage.getItem('phoenixTheme') === 'dark') {
document.documentElement.setAttribute('data-bs-theme', 'dark');
@ -102,7 +103,7 @@
if (localStorage.getItem('phoenixNavbarPosition') === 'combo') {
document.documentElement.setAttribute('data-navigation-type', 'combo');
}
var config = {
config: CONFIG,
reset: resetConfig,

View File

@ -1652,6 +1652,7 @@
/* -------------------------------------------------------------------------- */
const createBoardInit = () => {
const { getData } = window.phoenix.utils;
const submit_btn = document.querySelector('#submit_btn');
const selectors = {
CREATE_BOARD: '[data-create-board]',
TOGGLE_BUTTON_EL: '[data-wizard-step]',
@ -1727,6 +1728,8 @@
}
};
/* -------------------------------------------------------------------------- */
/* Detector */
/* -------------------------------------------------------------------------- */

View File

@ -0,0 +1,65 @@
{% extends "base.html" %}
{% load crispy_forms_filters %}
{% load i18n %}
{% 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">
<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>
</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>
</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>
</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>
</a></li>
</ul>
</div>
<div class="card-body pt-4 pb-0">
<div class="tab-content">
<div class="tab-pane active" role="tabpanel" aria-labelledby="bootstrap-wizard-validation-tab1" id="bootstrap-wizard-validation-tab1">
<form class="needs-validation" id="wizardValidationForm1" novalidate="novalidate" data-wizard-form="1">
{{form1|crispy}}
</form>
</div>
<div class="tab-pane" role="tabpanel" aria-labelledby="bootstrap-wizard-validation-tab2" id="bootstrap-wizard-validation-tab2">
<form class="needs-validation" id="wizardValidationForm2" novalidate="novalidate" data-wizard-form="2">
{{form2|crispy}}
</form>
</div>
<div class="tab-pane" role="tabpanel" aria-labelledby="bootstrap-wizard-validation-tab3" id="bootstrap-wizard-validation-tab3">
<form class="needs-validation" id="wizardValidationForm3" novalidate="novalidate" data-wizard-form="3">
{{form3|crispy}}
</form>
</div>
<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>
<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>
</div>
</div>
</div>
</div>
</div>
</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>
<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>
</div>
</div>
</div>
</div>
{% endblock content %}

View File

@ -253,7 +253,7 @@
<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">
<div class="d-flex align-items-center">
{% if data_bs_theme == 'dark' %}
<img src="{% static 'images/logos/logo.png' %}" alt="haikal" width="27" />
{% else %}
@ -683,8 +683,27 @@
<!-- ===============================================-->
<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') {
@ -695,10 +714,7 @@
var navbarVertical = document.querySelector('.navbar-vertical');
if (navbarVertical && navbarVerticalStyle === 'darker') {
navbarVertical.setAttribute('data-navbar-appearance', 'darker');
}
</script>
<script>
}
function save_as_pdf(){
const quotationHtml = document.getElementById('quotation-html').outerHTML;
const printWindow = window.open('', '', 'height=500,width=800');
@ -707,6 +723,85 @@
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>
<!-- ===============================================-->

View File

@ -394,21 +394,6 @@ document.addEventListener("DOMContentLoaded", function() {
codeReader = new ZXing.BrowserMultiFormatReader();
let currentStream = null;
function showLoading() {
Swal.fire({
title: "{% trans 'Please Wait' %}",
text: "{% trans 'Loading' %}...",
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading();
}
});
}
function hideLoading() {
Swal.close();
}
function closeModal(){
stopScanner();
try {

View File

@ -23,7 +23,7 @@
<div class="overlay position-absolute top-0 start-0 w-100 h-100 bg-dark bg-opacity-50 d-flex flex-column justify-content-center align-items-center">
<h1 class="display-4 fw-bold">{{ _("Welcome to ") }}{{ _('HAIKAL') }}</h1>
<p class="lead">{{ _("Discover a seamless way to manage your cars, reservations, and inventory like never before!") }}</p>
<a href="{% url 'signup' %}" class="btn btn-primary btn-lg mt-3">{{ _("Get Started") }}</a>
<a href="{% url 'account_signup' %}" class="btn btn-primary btn-lg mt-3">{{ _("Get Started") }}</a>
</div>
</div>
@ -58,7 +58,7 @@
<!-- Call-to-Action Section -->
<div class="bg-light py-5 text-center">
<h3>{{ _("Ready to take control of your inventory?") }}</h3>
<a href="{% url 'signup' %}" class="btn btn-success btn-lg mt-3">{{ _("Join Us Today") }}</a>
<a href="{% url 'account_signup' %}" class="btn btn-success btn-lg mt-3">{{ _("Join Us Today") }}</a>
</div>
</div>
{% endblock %}