update
This commit is contained in:
parent
9d0485e1dd
commit
cced58632f
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -3,6 +3,7 @@ from . import models
|
||||
|
||||
|
||||
admin.site.register(models.Dealer)
|
||||
admin.site.register(models.Staff)
|
||||
admin.site.register(models.Vendor)
|
||||
admin.site.register(models.Customer)
|
||||
admin.site.register(models.SaleQuotation)
|
||||
|
||||
@ -21,7 +21,8 @@ from .models import (
|
||||
Representative,
|
||||
Payment,
|
||||
SaleQuotationCar,
|
||||
AdditionalServices
|
||||
AdditionalServices,
|
||||
Staff
|
||||
|
||||
)
|
||||
from django_ledger.models import ItemModel
|
||||
@ -41,10 +42,10 @@ class PaymentForm(forms.ModelForm):
|
||||
model = Payment
|
||||
fields = ['amount','payment_method', 'reference_number']
|
||||
|
||||
class UserForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Dealer
|
||||
fields = ['name', 'arabic_name', 'phone_number', 'address','dealer_type']
|
||||
# class UserForm(forms.ModelForm):
|
||||
# class Meta:
|
||||
# model = Staff
|
||||
# fields = ['name', 'arabic_name', 'phone_number', 'address','staff_type']
|
||||
|
||||
# Dealer Form
|
||||
class DealerForm(forms.ModelForm):
|
||||
@ -395,8 +396,10 @@ class WizardForm3(forms.Form):
|
||||
password = cleaned_data.get("password")
|
||||
confirm_password = cleaned_data.get("confirm_password")
|
||||
|
||||
if password and confirm_password and password != confirm_password:
|
||||
if password != confirm_password:
|
||||
raise forms.ValidationError("Passwords do not match.")
|
||||
else:
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class ItemForm(forms.Form):
|
||||
|
||||
@ -0,0 +1,55 @@
|
||||
# Generated by Django 5.1.4 on 2024-12-29 15:46
|
||||
|
||||
import django.db.models.deletion
|
||||
import inventory.mixins
|
||||
import phonenumber_field.modelfields
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0007_vendor_created_at'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='dealer',
|
||||
options={'verbose_name': 'Dealer', 'verbose_name_plural': 'Dealers'},
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='dealer',
|
||||
name='dealer_type',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='dealer',
|
||||
name='parent_dealer',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dealer',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True, verbose_name='Updated At'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Staff',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Name')),
|
||||
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
|
||||
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')),
|
||||
('staff_type', models.CharField(choices=[('manager', 'Manager'), ('inventory', 'Inventory'), ('accountant', 'Accountant'), ('sales', 'Sales'), ('receptionist', 'Receptionist'), ('technician', 'Technician'), ('driver', 'Driver')], 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')),
|
||||
('dealer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='inventory.dealer')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='staff', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Staff',
|
||||
'verbose_name_plural': 'Staff',
|
||||
'permissions': [],
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
]
|
||||
@ -33,7 +33,7 @@ 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, )
|
||||
Dealer.objects.create(user=user, name=dealer_name,arabic_name=arabic_name, crn=crn, vrn=vrn, address=address, **extra_fields)
|
||||
|
||||
return user
|
||||
|
||||
@ -533,16 +533,17 @@ class Dealer(models.Model, LocalizedNameMixin):
|
||||
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"))
|
||||
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,)
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At"))
|
||||
# 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
|
||||
@ -567,25 +568,25 @@ class Dealer(models.Model, LocalizedNameMixin):
|
||||
class Meta:
|
||||
verbose_name = _("Dealer")
|
||||
verbose_name_plural = _("Dealers")
|
||||
permissions = [
|
||||
('change_dealer_type', 'Can change dealer type'),
|
||||
]
|
||||
# permissions = [
|
||||
# ('change_dealer_type', 'Can change dealer type'),
|
||||
# ]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def get_sub_dealers(self):
|
||||
if self.dealer_type == "OWNER":
|
||||
return self.sub_dealers.all()
|
||||
return None
|
||||
|
||||
@property
|
||||
def is_parent(self):
|
||||
return self.dealer_type == "OWNER"
|
||||
@property
|
||||
def get_root_dealer(self):
|
||||
return self.parent_dealer if self.parent_dealer else self
|
||||
# @property
|
||||
# def get_sub_dealers(self):
|
||||
# if self.dealer_type == "OWNER":
|
||||
# return self.sub_dealers.all()
|
||||
# return None
|
||||
#
|
||||
# @property
|
||||
# def is_parent(self):
|
||||
# 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):
|
||||
@ -593,30 +594,30 @@ class Dealer(models.Model, LocalizedNameMixin):
|
||||
# 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 = []
|
||||
class STAFF_TYPES(models.TextChoices):
|
||||
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"))
|
||||
phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number"))
|
||||
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
|
||||
|
||||
@ -3,7 +3,7 @@ from django.dispatch import receiver
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.contrib.auth import get_user_model
|
||||
from django_ledger.io import roles
|
||||
from django_ledger.models import EntityModel,AccountModel,ItemModel,ItemModelAbstract,UnitOfMeasureModel
|
||||
from django_ledger.models import EntityModel,AccountModel,ItemModel,ItemModelAbstract,UnitOfMeasureModel, VendorModel
|
||||
from . import models
|
||||
|
||||
User = get_user_model()
|
||||
@ -49,13 +49,13 @@ def create_car_location(sender, instance, created, **kwargs):
|
||||
"""
|
||||
try:
|
||||
if created:
|
||||
if instance.dealer is None:
|
||||
if instance.user.dealer is None:
|
||||
raise ValueError(f"Cannot create CarLocation for car {instance.vin}: dealer is missing.")
|
||||
|
||||
models.CarLocation.objects.create(
|
||||
car=instance,
|
||||
owner=instance.dealer,
|
||||
showroom=instance.dealer,
|
||||
owner=instance.user.dealer,
|
||||
showroom=instance.user.dealer,
|
||||
description=f"Initial location set for car {instance.vin}."
|
||||
)
|
||||
print("Car Location created")
|
||||
@ -83,105 +83,103 @@ def update_car_status_on_reservation_delete(sender, instance, **kwargs):
|
||||
@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,
|
||||
entity_name = instance.user.dealer.name
|
||||
entity = EntityModel.create_entity(
|
||||
name=entity_name,
|
||||
admin=instance.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}")
|
||||
|
||||
# Create Cash Account
|
||||
entity.create_account(
|
||||
coa_model=coa,
|
||||
code="1010",
|
||||
role=roles.ASSET_CA_CASH,
|
||||
name=_("Cash"),
|
||||
balance_type="debit",
|
||||
active=True,
|
||||
)
|
||||
|
||||
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 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 Cash Account
|
||||
entity.create_account(
|
||||
coa_model=coa,
|
||||
code="1010",
|
||||
role=roles.ASSET_CA_CASH,
|
||||
name=_("Cash"),
|
||||
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 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 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,
|
||||
)
|
||||
|
||||
|
||||
# Create Vendor
|
||||
@ -191,8 +189,9 @@ def create_ledger_vendor(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
entity = EntityModel.objects.filter(name=instance.dealer.name).first()
|
||||
|
||||
entity.create_vendor(
|
||||
name=instance.name,
|
||||
VendorModel.objects.create(
|
||||
entity_model=entity,
|
||||
vendor_name=instance.name,
|
||||
vendor_number=instance.crn,
|
||||
address_1=instance.address,
|
||||
phone=instance.phone_number,
|
||||
@ -202,7 +201,8 @@ def create_ledger_vendor(sender, instance, created, **kwargs):
|
||||
additional_info={
|
||||
"arabic_name": instance.arabic_name,
|
||||
"contact_person": instance.contact_person,
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
print(f"VendorModel created for Vendor: {instance.name}")
|
||||
@ -211,7 +211,7 @@ def create_ledger_vendor(sender, instance, created, **kwargs):
|
||||
@receiver(post_save, sender=models.Customer)
|
||||
def create_customer(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
dealer = instance.dealer.get_root_dealer
|
||||
dealer = instance.dealer
|
||||
entity = dealer.entity
|
||||
name = f"{instance.first_name} {instance.middle_name} {instance.last_name}"
|
||||
if entity:
|
||||
@ -241,14 +241,8 @@ def create_item_model(sender, instance, created, **kwargs):
|
||||
if not entity:
|
||||
return
|
||||
|
||||
uom_name = _("Car")
|
||||
unit_abbr = _("C")
|
||||
uom = entity.get_uom_all().filter(name=uom_name, unit_abbr=unit_abbr).first()
|
||||
if not uom:
|
||||
uom = entity.create_uom(
|
||||
name=uom_name,
|
||||
unit_abbr=unit_abbr
|
||||
)
|
||||
|
||||
uom = entity.get_uom_all()
|
||||
|
||||
entity.create_item_product(
|
||||
name=item_name,
|
||||
|
||||
@ -87,11 +87,11 @@ urlpatterns = [
|
||||
path('sales/quotations/<int:pk>/payment/', views.payment_create, name='payment_create'),
|
||||
|
||||
# Users URLs
|
||||
path('user/create/', views.UserCreateView.as_view(), name='user_create'),
|
||||
path('user/<int:pk>/update/', views.UserUpdateView.as_view(), name='user_update'),
|
||||
path('user/<int:pk>/', views.UserDetailView.as_view(), name='user_detail'),
|
||||
path('user/', views.UserListView.as_view(), name='user_list'),
|
||||
path('user/<int:pk>/confirm/', views.UserDeleteview, name='user_delete'),
|
||||
# path('user/create/', views.UserCreateView.as_view(), name='user_create'),
|
||||
# path('user/<int:pk>/update/', views.UserUpdateView.as_view(), name='user_update'),
|
||||
# path('user/<int:pk>/', views.UserDetailView.as_view(), name='user_detail'),
|
||||
# path('user/', views.UserListView.as_view(), name='user_list'),
|
||||
# path('user/<int:pk>/confirm/', views.UserDeleteview, name='user_delete'),
|
||||
# Organization URLs
|
||||
path('organizations/', views.OrganizationListView.as_view(), name='organization_list'),
|
||||
path('organizations/<int:pk>/', views.OrganizationDetailView.as_view(), name='organization_detail'),
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django_ledger.models import EntityModel, InvoiceModel,BankAccountModel,AccountModel,JournalEntryModel,TransactionModel,EstimateModel,CustomerModel
|
||||
|
||||
from django_ledger.forms.bank_account import BankAccountCreateForm,BankAccountUpdateForm
|
||||
@ -46,6 +47,7 @@ from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.contrib.auth.models import Group
|
||||
from .utils import get_calculations
|
||||
from django.contrib.auth.models import User
|
||||
from allauth.account import views
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
@ -93,15 +95,16 @@ def dealer_signup(request, *args, **kwargs):
|
||||
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")
|
||||
password = wf1.get("password")
|
||||
password_confirm = wf1.get("confirm_password")
|
||||
name = wf2.get("name")
|
||||
arabic_name = wf2.get("arabic_name")
|
||||
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")
|
||||
crn = wf3.get("crn")
|
||||
vrn = wf3.get("vrn")
|
||||
address = wf3.get("address")
|
||||
|
||||
|
||||
if password != password_confirm:
|
||||
return JsonResponse({"error": "Passwords do not match."}, status=400)
|
||||
@ -109,16 +112,14 @@ def dealer_signup(request, *args, **kwargs):
|
||||
try:
|
||||
with transaction.atomic():
|
||||
user = User.objects.create(email=email, password=password)
|
||||
user.set_password(password)
|
||||
user.save()
|
||||
|
||||
models.Dealer.objects.create(user=user,
|
||||
name=name,
|
||||
arabic_name=arabic_name,
|
||||
crn=crn,
|
||||
vrn=vrn,
|
||||
phone_number=phone,
|
||||
address=address,
|
||||
dealer_type="OWNER",)
|
||||
address=address,)
|
||||
return JsonResponse({"message": "User created successfully."}, status=200)
|
||||
except Exception as e:
|
||||
return JsonResponse({"error": str(e)}, status=400)
|
||||
@ -143,8 +144,8 @@ class AccountingDashboard(LoginRequiredMixin, TemplateView):
|
||||
|
||||
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"])
|
||||
not request.user.is_authenticated
|
||||
):
|
||||
# messages.error(request, _("You are not associated with any dealer."))
|
||||
return redirect("welcome")
|
||||
@ -192,7 +193,7 @@ class CarCreateView(LoginRequiredMixin, CreateView):
|
||||
return reverse("inventory_stats")
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.dealer = self.request.user.dealer.get_root_dealer
|
||||
form.instance.dealer = self.request.user.dealer
|
||||
form.save()
|
||||
messages.success(self.request, "Car saved successfully.")
|
||||
return super().form_valid(form)
|
||||
@ -347,7 +348,7 @@ class CarInventory(LoginRequiredMixin, ListView):
|
||||
trim_id = self.kwargs['trim_id']
|
||||
|
||||
cars = models.Car.objects.filter(
|
||||
dealer=self.request.user.dealer.get_root_dealer,
|
||||
dealer=self.request.user.dealer,
|
||||
id_car_make=make_id,
|
||||
id_car_model=model_id,
|
||||
id_car_trim=trim_id,
|
||||
@ -391,7 +392,7 @@ def inventory_stats_view(request):
|
||||
|
||||
# Annotate total cars by make, model, and trim
|
||||
cars = (
|
||||
models.Car.objects.filter(dealer=dealer.get_root_dealer)
|
||||
models.Car.objects.filter(dealer=dealer)
|
||||
.select_related("id_car_make", "id_car_model", "id_car_trim")
|
||||
.annotate(
|
||||
make_total=Count("id_car_make"),
|
||||
@ -636,17 +637,17 @@ class DealerUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
def get_success_url(self):
|
||||
return reverse("dealer_detail", kwargs={"pk": self.object.pk})
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
if hasattr(form.fields, "dealer_type"):
|
||||
form.fields.pop("dealer_type")
|
||||
return form
|
||||
|
||||
def get_form_class(self):
|
||||
if self.request.user.dealer.dealer_type == "OWNER":
|
||||
return forms.DealerForm
|
||||
else:
|
||||
return forms.UserForm
|
||||
# def get_form(self, form_class=None):
|
||||
# form = super().get_form(form_class)
|
||||
# if hasattr(form.fields, "dealer_type"):
|
||||
# form.fields.pop("dealer_type")
|
||||
# return form
|
||||
#
|
||||
# def get_form_class(self):
|
||||
# if self.request.user.dealer.dealer_type == "OWNER":
|
||||
# return forms.DealerForm
|
||||
# else:
|
||||
# return forms.UserForm
|
||||
|
||||
|
||||
class CustomerListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||||
@ -660,7 +661,7 @@ class CustomerListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||||
def get_queryset(self):
|
||||
query = self.request.GET.get("q")
|
||||
customers = models.Customer.objects.filter(
|
||||
dealer=self.request.user.dealer.get_root_dealer
|
||||
dealer=self.request.user.dealer
|
||||
)
|
||||
|
||||
if query:
|
||||
@ -697,6 +698,10 @@ class CustomerCreateView(
|
||||
permission_required = ("inventory.add_customer",)
|
||||
success_message = _("Customer created successfully.")
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.dealer = self.request.user.dealer
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
|
||||
class CustomerUpdateView(
|
||||
@ -784,7 +789,7 @@ class QuotationCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateVie
|
||||
permission_required = ("inventory.add_salequotation",)
|
||||
|
||||
def form_valid(self, form):
|
||||
dealer = self.request.user.dealer.get_root_dealer
|
||||
dealer = self.request.user.dealer
|
||||
form.instance.dealer = dealer
|
||||
quotation = form.save()
|
||||
selected_cars = form.cleaned_data.get("cars")
|
||||
@ -809,7 +814,7 @@ class QuotationListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||||
|
||||
def get_queryset(self):
|
||||
status = self.request.GET.get("status")
|
||||
queryset = self.request.user.dealer.get_root_dealer.sales.all()
|
||||
queryset = self.request.user.dealer.sales.all()
|
||||
if status:
|
||||
queryset = queryset.filter(status=status)
|
||||
return queryset
|
||||
@ -834,7 +839,7 @@ class QuotationDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailVie
|
||||
@login_required
|
||||
def generate_invoice(request, pk):
|
||||
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||
dealer = request.user.dealer.get_root_dealer
|
||||
dealer = request.user.dealer
|
||||
entity = dealer.entity
|
||||
if not quotation.is_approved:
|
||||
messages.error(
|
||||
@ -981,7 +986,7 @@ def generate_invoice(request, pk):
|
||||
@login_required
|
||||
def post_quotation(request, pk):
|
||||
qoutation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||
dealer = request.user.dealer.get_root_dealer
|
||||
dealer = request.user.dealer
|
||||
entity = dealer.entity
|
||||
if qoutation.posted:
|
||||
messages.error(request, "Quotation is already posted")
|
||||
@ -1037,7 +1042,7 @@ def post_quotation(request, pk):
|
||||
def mark_quotation(request, pk):
|
||||
qoutation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||
status = request.GET.get("status")
|
||||
dealer = request.user.dealer.get_root_dealer
|
||||
dealer = request.user.dealer
|
||||
entity = dealer.entity
|
||||
date = datetime.datetime.now()
|
||||
customer = entity.get_customers().filter(customer_name=qoutation.customer.get_full_name).first()
|
||||
@ -1052,7 +1057,7 @@ def mark_quotation(request, pk):
|
||||
messages.error(request, "Quotation is not ready for approval")
|
||||
return redirect("quotation_detail", pk=pk)
|
||||
|
||||
invoice_model.mark_as_approved(entity_slug=entity.slug, user_model=request.user.dealer.get_root_dealer.user)
|
||||
invoice_model.mark_as_approved(entity_slug=entity.slug, user_model=request.user.dealer)
|
||||
invoice_model.date_approved = date
|
||||
qoutation.date_approved = date
|
||||
invoice_model.save()
|
||||
@ -1072,7 +1077,7 @@ def mark_quotation(request, pk):
|
||||
messages.error(request, "Quotation is not ready for payment")
|
||||
return redirect("quotation_detail", pk=pk)
|
||||
|
||||
invoice_model.mark_as_paid(entity_slug=entity.slug, user_model=request.user.dealer.get_root_dealer.user)
|
||||
invoice_model.mark_as_paid(entity_slug=entity.slug, user_model=request.user.dealer)
|
||||
invoice_model.date_paid = date
|
||||
qoutation.date_paid = date
|
||||
invoice_model.save()
|
||||
@ -1117,115 +1122,115 @@ class SalesOrderDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailVi
|
||||
|
||||
|
||||
# Users
|
||||
class UserListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||||
model = models.Dealer
|
||||
context_object_name = "users"
|
||||
paginate_by = 10
|
||||
template_name = "users/user_list.html"
|
||||
permission_required = ("inventory.view_dealer",)
|
||||
|
||||
def get_queryset(self):
|
||||
query = self.request.GET.get("q")
|
||||
users = self.request.user.dealer.sub_dealers
|
||||
|
||||
if query:
|
||||
users = users.filter(
|
||||
Q(name__icontains=query)
|
||||
| Q(arabic_name__icontains=query)
|
||||
| Q(phone_number__icontains=query)
|
||||
| Q(address__icontains=query)
|
||||
)
|
||||
return users.all()
|
||||
# class UserListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||||
# model = models.Staff
|
||||
# context_object_name = "users"
|
||||
# paginate_by = 10
|
||||
# template_name = "users/user_list.html"
|
||||
# permission_required = ("inventory.view_dealer",)
|
||||
#
|
||||
# def get_queryset(self):
|
||||
# query = self.request.GET.get("q")
|
||||
# users = self.request.user.dealer.staff.all()
|
||||
#
|
||||
# if query:
|
||||
# users = users.filter(
|
||||
# Q(name__icontains=query)
|
||||
# | Q(arabic_name__icontains=query)
|
||||
# | Q(phone_number__icontains=query)
|
||||
# | Q(address__icontains=query)
|
||||
# )
|
||||
# return users.all()
|
||||
#
|
||||
#
|
||||
# class UserDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
|
||||
# model = models.Staff
|
||||
# template_name = "users/user_detail.html"
|
||||
# context_object_name = "user_"
|
||||
# permission_required = ("inventory.view_dealer",)
|
||||
|
||||
|
||||
class UserDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
|
||||
model = models.Dealer
|
||||
template_name = "users/user_detail.html"
|
||||
context_object_name = "user_"
|
||||
permission_required = ("inventory.view_dealer",)
|
||||
# class UserCreateView(
|
||||
# LoginRequiredMixin,
|
||||
# PermissionRequiredMixin,
|
||||
# SuccessMessageMixin,
|
||||
# CreateView,
|
||||
# ):
|
||||
# model = models.Staff
|
||||
# form_class = forms.UserForm
|
||||
# template_name = "users/user_form.html"
|
||||
# success_url = reverse_lazy("user_list")
|
||||
# permission_required = ("inventory.add_dealer",)
|
||||
# success_message = _("User created successfully.")
|
||||
#
|
||||
# def get_form(self, form_class=None):
|
||||
# form = super().get_form(form_class)
|
||||
# form.fields["staff_type"].choices = [
|
||||
# t for t in form.fields["staff_type"].choices if t[0] != "OWNER"
|
||||
# ]
|
||||
# return form
|
||||
#
|
||||
# def form_valid(self, form):
|
||||
# dealer = self.request.user.dealer
|
||||
# if dealer.sub_dealers.count() >= dealer.get_active_plan.max_users:
|
||||
# messages.error(
|
||||
# self.request, _("You have reached the maximum number of users.")
|
||||
# )
|
||||
# return redirect("user_list")
|
||||
#
|
||||
# user = User.objects.create_user(username=form.cleaned_data["name"])
|
||||
# user.set_password("Tenhal@123")
|
||||
# user.save()
|
||||
# form.instance.user = user
|
||||
# form.instance.parent_dealer = dealer
|
||||
# for group in user.groups.all():
|
||||
# group.user_set.remove(user)
|
||||
# Group.objects.get(name=form.cleaned_data["dealer_type"].lower()).user_set.add(
|
||||
# user
|
||||
# )
|
||||
# form.save()
|
||||
# return super().form_valid(form)
|
||||
|
||||
|
||||
class UserCreateView(
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
SuccessMessageMixin,
|
||||
CreateView,
|
||||
):
|
||||
model = models.Dealer
|
||||
form_class = forms.UserForm
|
||||
template_name = "users/user_form.html"
|
||||
success_url = reverse_lazy("user_list")
|
||||
permission_required = ("inventory.add_dealer",)
|
||||
success_message = _("User created successfully.")
|
||||
|
||||
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"
|
||||
]
|
||||
return form
|
||||
|
||||
def form_valid(self, form):
|
||||
dealer = self.request.user.dealer.get_root_dealer
|
||||
if dealer.sub_dealers.count() >= dealer.get_active_plan.max_users:
|
||||
messages.error(
|
||||
self.request, _("You have reached the maximum number of users.")
|
||||
)
|
||||
return redirect("user_list")
|
||||
|
||||
user = User.objects.create_user(username=form.cleaned_data["name"])
|
||||
user.set_password("Tenhal@123")
|
||||
user.save()
|
||||
form.instance.user = user
|
||||
form.instance.parent_dealer = dealer
|
||||
for group in user.groups.all():
|
||||
group.user_set.remove(user)
|
||||
Group.objects.get(name=form.cleaned_data["dealer_type"].lower()).user_set.add(
|
||||
user
|
||||
)
|
||||
form.save()
|
||||
return super().form_valid(form)
|
||||
# class UserUpdateView(
|
||||
# LoginRequiredMixin,
|
||||
# PermissionRequiredMixin,
|
||||
# SuccessMessageMixin,
|
||||
# UpdateView,
|
||||
# ):
|
||||
# model = models.Dealer
|
||||
# form_class = forms.UserForm
|
||||
# template_name = "users/user_form.html"
|
||||
# success_url = reverse_lazy("user_list")
|
||||
# permission_required = ("inventory.change_dealer",)
|
||||
# success_message = _("User updated successfully.")
|
||||
#
|
||||
# def get_form(self, form_class=None):
|
||||
# form = super().get_form(form_class)
|
||||
# if not self.request.user.has_perms(["inventory.change_dealer_type"]):
|
||||
# field = form.fields["dealer_type"]
|
||||
# field.widget = field.hidden_widget()
|
||||
# form.fields["dealer_type"].choices = [
|
||||
# t for t in form.fields["dealer_type"].choices if t[0] != "Owner"
|
||||
# ]
|
||||
# return form
|
||||
#
|
||||
# def form_valid(self, form):
|
||||
# user = form.instance.user
|
||||
# for group in user.groups.all():
|
||||
# group.user_set.remove(user)
|
||||
# Group.objects.get(name=form.cleaned_data["dealer_type"].lower()).user_set.add(
|
||||
# user
|
||||
# )
|
||||
# form.save()
|
||||
# return super().form_valid(form)
|
||||
|
||||
|
||||
class UserUpdateView(
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
SuccessMessageMixin,
|
||||
UpdateView,
|
||||
):
|
||||
model = models.Dealer
|
||||
form_class = forms.UserForm
|
||||
template_name = "users/user_form.html"
|
||||
success_url = reverse_lazy("user_list")
|
||||
permission_required = ("inventory.change_dealer",)
|
||||
success_message = _("User updated successfully.")
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
if not self.request.user.has_perms(["inventory.change_dealer_type"]):
|
||||
field = form.fields["dealer_type"]
|
||||
field.widget = field.hidden_widget()
|
||||
form.fields["dealer_type"].choices = [
|
||||
t for t in form.fields["dealer_type"].choices if t[0] != "Owner"
|
||||
]
|
||||
return form
|
||||
|
||||
def form_valid(self, form):
|
||||
user = form.instance.user
|
||||
for group in user.groups.all():
|
||||
group.user_set.remove(user)
|
||||
Group.objects.get(name=form.cleaned_data["dealer_type"].lower()).user_set.add(
|
||||
user
|
||||
)
|
||||
form.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
def UserDeleteview(request, pk):
|
||||
user = get_object_or_404(models.Dealer, pk=pk)
|
||||
user.delete()
|
||||
messages.success(request, _("User deleted successfully."))
|
||||
return redirect("user_list")
|
||||
# def UserDeleteview(request, pk):
|
||||
# user = get_object_or_404(models.Dealer, pk=pk)
|
||||
# user.delete()
|
||||
# messages.success(request, _("User deleted successfully."))
|
||||
# return redirect("user_list")
|
||||
|
||||
|
||||
# errors
|
||||
@ -1266,7 +1271,7 @@ class OrganizationCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView
|
||||
|
||||
def form_valid(self, form):
|
||||
if form.is_valid():
|
||||
form.instance.dealer = self.request.user.dealer.get_root_dealer
|
||||
form.instance.dealer = self.request.user.dealer
|
||||
form.save()
|
||||
return super().form_valid(form)
|
||||
else:
|
||||
@ -1309,7 +1314,7 @@ class RepresentativeCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateVi
|
||||
|
||||
def form_valid(self, form):
|
||||
if form.is_valid():
|
||||
form.instance.dealer = self.request.user.dealer.get_root_dealer
|
||||
form.instance.dealer = self.request.user.dealer
|
||||
form.save()
|
||||
return super().form_valid(form)
|
||||
else:
|
||||
@ -1382,7 +1387,7 @@ def download_quotation_pdf(request, quotation_id):
|
||||
@login_required
|
||||
def invoice_detail(request,pk):
|
||||
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||
dealer = request.user.dealer.get_root_dealer
|
||||
dealer = request.user.dealer
|
||||
entity = dealer.entity
|
||||
customer = entity.get_customers().filter(customer_name=quotation.customer.get_full_name).first()
|
||||
invoice_model = entity.get_invoices()
|
||||
@ -1395,7 +1400,7 @@ def invoice_detail(request,pk):
|
||||
@login_required
|
||||
def payment_invoice(request,pk):
|
||||
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||
dealer = request.user.dealer.get_root_dealer
|
||||
dealer = request.user.dealer
|
||||
entity = dealer.entity
|
||||
customer = entity.get_customers().filter(customer_name=quotation.customer.get_full_name).first()
|
||||
invoice_model = entity.get_invoices()
|
||||
@ -1425,14 +1430,14 @@ def payment_invoice(request,pk):
|
||||
|
||||
def payment_create(request, pk):
|
||||
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||
dealer = request.user.dealer.get_root_dealer
|
||||
dealer = request.user.dealer
|
||||
if request.method == "POST":
|
||||
form = forms.PaymentForm(request.POST)
|
||||
if form.is_valid():
|
||||
form.instance.quotation = quotation
|
||||
insatnce = form.save()
|
||||
|
||||
dealer = request.user.dealer.get_root_dealer
|
||||
dealer = request.user.dealer
|
||||
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()
|
||||
@ -1461,7 +1466,7 @@ def payment_create(request, pk):
|
||||
|
||||
invoice_model = entity.get_invoices().filter(date_approved=quotation.date_approved).first()
|
||||
|
||||
invoice_model.mark_as_paid(entity_slug=entity.slug, user_model=request.user.dealer.get_root_dealer.user)
|
||||
invoice_model.mark_as_paid(entity_slug=entity.slug, user_model=request.user.dealer)
|
||||
date = timezone.now()
|
||||
invoice_model.date_paid = date
|
||||
quotation.date_paid = date
|
||||
|
||||
BIN
templates/.DS_Store
vendored
BIN
templates/.DS_Store
vendored
Binary file not shown.
@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "auth_base.html" %}
|
||||
{% load crispy_forms_filters %}
|
||||
{% load i18n static %}
|
||||
|
||||
|
||||
229
templates/auth_base.html
Normal file
229
templates/auth_base.html
Normal file
@ -0,0 +1,229 @@
|
||||
{% load static %} {% load i18n %}
|
||||
<!DOCTYPE html>
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<html lang="{{ LANGUAGE_CODE }}"
|
||||
dir="{% if LANGUAGE_CODE == 'ar' %}rtl{% else %}ltr{% endif %}"
|
||||
data-bs-theme=""
|
||||
data-navigation-type="default"
|
||||
data-navbar-horizontal-shape="default">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="Haikal - The Backbone of Car Qar: An innovative car inventory management system designed to streamline dealership operations. Manage inventory, sales, transfers, and accounting seamlessly with advanced analytics and intuitive tools. Inspired by Arabic origins, Haikal empowers businesses with precision and efficiency.">
|
||||
|
||||
<title>{% block title %}{% trans 'HAIKAL' %}{% endblock %}</title>
|
||||
|
||||
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{% static 'images/favicons/apple-touch-icon.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{% static 'images/favicons/favicon-32x32.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="{% static 'images/favicons/favicon-16x16.png' %}">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="{% static 'images/favicons/favicon.ico' %}">
|
||||
<link rel="manifest" href="{% static 'images/favicons/manifest.json' %}">
|
||||
<meta name="msapplication-TileImage" content="{% static 'images/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/sweetalert2.all.min.js' %}"></script>
|
||||
|
||||
|
||||
|
||||
<!-- ===============================================-->
|
||||
<!-- Stylesheets-->
|
||||
<!-- ===============================================-->
|
||||
|
||||
|
||||
<link href="{% static 'vendors/mapbox-gl/mapbox-gl.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'vendors/swiper/swiper-bundle.min.css' %}" rel="stylesheet">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;600;700;800;900&display=swap" rel="stylesheet">
|
||||
<link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'css/sweetalert2.min.css' %}" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css">
|
||||
{% if LANGUAGE_CODE == 'en' %}
|
||||
<link href="{% static 'css/theme.min.css' %}" type="text/css" rel="stylesheet" id="style-default">
|
||||
<link href="{% static 'css/user.min.css' %}" type="text/css" rel="stylesheet" id="user-style-default">
|
||||
{% else %}
|
||||
<link href="{% static 'css/theme-rtl.min.css' %}" type="text/css" rel="stylesheet" id="style-rtl">
|
||||
<link href="{% static 'css/user-rtl.min.css' %}" type="text/css" rel="stylesheet" id="user-style-rtl">
|
||||
{% endif %}
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{% include 'messages.html' %}
|
||||
<main class="main" id="top">
|
||||
|
||||
<div class="content">
|
||||
|
||||
{% block content %}
|
||||
<!-- Main content goes here -->
|
||||
{% endblock %}
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
<!-- ===============================================-->
|
||||
<!-- End of Main Content-->
|
||||
<!-- ===============================================-->
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
{% block extra_js %}{% endblock extra_js %}
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
// Function to calculate Total Cost and Total Revenue
|
||||
function calculateTotals(container) {
|
||||
const quantity = parseFloat(container.querySelector('.quantity').value) || 0;
|
||||
const unitCost = parseFloat(container.querySelector('.unitCost').value) || 0;
|
||||
const unitSalesPrice = parseFloat(container.querySelector('.unitSalesPrice').value) || 0;
|
||||
|
||||
const totalCost = quantity * unitCost;
|
||||
const totalRevenue = quantity * unitSalesPrice;
|
||||
|
||||
container.querySelector('.totalCost').value = totalCost.toFixed(2);
|
||||
container.querySelector('.totalRevenue').value = totalRevenue.toFixed(2);
|
||||
}
|
||||
|
||||
// Add event listeners to inputs for dynamic calculation
|
||||
function addInputListeners(container) {
|
||||
container.querySelectorAll('.quantity, .unitCost, .unitSalesPrice').forEach(input => {
|
||||
input.addEventListener('input', () => calculateTotals(container));
|
||||
});
|
||||
}
|
||||
|
||||
// Add new form fields
|
||||
document.getElementById('addMoreBtn').addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
const formContainer = document.getElementById('formContainer');
|
||||
const newForm = document.createElement('div');
|
||||
newForm.className = 'form-container row g-3 mb-3 mt-5';
|
||||
newForm.innerHTML = `
|
||||
<div class="mb-2 col-sm-2">
|
||||
<select class="form-control item" name="item[]" required>
|
||||
{% for item in items %}
|
||||
<option value="{{ item.pk }}">{{ item.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-2 col-sm-2">
|
||||
<input class="form-control quantity" type="number" placeholder="Quantity" name="quantity[]" required>
|
||||
</div>
|
||||
<div class="mb-2 col-sm-2">
|
||||
<input class="form-control unitCost" type="number" placeholder="Unit Cost" name="unitCost[]" step="0.01" required>
|
||||
</div>
|
||||
<div class="mb-2 col-sm-2">
|
||||
<input class="form-control unitSalesPrice" type="number" placeholder="Unit Sales Price" name="unitSalesPrice[]" step="0.01" required>
|
||||
</div>
|
||||
<div class="mb-2 col-sm-2">
|
||||
<input class="form-control totalCost" type="number" placeholder="Total Cost" name="totalCost[]" readonly>
|
||||
</div>
|
||||
<div class="mb-2 col-sm-1">
|
||||
<input class="form-control totalRevenue" type="number" placeholder="Total Revenue" name="totalRevenue[]" readonly>
|
||||
</div>
|
||||
<div class="mb-2 col-sm-1">
|
||||
<button class="btn btn-danger removeBtn">Remove</button>
|
||||
</div>
|
||||
`;
|
||||
formContainer.appendChild(newForm);
|
||||
addInputListeners(newForm); // Add listeners to the new form
|
||||
|
||||
// Add remove button functionality
|
||||
newForm.querySelector('.removeBtn').addEventListener('click', function() {
|
||||
newForm.remove();
|
||||
});
|
||||
});
|
||||
|
||||
// Add listeners to the initial form
|
||||
document.querySelectorAll('.form-container').forEach(container => {
|
||||
addInputListeners(container);
|
||||
|
||||
// Add remove button functionality to the initial form
|
||||
container.querySelector('.removeBtn').addEventListener('click', function() {
|
||||
container.remove();
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('mainForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Collect all form data
|
||||
const formData = new FormData(this);
|
||||
const csrfToken = getCookie('csrftoken');
|
||||
const data = {};
|
||||
formData.forEach((value, key) => {
|
||||
// Handle multi-value fields (e.g., item[], quantity[])
|
||||
if (data[key]) {
|
||||
if (!Array.isArray(data[key])) {
|
||||
data[key] = [data[key]]; // Convert to array
|
||||
}
|
||||
data[key].push(value);
|
||||
} else {
|
||||
data[key] = value;
|
||||
}
|
||||
});
|
||||
// Send data to the server using fetch
|
||||
fetch('http://10.10.1.120:8888/en/sales/estimates/create/', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-CSRFToken': csrfToken,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log('Success:', data);
|
||||
if(data.status == "error"){
|
||||
notify("error",data.message);
|
||||
}
|
||||
else{
|
||||
notify("success","Estimate created successfully");
|
||||
setTimeout(() => {
|
||||
window.location.href = data.url;
|
||||
}, 1000);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
notify("error",error);
|
||||
alert('An error occurred while submitting the form.');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<!-- ===============================================-->
|
||||
<!-- JavaScripts-->
|
||||
<!-- ===============================================-->
|
||||
<script src="{% static 'vendors/popper/popper.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/bootstrap/bootstrap.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/anchorjs/anchor.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/is/is.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/fontawesome/all.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/lodash/lodash.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/list.js/list.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/feather-icons/feather.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/dayjs/dayjs.min.js' %}"></script>
|
||||
<script src="{% static 'js/phoenix.js' %}"></script>
|
||||
<script src="{% static 'vendors/echarts/echarts.min.js' %}"></script>
|
||||
<script src="{% static 'js/travel-agency-dashboard.js' %}"></script>
|
||||
<script src="{% static 'vendors/mapbox-gl/mapbox-gl.js' %}"></script>
|
||||
<script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script>
|
||||
<script src="{% static 'vendors/swiper/swiper-bundle.min.js' %}"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user