2299 lines
77 KiB
Python
2299 lines
77 KiB
Python
from django.core.cache import cache
|
|
from datetime import datetime
|
|
from luhnchecker.luhn import Luhn
|
|
from django.contrib.auth.models import Permission
|
|
|
|
# from appointment.models import Service
|
|
from django.core.validators import MinLengthValidator
|
|
from django import forms
|
|
from plans.models import PlanPricing
|
|
from django.contrib.auth import get_user_model
|
|
|
|
from inventory.validators import SaudiPhoneNumberValidator, vat_rate_validator
|
|
from .models import CustomGroup, Status, Stage
|
|
from .mixins import AddClassMixin
|
|
from django_ledger.forms.invoice import (
|
|
InvoiceModelCreateForm as InvoiceModelCreateFormBase,
|
|
)
|
|
from django.forms.models import inlineformset_factory
|
|
from django_ledger.forms.bill import BillModelCreateForm as BillModelCreateFormBase
|
|
from django.contrib.auth.forms import PasswordChangeForm, SetPasswordForm
|
|
from django_ledger.forms.journal_entry import (
|
|
JournalEntryModelCreateForm as JournalEntryModelCreateFormBase,
|
|
)
|
|
import csv
|
|
from io import TextIOWrapper
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from .models import (
|
|
Dealer,
|
|
DealersMake,
|
|
Vendor,
|
|
Schedule,
|
|
Car,
|
|
VatRate,
|
|
CarTransfer,
|
|
# CarFinance,
|
|
CustomCard,
|
|
CarRegistration,
|
|
CarColors,
|
|
ExteriorColors,
|
|
InteriorColors,
|
|
CarLocation,
|
|
Representative,
|
|
AdditionalServices,
|
|
Staff,
|
|
Opportunity,
|
|
Lead,
|
|
Activity,
|
|
Notes,
|
|
CarModel,
|
|
CarSerie,
|
|
CarTrim,
|
|
SaleOrder,
|
|
CarMake,
|
|
Customer,
|
|
Organization,
|
|
DealerSettings,
|
|
Tasks,
|
|
Recall,
|
|
Ticket,
|
|
UserRegistration,
|
|
)
|
|
from django_ledger import models as ledger_models
|
|
from django.forms import (
|
|
DateInput,
|
|
DateTimeInput,
|
|
)
|
|
from django.utils.translation import gettext_lazy as _
|
|
import django_tables2 as tables
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
class SaudiPhoneNumberField(forms.CharField):
|
|
def __init__(self, *args, **kwargs):
|
|
kwargs.setdefault("min_length", 10)
|
|
kwargs.setdefault("max_length", 13)
|
|
super().__init__(*args, **kwargs)
|
|
self.validators.append(SaudiPhoneNumberValidator())
|
|
|
|
|
|
class AdditionalServiceForm(forms.ModelForm):
|
|
"""
|
|
A form used for creating and updating instances of the
|
|
`AdditionalServices` model with fields for specifying
|
|
service details such as name, price, description, tax
|
|
applicability, and unit of measure.
|
|
|
|
This form is designed to streamline input validation and
|
|
rendering for the `AdditionalServices` model.
|
|
|
|
:ivar Meta.model: The model to associate with this form.
|
|
:type Meta.model: type
|
|
:ivar Meta.fields: List of fields in the `AdditionalServices`
|
|
model that this form includes.
|
|
:type Meta.fields: list
|
|
"""
|
|
|
|
class Meta:
|
|
model = AdditionalServices
|
|
fields = ["name", "price", "description", "taxable", "uom"]
|
|
|
|
|
|
class StaffForm(forms.ModelForm):
|
|
"""
|
|
Represents a form for managing Staff entities, including associated user email updates
|
|
and service offerings.
|
|
|
|
This class defines a Django ModelForm for the `Staff` model, providing fields
|
|
to manage staff details such as name, Arabic name, phone number, staff type, and email.
|
|
It allows selection of multiple services offered by staff through a multiple choice
|
|
field. The form integrates email management as an additional feature.
|
|
|
|
:ivar email: Email address of the user associated with the staff.
|
|
:type email: forms.EmailField
|
|
:ivar service_offered: Collection of services offered by the staff.
|
|
:type service_offered: forms.ModelMultipleChoiceField
|
|
"""
|
|
|
|
email = forms.EmailField(
|
|
required=True,
|
|
label=_("Email"),
|
|
widget=forms.EmailInput(attrs={"class": "form-control form-control-sm"}),
|
|
)
|
|
|
|
# service_offered = forms.ModelMultipleChoiceField(
|
|
# label=_("Services Offered"),
|
|
# widget=forms.CheckboxSelectMultiple(attrs={"class": "form-check-input"}),
|
|
# # queryset=Service.objects.all(),
|
|
# required=False,
|
|
# )
|
|
# phone_number = SaudiPhoneNumberField(
|
|
# required=False,
|
|
# widget=forms.TextInput(
|
|
# attrs={
|
|
# "class": "form-control",
|
|
# "placeholder": _("Phone Number"),
|
|
# "id": "phone",
|
|
# }
|
|
# ),
|
|
# label=_("Phone Number"),
|
|
# )
|
|
group = forms.ModelMultipleChoiceField(
|
|
label=_("Group"),
|
|
widget=forms.CheckboxSelectMultiple(attrs={"class": "form-check-input"}),
|
|
queryset=CustomGroup.objects.all(),
|
|
required=True,
|
|
)
|
|
|
|
class Meta:
|
|
model = Staff
|
|
fields = [
|
|
"first_name",
|
|
"last_name",
|
|
"arabic_name",
|
|
"phone_number",
|
|
"address",
|
|
"logo",
|
|
"group",
|
|
]
|
|
|
|
|
|
# Dealer Form
|
|
class DealerForm(forms.ModelForm):
|
|
"""
|
|
Represents a Django ModelForm specifically for the `Dealer` model.
|
|
|
|
Designed to facilitate the creation, update, and validation of `Dealer` model
|
|
instances through form-based data handling. This form exposes specific
|
|
fields of the `Dealer` model for interaction.
|
|
|
|
:ivar name: Dealer's name.
|
|
:type name: str
|
|
:ivar arabic_name: Dealer's name in Arabic.
|
|
:type arabic_name: str
|
|
:ivar crn: Commercial Registration Number for the dealer.
|
|
:type crn: str
|
|
:ivar vrn: Value-added Tax Registration Number for the dealer.
|
|
:type vrn: str
|
|
:ivar phone_number: Contact phone number of the dealer.
|
|
:type phone_number: str
|
|
:ivar address: Physical address of the dealer.
|
|
:type address: str
|
|
:ivar logo: Logo of the dealer.
|
|
:type logo: File
|
|
|
|
"""
|
|
|
|
# phone_number = SaudiPhoneNumberField(label=_("Phone Number"))
|
|
|
|
class Meta:
|
|
model = Dealer
|
|
fields = [
|
|
"name",
|
|
"arabic_name",
|
|
"crn",
|
|
"vrn",
|
|
"phone_number",
|
|
"address",
|
|
"logo",
|
|
]
|
|
|
|
|
|
class CustomerForm(forms.ModelForm):
|
|
# phone_number = SaudiPhoneNumberField(label=_("Phone Number"))
|
|
|
|
class Meta:
|
|
model = Customer
|
|
fields = [
|
|
"title",
|
|
"first_name",
|
|
"last_name",
|
|
"email",
|
|
"phone_number",
|
|
"national_id",
|
|
"dob",
|
|
"address",
|
|
"image",
|
|
]
|
|
widgets = {
|
|
"title": forms.Select(attrs={"class": "form-control form-control-sm"}),
|
|
"first_name": forms.TextInput(
|
|
attrs={"class": "form-control form-control-sm"}
|
|
),
|
|
"last_name": forms.TextInput(
|
|
attrs={"class": "form-control form-control-sm"}
|
|
),
|
|
"email": forms.EmailInput(attrs={"class": "form-control form-control-sm"}),
|
|
"phone_number": forms.TextInput(
|
|
attrs={"class": "form-control form-control-sm"}
|
|
),
|
|
"national_id": forms.TextInput(
|
|
attrs={"class": "form-control form-control-sm"}
|
|
),
|
|
"dob": DateInput(
|
|
attrs={"class": "form-control form-control-sm", "type": "date"}
|
|
),
|
|
"address": forms.Textarea(attrs={"class": "form-control form-control-sm"}),
|
|
"image": forms.FileInput(attrs={"class": "form-control form-control-sm"}),
|
|
}
|
|
|
|
|
|
# class CustomerForm(forms.Form):
|
|
# """
|
|
# Represents a form for collecting customer information.
|
|
|
|
# This form is used to gather and validate customer details such as name,
|
|
# email, phone number, national ID, tax registration details, and address.
|
|
# It includes several fields with validation constraints and specific
|
|
# requirements. It is designed to handle both required and optional fields,
|
|
# ensuring the correctness of user inputs.
|
|
|
|
# :ivar first_name: Customer's first name.
|
|
# :type first_name: forms.CharField
|
|
# :ivar last_name: Customer's last name.
|
|
# :type last_name: forms.CharField
|
|
# :ivar arabic_name: Customer's name in Arabic.
|
|
# :type arabic_name: forms.CharField
|
|
# :ivar email: Customer's email address.
|
|
# :type email: forms.EmailField
|
|
# :ivar phone_number: Customer's phone number. Validates the format and
|
|
# ensures the number is in the Saudi Arabia region.
|
|
# :type phone_number: PhoneNumberField
|
|
# :ivar national_id: Customer's national ID. Optional field limited to
|
|
# a maximum length of 10 characters.
|
|
# :type national_id: forms.CharField
|
|
# :ivar crn: Commercial registration number (CRN) of the customer. Optional field.
|
|
# :type crn: forms.CharField
|
|
# :ivar vrn: Value-added tax registration number (VRN) of the customer.
|
|
# Optional field.
|
|
# :type vrn: forms.CharField
|
|
# :ivar address: Customer's address.
|
|
# :type address: forms.CharField
|
|
# """
|
|
# first_name = forms.CharField()
|
|
# last_name = forms.CharField()
|
|
# arabic_name = forms.CharField()
|
|
# email = forms.EmailField()
|
|
# # phone_number = PhoneNumberField(
|
|
# # label=_("Phone Number"),
|
|
# # widget=forms.TextInput(
|
|
# # attrs={
|
|
# # "placeholder": _("Phone"),
|
|
# # }
|
|
# # ),
|
|
# # region="SA",
|
|
# # error_messages={
|
|
# # "required": _("This field is required."),
|
|
# # "invalid": _("Phone number must be in the format 05xxxxxxxx"),
|
|
# # },
|
|
# # required=True,
|
|
# # )
|
|
# phone_number = forms.CharField(label=_("Phone Number"),min_length=10,max_length=10,validators=[RegexValidator(regex='^05[0-9]{8}$')], required=True)
|
|
# national_id = forms.CharField(max_length=10,required=False)
|
|
# crn = forms.CharField(required=False)
|
|
# vrn = forms.CharField(required=False)
|
|
# address = forms.CharField()
|
|
# image = forms.ImageField(required=False)
|
|
|
|
|
|
class OrganizationForm(forms.ModelForm):
|
|
"""
|
|
Represents a form for collecting and handling organization-specific details.
|
|
|
|
This form extends the `CustomerForm` class and is tailored for organizations,
|
|
providing fields for optional input such as the contact person and organization
|
|
logo. The form can be used to capture all relevant data pertaining to the
|
|
organization during interactions.
|
|
|
|
:ivar contact_person: Optional field to specify the name of the contact person
|
|
for the organization.
|
|
:type contact_person: forms.CharField
|
|
:ivar logo: Optional field to upload the logo of the organization.
|
|
:type logo: forms.ImageField
|
|
"""
|
|
|
|
phone_number = SaudiPhoneNumberField(label=_("Phone Number"), required=True)
|
|
|
|
class Meta:
|
|
model = Organization
|
|
fields = [
|
|
"name",
|
|
"arabic_name",
|
|
"email",
|
|
"phone_number",
|
|
"crn",
|
|
"vrn",
|
|
"address",
|
|
"logo",
|
|
]
|
|
|
|
|
|
class CarForm(
|
|
forms.ModelForm,
|
|
AddClassMixin,
|
|
):
|
|
"""
|
|
A form class for creating and updating `Car` model instances.
|
|
|
|
This class extends `forms.ModelForm` and `AddClassMixin` to customize
|
|
form fields for the `Car` model. It manages custom field configurations,
|
|
such as filtering queryset and setting widgets, and is designed to be used
|
|
for handling the form representation of the `Car` model within a user interface.
|
|
|
|
:ivar Meta.model: Specifies the model associated with this form.
|
|
:type Meta.model: Car
|
|
:ivar Meta.fields: Specifies the fields included in the form.
|
|
:type Meta.fields: list
|
|
:ivar Meta.widgets: Defines the widgets for specific fields.
|
|
:type Meta.widgets: dict
|
|
"""
|
|
|
|
class Meta:
|
|
model = Car
|
|
fields = [
|
|
"vin",
|
|
"id_car_make",
|
|
"id_car_model",
|
|
"year",
|
|
"id_car_serie",
|
|
"id_car_trim",
|
|
"stock_type",
|
|
"remarks",
|
|
"mileage",
|
|
"receiving_date",
|
|
"vendor",
|
|
]
|
|
required_fields = [
|
|
"vin",
|
|
"id_car_make",
|
|
"id_car_model",
|
|
"id_car_serie",
|
|
"id_car_trim",
|
|
"vendor",
|
|
]
|
|
widgets = {
|
|
"id_car_make": forms.Select(attrs={"class": "form-select form-select-sm"}),
|
|
"receiving_date": forms.DateTimeInput(attrs={"type": "datetime-local"}),
|
|
"remarks": forms.Textarea(attrs={"rows": 2}),
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
dealer = kwargs.pop("dealer", None)
|
|
super().__init__(*args, **kwargs)
|
|
|
|
if "id_car_make" in self.fields:
|
|
queryset = self.fields["id_car_make"].queryset.filter(is_sa_import=True)
|
|
self.fields["id_car_make"].choices = [
|
|
(obj.id_car_make, obj.get_local_name()) for obj in queryset
|
|
]
|
|
if "id_car_model" in self.fields:
|
|
queryset = self.fields["id_car_model"].queryset
|
|
self.fields["id_car_model"].choices = [
|
|
(obj.id_car_model, obj.get_local_name()) for obj in queryset
|
|
]
|
|
# if "vendor" in self.fields:
|
|
# self.fields["vendor"].queryset = dealer.vendors.all()
|
|
|
|
|
|
class CarUpdateForm(forms.ModelForm, AddClassMixin):
|
|
"""
|
|
Form class used to update Car instances through a Django ModelForm. This class
|
|
extends `forms.ModelForm` and applies custom widgets and field configurations
|
|
to support specific requirements for the Car model.
|
|
|
|
The form is intended for managing Car-related information, allowing inputs
|
|
for vendor details, status, stock type, mileage, receiving date, and remarks.
|
|
It also provides customization options for field configurations through its
|
|
initialization method.
|
|
|
|
:ivar Meta.model: The model associated with the form, which is `Car`.
|
|
:ivar Meta.fields: Fields of the `Car` model included in the form: "vendor",
|
|
"status", "stock_type", "mileage", "receiving_date", "remarks".
|
|
:ivar Meta.widgets: Custom widgets for specific fields. The "receiving_date" field
|
|
uses a `DateTimeInput` configured with `datetime-local` attributes, while
|
|
"remarks" uses a `Textarea` with a specific row configuration.
|
|
"""
|
|
|
|
class Meta:
|
|
model = Car
|
|
fields = [
|
|
"vendor",
|
|
"status",
|
|
"stock_type",
|
|
"mileage",
|
|
"receiving_date",
|
|
"remarks",
|
|
]
|
|
|
|
widgets = {
|
|
"receiving_date": forms.DateTimeInput(attrs={"type": "datetime-local"}),
|
|
"remarks": forms.Textarea(attrs={"rows": 2}),
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
dealer = kwargs.pop("dealer", None)
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
class CarFinanceForm(forms.ModelForm):
|
|
"""
|
|
Handles the form logic for car finance, including processing
|
|
additional financial services selected through the UI.
|
|
|
|
This class is used for creating or modifying instances of the CarFinance model.
|
|
It provides a mechanism to associate additional financial services with a car
|
|
finance application through a multiple choice selection.
|
|
|
|
:ivar additional_finances: A field that allows selecting multiple
|
|
additional services associated with a car finance application.
|
|
"""
|
|
|
|
def clean(self):
|
|
cleaned_data = super().clean()
|
|
cost_price = cleaned_data.get("cost_price")
|
|
marked_price = cleaned_data.get("marked_price")
|
|
|
|
if cost_price > marked_price:
|
|
raise forms.ValidationError(
|
|
{"cost_price": "Cost price should not be greater than marked price"}
|
|
)
|
|
|
|
return cleaned_data
|
|
|
|
class Meta:
|
|
model = Car
|
|
fields = ["cost_price", "marked_price"]
|
|
|
|
|
|
class CarLocationForm(forms.ModelForm):
|
|
"""
|
|
Represents a Django ModelForm for managing CarLocation instances.
|
|
|
|
This form is designed to create or update `CarLocation` model objects.
|
|
It provides a user-friendly interface for inputting data, including custom
|
|
widget configurations for the `description` field.
|
|
|
|
:ivar Meta: Contains metadata for the form, including the associated model,
|
|
specified fields, and custom widget configurations.
|
|
:type Meta: class
|
|
"""
|
|
|
|
class Meta:
|
|
model = CarLocation
|
|
fields = ["showroom", "description"]
|
|
widgets = {
|
|
"description": forms.Textarea(attrs={"rows": 2, "class": "form-control"}),
|
|
}
|
|
|
|
|
|
class CarTransferForm(forms.ModelForm):
|
|
"""
|
|
Represents a form for transferring a car to a dealer.
|
|
|
|
This form is used to facilitate the transfer of a car from one entity
|
|
to a dealer. It extends the ModelForm framework to provide functionality
|
|
specific to car transfer operations.
|
|
|
|
:ivar model: The model associated with this form.
|
|
:type model: Type[CarTransfer]
|
|
:ivar fields: The fields included in the form.
|
|
:type fields: list[str]
|
|
:ivar widgets: Custom widgets applied to the form fields.
|
|
:type widgets: dict[str, Any]
|
|
"""
|
|
|
|
class Meta:
|
|
model = CarTransfer
|
|
fields = ["car", "to_dealer", "remarks"]
|
|
widgets = {
|
|
"remarks": forms.Textarea(attrs={"rows": 2, "class": "form-control"}),
|
|
}
|
|
|
|
|
|
# Custom Card Form
|
|
class CustomCardForm(forms.ModelForm):
|
|
"""
|
|
Representation of a custom card form used for collecting and validating custom card data.
|
|
|
|
This form is a subclass of ``forms.ModelForm`` and is specifically tailored for
|
|
the ``CustomCard`` model. It provides a user-friendly interface by predefining
|
|
the fields and allowing customization of widgets and labels for seamless user
|
|
interaction. The form integrates with Django's model validation system to ensure
|
|
data integrity before saving to the database.
|
|
|
|
:ivar custom_date: The custom date field that accepts user input for a date
|
|
value. This field uses a custom widget to render a date input type and
|
|
includes a specific label for localized presentation.
|
|
:type custom_date: forms.DateTimeField
|
|
"""
|
|
|
|
custom_date = forms.DateTimeField(
|
|
widget=forms.DateInput(attrs={"type": "date"}),
|
|
label=_("Custom Date"),
|
|
)
|
|
|
|
class Meta:
|
|
model = CustomCard
|
|
fields = ["custom_number", "custom_date"]
|
|
|
|
|
|
# Car Registration Form
|
|
class CarRegistrationForm(forms.ModelForm):
|
|
"""
|
|
Represents a Django form for car registration.
|
|
|
|
This class is a Django ModelForm specifically designed to handle car
|
|
registration data. It is linked to the `CarRegistration` model and
|
|
provides fields for entering or managing a license plate number,
|
|
additional textual fields, and the registration date. This form also
|
|
customizes the widget for the `registration_date` field to use a
|
|
datetime-local input.
|
|
|
|
:ivar Meta.model: The `CarRegistration` model to associate with this form.
|
|
:type Meta.model: Model
|
|
:ivar Meta.fields: List of fields to include in the form.
|
|
:type Meta.fields: list
|
|
:ivar Meta.widgets: Custom widget configurations for specific fields.
|
|
:type Meta.widgets: dict
|
|
"""
|
|
|
|
class Meta:
|
|
model = CarRegistration
|
|
fields = ["plate_number", "text1", "text2", "text3", "registration_date"]
|
|
|
|
widgets = {
|
|
"registration_date": forms.DateTimeInput(attrs={"type": "datetime-local"}),
|
|
}
|
|
|
|
|
|
# class VendorForm(VendorModelForm):
|
|
# pass
|
|
class VendorForm(forms.ModelForm):
|
|
"""
|
|
Representation of a form used for Vendor model interactions.
|
|
|
|
This class extends the functionality of Django's ModelForm to facilitate
|
|
CRUD operations for the Vendor model. It includes validation and mapping of
|
|
fields defined in the Vendor model to their corresponding form fields.
|
|
|
|
:ivar Meta: Inner class to define metadata for the Vendor form.
|
|
:type Meta: Type[VendorForm.Meta]
|
|
"""
|
|
|
|
# phone_number = SaudiPhoneNumberField(label=_("Phone Number"))
|
|
contact_person = forms.CharField(label=_("Contact Person"))
|
|
|
|
class Meta:
|
|
model = Vendor
|
|
fields = [
|
|
"name",
|
|
"arabic_name",
|
|
"crn",
|
|
"vrn",
|
|
"email",
|
|
"phone_number",
|
|
"contact_person",
|
|
"address",
|
|
"logo",
|
|
]
|
|
|
|
unique_together = (
|
|
("dealer", "crn"),
|
|
("dealer", "vrn"),
|
|
("dealer", "email"),
|
|
("dealer", "phone_number"),
|
|
)
|
|
|
|
|
|
class CarColorsForm(forms.ModelForm):
|
|
"""
|
|
Handles the dynamic customization and validation of color selection forms
|
|
for car exterior and interior.
|
|
|
|
This class provides a mechanism to dynamically populate querysets and choices
|
|
for exterior and interior color fields in the form, as well as enforce
|
|
business rules such as ensuring that the user selects values for both
|
|
exterior and interior colors.
|
|
|
|
:ivar Meta.model: The model associated with this form, which is ``CarColors``.
|
|
:type Meta.model: Model
|
|
:ivar Meta.fields: List of form fields that correspond to model attributes
|
|
to include in the form, namely ``exterior`` and ``interior``.
|
|
:type Meta.fields: list
|
|
"""
|
|
|
|
class Meta:
|
|
model = CarColors
|
|
fields = ["exterior", "interior"]
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
self.fields["exterior"].queryset = ExteriorColors.objects.all()
|
|
self.fields["exterior"].widget = forms.RadioSelect(
|
|
attrs={"class": "form-check-input"}
|
|
)
|
|
self.fields["exterior"].choices = [
|
|
(color.id, f"{color.get_local_name}")
|
|
for color in ExteriorColors.objects.all().order_by("-name")
|
|
]
|
|
|
|
self.fields["interior"].queryset = InteriorColors.objects.all()
|
|
self.fields["interior"].widget = forms.RadioSelect(
|
|
attrs={"class": "form-check-input"}
|
|
)
|
|
self.fields["interior"].choices = [
|
|
(color.id, f"{color.get_local_name}")
|
|
for color in InteriorColors.objects.all().order_by("-name")
|
|
]
|
|
|
|
def clean(self):
|
|
cleaned_data = super().clean()
|
|
exterior = cleaned_data.get("exterior")
|
|
interior = cleaned_data.get("interior")
|
|
|
|
if not exterior or not interior:
|
|
raise forms.ValidationError(
|
|
_("Both exterior and interior colors must be selected.")
|
|
)
|
|
|
|
return cleaned_data
|
|
|
|
|
|
class RepresentativeForm(forms.ModelForm):
|
|
"""
|
|
A form for creating or updating instances of the Representative model.
|
|
|
|
The RepresentativeForm class inherits from Django's ModelForm and is customized
|
|
to work with the Representative model. This class initializes a form instance
|
|
with fields relevant to a representative, including their name, contact details,
|
|
and associated organization. It supports dynamic customization by passing additional
|
|
parameters during initialization.
|
|
|
|
:ivar Meta.model: The Django model associated with this form.
|
|
:type Meta.model: type
|
|
:ivar Meta.fields: The fields from the model to include in the form.
|
|
:type Meta.fields: list of str
|
|
"""
|
|
|
|
# phone_number = SaudiPhoneNumberField(label=_("Phone Number"))
|
|
|
|
class Meta:
|
|
model = Representative
|
|
fields = [
|
|
"name",
|
|
"arabic_name",
|
|
"id_number",
|
|
"phone_number",
|
|
"address",
|
|
"organization",
|
|
]
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
dealer = kwargs.pop("dealer", None)
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
class CarSelectionTable(tables.Table):
|
|
"""
|
|
Creates a table for car selection using Django Tables2.
|
|
|
|
This class is used to generate a table with a checkbox column for selecting cars.
|
|
It leverages Django Tables2 functionality to display table rows corresponding to
|
|
cars, along with metadata for VIN, year, car make, and car model. The table is
|
|
rendered using the Bootstrap 4 template for consistent styling.
|
|
|
|
:ivar select: Checkbox column for selecting a car, linked to the primary key.
|
|
:type select: CheckBoxColumn
|
|
:ivar Meta: Metadata containing configuration for the table, including the
|
|
model, fields, and template.
|
|
:type Meta: django_tables2.tables.Table.Meta
|
|
"""
|
|
|
|
select = tables.CheckBoxColumn(accessor="pk", orderable=False)
|
|
|
|
class Meta:
|
|
model = Car
|
|
fields = ["vin", "year", "id_car_make", "id_car_model"]
|
|
template_name = "django_tables2/bootstrap4.html"
|
|
|
|
|
|
class WizardForm1(forms.Form):
|
|
"""
|
|
Represents a form used for the first step of a wizard validation process.
|
|
|
|
This class is a Django form designed for a multi-step process and includes
|
|
fields for collecting user information such as email, password, and consent
|
|
to terms. It provides validation for fields, such as confirming password
|
|
matching and preventing duplicate email addresses. The form incorporates user
|
|
interaction attributes for frontend handling during the wizard workflow.
|
|
|
|
:ivar hx_attrs: Default attributes for htmx library operations, such as\
|
|
defining the request behavior and target elements.
|
|
:type hx_attrs: dict
|
|
:ivar email: Field for entering the user's email address. Includes validation\
|
|
to ensure uniqueness of the email.
|
|
:type email: django.forms.EmailField
|
|
:ivar password: Field for entering a password with a minimum length of 8 and\
|
|
appropriate input styling.
|
|
:type password: django.forms.CharField
|
|
:ivar confirm_password: Field for confirming the password matches the entry\
|
|
in the password field.
|
|
:type confirm_password: django.forms.CharField
|
|
:ivar terms: Field representing acceptance of the terms and privacy policy.\
|
|
This is a required checkbox input.
|
|
:type terms: django.forms.BooleanField
|
|
"""
|
|
|
|
hx_attrs = {
|
|
"hx-post": "",
|
|
"hx-target": "#wizardValidationForm1",
|
|
"hx-select": "#wizardValidationForm1",
|
|
"hx-trigger": "blur delay:500ms",
|
|
"hx-swap": "innerHTML",
|
|
}
|
|
email = forms.EmailField(
|
|
label=_("Email Address"),
|
|
widget=forms.EmailInput(
|
|
attrs={
|
|
"class": "form-control form-control-sm email",
|
|
"placeholder": _("Email address"),
|
|
"name": _("email"),
|
|
"required": "required",
|
|
# **hx_attrs
|
|
}
|
|
),
|
|
error_messages={
|
|
"required": _("You must add an email."),
|
|
},
|
|
)
|
|
|
|
password = forms.CharField(
|
|
label=_("Password"),
|
|
widget=forms.PasswordInput(
|
|
attrs={
|
|
"class": "form-control form-control-sm",
|
|
"placeholder": _("Password"),
|
|
"required": "required",
|
|
# **hx_attrs
|
|
},
|
|
render_value=True,
|
|
),
|
|
error_messages={
|
|
"required": _("This field is required."),
|
|
},
|
|
min_length=8,
|
|
)
|
|
|
|
confirm_password = forms.CharField(
|
|
label=_("Confirm Password"),
|
|
widget=forms.PasswordInput(
|
|
attrs={
|
|
"class": "form-control form-control-sm",
|
|
"placeholder": _("Confirm Password"),
|
|
"required": "required",
|
|
# **hx_attrs
|
|
},
|
|
render_value=True,
|
|
),
|
|
error_messages={
|
|
"required": _("This field is required."),
|
|
},
|
|
min_length=8,
|
|
)
|
|
|
|
terms = forms.BooleanField(
|
|
label=_("I accept the Terms and Privacy Policy"),
|
|
widget=forms.CheckboxInput(
|
|
attrs={
|
|
"class": "form-check-input",
|
|
"required": "required",
|
|
# **hx_attrs
|
|
}
|
|
),
|
|
error_messages={
|
|
"required": _("You must accept the terms and privacy policy."),
|
|
},
|
|
)
|
|
|
|
def clean_email(self):
|
|
email = self.cleaned_data.get("email")
|
|
if email:
|
|
if User.objects.filter(email=email).exists():
|
|
raise forms.ValidationError(
|
|
_("An account with this email already exists.")
|
|
)
|
|
return email
|
|
|
|
def clean_confirm_password(self):
|
|
password = self.cleaned_data.get("password")
|
|
confirm_password = self.cleaned_data.get("confirm_password")
|
|
if password and confirm_password and password != confirm_password:
|
|
raise forms.ValidationError(_("Passwords do not match."))
|
|
return confirm_password
|
|
|
|
|
|
class WizardForm2(forms.Form):
|
|
"""
|
|
Form for capturing specific user details, including name, Arabic name,
|
|
and a phone number.
|
|
|
|
This form is primarily designed to capture user information in both
|
|
English and Arabic formats along with ensuring the phone number follows
|
|
a valid format specifically for the region it is intended for. It makes
|
|
use of form controls and placeholder attributes for user-friendly input
|
|
handling.
|
|
|
|
:ivar name: English name of the user. It is a required field and provides
|
|
user-friendly input placeholders and custom error messages.
|
|
:type name: forms.CharField
|
|
:ivar arabic_name: Arabic name of the user. It is a required field with
|
|
user-friendly placeholders and custom error messages.
|
|
:type arabic_name: forms.CharField
|
|
:ivar phone_number: Contact phone number of the user. Validation is
|
|
applied to ensure format compliance, with custom error handling
|
|
and region-specific configuration.
|
|
:type phone_number: PhoneNumberField
|
|
"""
|
|
|
|
name = forms.CharField(
|
|
label=_("Name"),
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": "form-control form-control-sm",
|
|
"placeholder": _("English Name"),
|
|
"required": "required",
|
|
}
|
|
),
|
|
error_messages={
|
|
"required": _("Please enter an English Name."),
|
|
},
|
|
)
|
|
|
|
arabic_name = forms.CharField(
|
|
label=_("Arabic Name"),
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": "form-control form-control-sm",
|
|
"placeholder": _("Arabic Name"),
|
|
"required": "required",
|
|
}
|
|
),
|
|
error_messages={
|
|
"required": _("Please enter an Arabic name."),
|
|
},
|
|
)
|
|
|
|
# phone_number = PhoneNumberField(
|
|
# label=_("Phone Number"),
|
|
# widget=forms.TextInput(
|
|
# attrs={
|
|
# "placeholder": _("Phone"),
|
|
# }
|
|
# ),
|
|
# region="SA",
|
|
# error_messages={
|
|
# "required": _("This field is required."),
|
|
# "invalid": _("Phone number must be in the format 05xxxxxxxx"),
|
|
# },
|
|
# required=True,
|
|
# )
|
|
phone_number = SaudiPhoneNumberField(label=_("Phone Number"))
|
|
|
|
|
|
class WizardForm3(forms.Form):
|
|
"""
|
|
Form for collecting specific business-related information.
|
|
|
|
This class defines a Django form for collecting business information, particularly
|
|
the Commercial Registration Number (CRN), VAT Registration Number (VRN), and the address.
|
|
Each field has custom validation rules, user-friendly error messages, and associated
|
|
widgets for user input.
|
|
|
|
:ivar crn: Field for the Commercial Registration Number (CRN), with a maximum
|
|
length of 10 characters. This field is required.
|
|
:type crn: django.forms.fields.CharField
|
|
:ivar vrn: Field for the VAT Registration Number (VRN), with a maximum
|
|
length of 15 characters. This field is required.
|
|
:type vrn: django.forms.fields.CharField
|
|
:ivar address: Field for the address input, supporting multi-line input,
|
|
with validation enforcing the field to be required.
|
|
:type address: django.forms.fields.CharField
|
|
"""
|
|
|
|
# CRN field with max length of 10
|
|
crn = forms.CharField(
|
|
label=_("CRN"),
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": "form-control form-control-sm",
|
|
"placeholder": _("Commercial Registration Number"),
|
|
"required": "required",
|
|
"maxlength": "10",
|
|
}
|
|
),
|
|
max_length=10,
|
|
error_messages={
|
|
"required": _("This field is required."),
|
|
"max_length": _("Commercial Registration Number must be 10 characters"),
|
|
},
|
|
)
|
|
|
|
# VRN field with max length of 15
|
|
vrn = forms.CharField(
|
|
label=_("VRN"),
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": "form-control form-control-sm",
|
|
"placeholder": _("VAT Registration Number"),
|
|
"required": "required",
|
|
"maxlength": "15",
|
|
}
|
|
),
|
|
max_length=15, #
|
|
error_messages={
|
|
"required": _("This field is required."),
|
|
"max_length": _("VAT Registration Number must be 15 characters."),
|
|
},
|
|
)
|
|
|
|
address = forms.CharField(
|
|
label=_("Address"),
|
|
widget=forms.Textarea(
|
|
attrs={
|
|
"class": "form-control form-control-sm",
|
|
"rows": "3",
|
|
"required": "required",
|
|
}
|
|
),
|
|
error_messages={
|
|
"required": _("This field is required."),
|
|
},
|
|
)
|
|
|
|
|
|
class ItemForm(forms.Form):
|
|
"""
|
|
A form for handling item-related inputs in the application.
|
|
|
|
This form is used to manage user-provided data for items, including
|
|
the selection of an item and its quantity. It enforces specific rules,
|
|
such as a minimum length for the item and requires certain inputs.
|
|
|
|
:ivar item: A field representing the selection of an item from a
|
|
predefined queryset. Subject to a minimum length validator.
|
|
:type item: ModelChoiceField
|
|
:ivar quantity: A field for specifying the desired quantity of the
|
|
selected item.
|
|
:type quantity: DecimalField
|
|
"""
|
|
|
|
item = forms.ModelChoiceField(
|
|
queryset=ledger_models.ItemModel.objects.all(),
|
|
label=_("Item"),
|
|
required=True,
|
|
validators=[MinLengthValidator(5)],
|
|
)
|
|
quantity = forms.DecimalField(label=_("Quantity"), required=True)
|
|
|
|
|
|
class PaymentForm(forms.Form):
|
|
"""
|
|
Form for handling payment-related data submission and validation.
|
|
|
|
This form is designed to manage various payment-related fields, including
|
|
invoice and bill associations, payment amount and method, and payment date.
|
|
It validates the payment amount against the outstanding dues of the specified
|
|
invoice or bill and ensures logical correctness for the provided data.
|
|
|
|
:ivar invoice: Reference to an optional invoice for the payment.
|
|
:type invoice: ModelChoiceField
|
|
:ivar bill: Reference to an optional bill for the payment.
|
|
:type bill: ModelChoiceField
|
|
:ivar amount: The payment amount being submitted.
|
|
:type amount: DecimalField
|
|
:ivar payment_method: The method chosen to process the payment.
|
|
:type payment_method: ChoiceField
|
|
:ivar payment_date: The date on which the payment is made.
|
|
:type payment_date: DateField
|
|
"""
|
|
|
|
invoice = forms.ModelChoiceField(
|
|
queryset=ledger_models.InvoiceModel.objects.all(),
|
|
label=_("Invoice"),
|
|
required=False,
|
|
)
|
|
bill = forms.ModelChoiceField(
|
|
queryset=ledger_models.BillModel.objects.all(), label=_("Bill"), required=False
|
|
)
|
|
amount = forms.DecimalField(label=_("Amount"), required=True)
|
|
payment_method = forms.ChoiceField(
|
|
choices=[
|
|
("cash", _("cash")),
|
|
("credit", _("credit")),
|
|
("transfer", _("transfer")),
|
|
("debit", _("debit")),
|
|
("SADAD", _("SADAD")),
|
|
],
|
|
label=_("Payment Method"),
|
|
required=True,
|
|
)
|
|
payment_date = forms.DateField(
|
|
label=_("Payment Date"), widget=DateInput(attrs={"type": "date"}), required=True
|
|
)
|
|
|
|
def clean_amount(self):
|
|
invoice = self.cleaned_data["invoice"]
|
|
bill = self.cleaned_data["bill"]
|
|
model = invoice if invoice else bill
|
|
amount = self.cleaned_data["amount"]
|
|
if amount + model.amount_paid > model.amount_due:
|
|
raise forms.ValidationError(_("Payment amount is greater than amount due"))
|
|
if amount <= 0:
|
|
raise forms.ValidationError(_("Payment amount must be greater than 0"))
|
|
if model.is_paid():
|
|
raise forms.ValidationError(_("Invoice is already paid"))
|
|
if amount > model.amount_due:
|
|
raise forms.ValidationError(_("Payment amount is greater than amount due"))
|
|
return amount
|
|
|
|
|
|
class EmailForm(forms.Form):
|
|
"""
|
|
Represents an email form for collecting and validating email data.
|
|
|
|
This class is designed to handle input related to email communication,
|
|
including subject, message body, sender email, and recipient email. It
|
|
validates the provided input to ensure they adhere to the specified
|
|
constraints. This form is typically used in applications where users
|
|
need to send emails through a web interface.
|
|
|
|
:ivar subject: The subject line of the email.
|
|
:type subject: forms.CharField
|
|
:ivar message: The main body content of the email.
|
|
:type message: forms.CharField
|
|
:ivar from_email: The sender's email address.
|
|
:type from_email: forms.EmailField
|
|
:ivar to_email: The recipient's email address.
|
|
:type to_email: forms.EmailField
|
|
"""
|
|
|
|
subject = forms.CharField(max_length=255)
|
|
message = forms.CharField(widget=forms.Textarea)
|
|
from_email = forms.EmailField()
|
|
to_email = forms.EmailField(label=_("To"))
|
|
|
|
|
|
class LeadForm(forms.ModelForm):
|
|
"""
|
|
Form class for capturing lead details.
|
|
|
|
This class is a Django ModelForm specifically designed for the `Lead` model.
|
|
It extends the capabilities of a standard Django form by providing customized
|
|
handling for fields related to car make and model. The `id_car_make` field includes
|
|
integration with htmx for dynamic model selections.
|
|
|
|
:ivar id_car_make: A dropdown field for selecting a car make. This field is
|
|
dynamically updated and restricts the queryset to only South African imports.
|
|
:type id_car_make: ModelChoiceField
|
|
:ivar id_car_model: A dropdown field for selecting a car model. Initially, no
|
|
options are displayed until a car make is selected.
|
|
:type id_car_model: ModelChoiceField
|
|
"""
|
|
|
|
# phone_number = SaudiPhoneNumberField(label=_("Phone Number"))
|
|
|
|
# email = forms.EmailField(
|
|
# label=_("Email"),
|
|
# widget=forms.EmailInput(
|
|
# attrs={
|
|
# "hx-post": "",
|
|
# "hx-include": "#id_email",
|
|
# "hx-select": "#div_id_email",
|
|
# "hx-target": "#div_id_email",
|
|
# "hx-trigger":"blur delay:1s, focusout",
|
|
# "hx-swap": "outerHTML",
|
|
# "hx-indicator": "#spinner",
|
|
# }
|
|
# ),
|
|
# required=True,
|
|
# )
|
|
id_car_make = forms.ModelChoiceField(
|
|
label=_("Make"),
|
|
queryset=CarMake.objects.filter(is_sa_import=True),
|
|
widget=forms.Select(
|
|
attrs={
|
|
"hx-get": "",
|
|
"hx-include": "#id_id_car_make",
|
|
"hx-select": "#div_id_id_car_model",
|
|
"hx-target": "#div_id_id_car_model",
|
|
"hx-swap": "outerHTML",
|
|
"hx-on::before-request": "document.querySelector('#id_id_car_model').setAttribute('disabled', true)",
|
|
"hx-on::after-request": "document.querySelector('#id_id_car_model').removeAttribute('disabled')",
|
|
"hx-indicator": "#spinner",
|
|
}
|
|
),
|
|
required=True,
|
|
)
|
|
id_car_model = forms.ModelChoiceField(
|
|
label=_("Model"),
|
|
queryset=CarModel.objects.none(),
|
|
widget=forms.Select(),
|
|
required=True,
|
|
)
|
|
|
|
class Meta:
|
|
model = Lead
|
|
fields = [
|
|
"first_name",
|
|
"last_name",
|
|
"email",
|
|
"phone_number",
|
|
"address",
|
|
"lead_type",
|
|
"id_car_make",
|
|
"id_car_model",
|
|
"source",
|
|
"channel",
|
|
"staff",
|
|
]
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
def filter_qs(self, **kwargs):
|
|
dealer = kwargs["dealer"]
|
|
dealer_make_list = DealersMake.objects.filter(dealer=dealer).values_list(
|
|
"car_make", flat=True
|
|
)
|
|
if "id_car_make" in self.fields:
|
|
queryset = self.fields["id_car_make"].queryset.filter(
|
|
is_sa_import=True, pk__in=dealer_make_list
|
|
)
|
|
self.fields["id_car_make"].choices = [
|
|
(obj.id_car_make, obj.get_local_name()) for obj in queryset
|
|
]
|
|
|
|
|
|
class ScheduleForm(forms.ModelForm):
|
|
"""
|
|
Represents a form for scheduling events, extending ModelForm to bind to the
|
|
Schedule model.
|
|
|
|
Provides a user interface to create or edit schedule entries with fields for
|
|
purpose, type, date/time of scheduling, duration, and additional notes. It
|
|
utilizes a custom widget for scheduling date and time in a localized format.
|
|
|
|
:ivar scheduled_at: Field to input date and time for scheduling, using a custom
|
|
'datetime-local' HTML widget.
|
|
:type scheduled_at: DateTimeField
|
|
"""
|
|
|
|
scheduled_at = forms.DateTimeField(
|
|
widget=DateTimeInput(attrs={"type": "datetime-local"})
|
|
)
|
|
reminder = forms.BooleanField(help_text=_("Send a reminder?"), required=False)
|
|
|
|
class Meta:
|
|
model = Schedule
|
|
fields = [
|
|
"purpose",
|
|
"scheduled_type",
|
|
"scheduled_at",
|
|
"start_time",
|
|
"end_time",
|
|
"notes",
|
|
]
|
|
|
|
widgets = {
|
|
"start_time": forms.TimeInput(attrs={"type": "time"}),
|
|
"end_time": forms.TimeInput(attrs={"type": "time"}),
|
|
}
|
|
|
|
|
|
class NoteForm(forms.ModelForm):
|
|
"""
|
|
Represents a form for creating or editing notes.
|
|
|
|
This class is a Django ModelForm for the Notes model. It provides validation
|
|
and rendering functionality for handling note data based on the specified
|
|
fields. It is used to simplify the creation and management of note-related
|
|
forms in Django applications.
|
|
|
|
:ivar Meta.model: The model associated with the ModelForm.
|
|
:type Meta.model: type
|
|
:ivar Meta.fields: The fields to include in the form.
|
|
:type Meta.fields: list
|
|
"""
|
|
|
|
class Meta:
|
|
model = Notes
|
|
fields = ["note"]
|
|
|
|
|
|
class ActivityForm(forms.ModelForm):
|
|
"""
|
|
Represents a form for the Activity model, allowing users to submit or update
|
|
data related to an activity instance.
|
|
|
|
This form is a subclass of Django's ModelForm, pre-configured to handle
|
|
instances of the `Activity` model. It maps to the "activity_type" and "notes"
|
|
fields of the model and provides validation and data-binding functionality.
|
|
|
|
:ivar Meta: Inner class that defines metadata for the form, including the model
|
|
associated with the form and the fields it comprises.
|
|
:type Meta: type
|
|
"""
|
|
|
|
activity_type = forms.ChoiceField(
|
|
choices=[("call", "Call"), ("email", "Email"), ("meeting", "Meeting")]
|
|
)
|
|
|
|
class Meta:
|
|
model = Activity
|
|
fields = ["activity_type", "notes"]
|
|
|
|
|
|
class OpportunityForm(forms.ModelForm):
|
|
"""
|
|
Represents a form for creating or editing Opportunity instances.
|
|
|
|
This class is a Django ModelForm designed to simplify the process of
|
|
validating and persisting data for Opportunity model instances. It
|
|
maps fields from the Opportunity model to form fields, making it
|
|
convenient to handle user input for operations such as creating and
|
|
updating opportunities.
|
|
|
|
:ivar Meta.model: The model associated with the form.
|
|
:type Meta.model: type
|
|
:ivar Meta.fields: List of fields from the model included in the form.
|
|
:type Meta.fields: list
|
|
"""
|
|
|
|
expected_close_date = forms.DateField(
|
|
label=_("Expected Closing Date"), widget=forms.DateInput(attrs={"type": "date"})
|
|
)
|
|
|
|
car = forms.ModelChoiceField(
|
|
queryset=Car.objects.all(), label=_("Car"), required=True
|
|
)
|
|
|
|
class Meta:
|
|
model = Opportunity
|
|
fields = [
|
|
"lead",
|
|
"car",
|
|
"stage",
|
|
"expected_close_date",
|
|
]
|
|
|
|
# def __init__(self, *args, **kwargs):
|
|
# super().__init__(*args, **kwargs)
|
|
# # Add a visible number input to display the current value
|
|
# self.fields["probability"].widget.attrs["class"] = (
|
|
# "d-none" # Hide the default input
|
|
# )
|
|
# if self.instance and self.instance.pk:
|
|
# self.fields["probability"].initial = self.instance.probability
|
|
|
|
|
|
class OpportunityStageForm(forms.ModelForm):
|
|
"""
|
|
Represents a form for creating or editing Opportunity instances.
|
|
|
|
This class is a Django ModelForm designed to simplify the process of
|
|
validating and persisting data for Opportunity model instances. It
|
|
maps fields from the Opportunity model to form fields, making it
|
|
convenient to handle user input for operations such as creating and
|
|
updating opportunities.
|
|
|
|
:ivar Meta.model: The model associated with the form.
|
|
:type Meta.model: type
|
|
:ivar Meta.fields: List of fields from the model included in the form.
|
|
:type Meta.fields: list
|
|
"""
|
|
|
|
class Meta:
|
|
model = Opportunity
|
|
fields = [
|
|
"stage",
|
|
]
|
|
|
|
|
|
class InvoiceModelCreateForm(InvoiceModelCreateFormBase):
|
|
"""
|
|
Represents a form for creating an Invoice model that inherits from a base
|
|
InvoiceModelCreateFormBase.
|
|
|
|
This form is intended for handling the creation of invoices with additional
|
|
field customizations. It modifies specific field widgets and defines custom
|
|
queryset logic for a customer field.
|
|
|
|
:ivar fields: A dictionary containing the form fields where keys represent
|
|
the field names and values are their respective Field objects.
|
|
:type fields: dict
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
# self.fields["cash_account"].widget = forms.HiddenInput()
|
|
# self.fields["prepaid_account"].widget = forms.HiddenInput()
|
|
# self.fields["unearned_account"].widget = forms.HiddenInput()
|
|
self.fields["date_draft"] = forms.DateField(
|
|
widget=DateInput(attrs={"type": "date"})
|
|
)
|
|
|
|
def get_customer_queryset(self):
|
|
if "customer" in self.fields:
|
|
self.fields[
|
|
"customer"
|
|
].queryset = self.USER_MODEL.dealer.entity.get_customers()
|
|
|
|
|
|
class BillModelCreateForm(BillModelCreateFormBase):
|
|
"""
|
|
This class represents a form for creating a BillModel, inheriting from
|
|
`BillModelCreateFormBase`.
|
|
|
|
It customizes the form fields by hiding certain account-related input fields,
|
|
such as `cash_account`, `prepaid_account`, and `unearned_account`. This form
|
|
is intended for specific use cases where these accounts should not be visible
|
|
or interactable by the user.
|
|
|
|
:ivar fields: A dictionary of all fields used in the form.
|
|
:type fields: dict
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
# self.fields["cash_account"].widget = forms.HiddenInput()
|
|
# self.fields["prepaid_account"].widget = forms.HiddenInput()
|
|
# self.fields["unearned_account"].widget = forms.HiddenInput()
|
|
|
|
|
|
class SaleOrderForm(forms.ModelForm):
|
|
"""
|
|
Represents a form for creating or updating sales orders.
|
|
|
|
This class is a Django ModelForm designed for handling the form
|
|
representation of the SaleOrder model. It defines specific fields
|
|
and their associated widgets to customize the input representation.
|
|
|
|
:ivar Meta.model: The model associated with this form. It is required
|
|
for linking the form to the SaleOrder model.
|
|
:type Meta.model: Type[models.Model]
|
|
:ivar Meta.fields: List of fields from the model to be used in the form.
|
|
:type Meta.fields: List[str]
|
|
:ivar Meta.widgets: Dictionary defining specific widgets to be applied
|
|
for fields, such as customizing their appearance.
|
|
:type Meta.widgets: Dict[str, Any]
|
|
"""
|
|
|
|
class Meta:
|
|
model = SaleOrder
|
|
fields = [
|
|
# "customer",
|
|
"expected_delivery_date",
|
|
# "estimate",
|
|
# "opportunity",
|
|
"comments",
|
|
# "order_date",
|
|
# "status",
|
|
]
|
|
widgets = {
|
|
"expected_delivery_date": forms.DateInput(
|
|
attrs={"type": "date", "label": _("Expected Delivery Date")}
|
|
),
|
|
# "order_date": forms.DateInput(
|
|
# attrs={"type": "date", "label": _("Order Date")}
|
|
# ),
|
|
# "customer": forms.Select(
|
|
# attrs={
|
|
# "class": "form-control",
|
|
# "label": _("Customer"),
|
|
# }
|
|
# ),
|
|
}
|
|
|
|
|
|
class EstimateModelCreateForm(forms.Form):
|
|
title = forms.CharField(max_length=255)
|
|
customer = forms.ModelChoiceField(queryset=Customer.objects.none())
|
|
|
|
|
|
# class EstimateModelCreateForm(EstimateModelCreateFormBase):
|
|
# """
|
|
# Defines the EstimateModelCreateForm class, which is used to create and manage
|
|
# forms for EstimateModel. This form handles the rendering and validation
|
|
# of specific fields, their input widgets, and labels.
|
|
|
|
# The purpose of this class is to provide a structured way to handle
|
|
# EstimateModel instances and their related fields, ensuring a user-friendly
|
|
# form interface with proper field configuration. It facilitates fetching
|
|
# related data, such as customer queries, based on user-specific parameters.
|
|
|
|
# :ivar ENTITY_SLUG: A string that represents the entity context in which the
|
|
# form operates.
|
|
# :type ENTITY_SLUG: str
|
|
# :ivar USER_MODEL: The user model that provides methods and objects needed
|
|
# to filter and query customers.
|
|
# :type USER_MODEL: Any
|
|
# :ivar fields: A dictionary representing fields included in the form such as
|
|
# "title", "customer", and "terms".
|
|
# :type fields: dict
|
|
# :ivar widgets: A dictionary defining custom input widgets for form fields,
|
|
# enabling specific attributes like classes and identifiers for styling or
|
|
# functionality purposes.
|
|
# :type widgets: dict
|
|
# :ivar labels: A dictionary specifying the human-readable labels for form fields.
|
|
# :type labels: dict
|
|
# """
|
|
# class Meta:
|
|
# model = ledger_models.EstimateModel
|
|
# fields = ["title","customer", "terms"]
|
|
# widgets = {
|
|
# "customer": forms.Select(
|
|
# attrs={
|
|
# "id": "djl-customer-estimate-customer-input",
|
|
# "class": "input",
|
|
# "label": _("Customer"),
|
|
# }
|
|
# ),
|
|
# 'terms': forms.Select(attrs={
|
|
# 'id': 'djl-customer-estimate-terms-input',
|
|
# 'class': 'input',
|
|
# 'label': _('Terms'),
|
|
# }),
|
|
# 'title': forms.TextInput(attrs={
|
|
# 'id': 'djl-customer-job-title-input',
|
|
# 'class': 'input' + ' is-large',
|
|
# 'label': _('Title'),
|
|
# })
|
|
# }
|
|
# labels = {
|
|
# 'title': _('Title'),
|
|
# 'terms': _('Terms'),
|
|
# "customer": _("Customer"),
|
|
# }
|
|
|
|
# def __init__(self, *args, entity_slug, user_model, **kwargs):
|
|
# super(EstimateModelCreateForm, self).__init__(
|
|
# *args, entity_slug=entity_slug, user_model=user_model, **kwargs
|
|
# )
|
|
# self.ENTITY_SLUG = entity_slug
|
|
# self.USER_MODEL = user_model
|
|
# self.fields["customer"].queryset = self.get_customer_queryset()
|
|
|
|
# def get_customer_queryset(self):
|
|
# return self.USER_MODEL.dealer.entity.get_customers()
|
|
|
|
|
|
class OpportunityStatusForm(forms.Form):
|
|
"""
|
|
Form used to handle and manage the opportunity status and stage selection.
|
|
|
|
This form allows users to select and update the status and stage of an
|
|
opportunity. It provides fields with pre-defined choices and custom widgets
|
|
for enhanced interactivity within the user interface.
|
|
|
|
:ivar status: The selected status of the opportunity, which is managed by a
|
|
choice field. It contains a set of predefined options for the status and
|
|
applies a custom widget with additional attributes for UI and action
|
|
handling.
|
|
:type status: ChoiceField
|
|
:ivar stage: The selected stage of the opportunity, which is managed by a
|
|
choice field. It includes predefined stage options and utilizes a custom
|
|
widget configured with attributes to handle UI updates dynamically.
|
|
:type stage: ChoiceField
|
|
"""
|
|
|
|
status = forms.ChoiceField(
|
|
label=_("Status"),
|
|
choices=Status.choices,
|
|
widget=forms.Select(
|
|
attrs={
|
|
"class": "form-control form-control-sm",
|
|
"hx-get": "{% url 'opportunity_update_status' opportunity.id %}",
|
|
"hx-target": ".other-information",
|
|
"hx-select": ".other-information",
|
|
"hx-swap": "outerHTML",
|
|
"hx-on::after-request": "this.setAttribute('disabled','true')",
|
|
"disabled": "disabled",
|
|
}
|
|
),
|
|
required=True,
|
|
)
|
|
stage = forms.ChoiceField(
|
|
label=_("Stage"),
|
|
choices=Stage.choices,
|
|
widget=forms.Select(
|
|
attrs={
|
|
"class": "form-control form-control-sm",
|
|
"hx-target": ".other-information",
|
|
"hx-select": ".other-information",
|
|
"hx-swap": "outerHTML",
|
|
"hx-on::after-request": "this.setAttribute('disabled','true')",
|
|
"disabled": "disabled",
|
|
}
|
|
),
|
|
required=True,
|
|
)
|
|
|
|
|
|
class GroupForm(forms.ModelForm):
|
|
"""
|
|
A form for creating and updating CustomGroup objects.
|
|
|
|
This form is used to handle the creation and modification of CustomGroup instances.
|
|
It is based on Django's ModelForm framework, which provides validation and transformation
|
|
of form data into a model instance. The form is designed to manage only the `name` field
|
|
of the `CustomGroup` model.
|
|
|
|
:ivar model: The model with which the form is associated, representing the `CustomGroup`.
|
|
:type model: Type[CustomGroup]
|
|
:ivar fields: List of fields to include in the form. In this case, only the `name` field.
|
|
:type fields: List[str]
|
|
"""
|
|
|
|
class Meta:
|
|
model = CustomGroup
|
|
fields = ["name"]
|
|
|
|
|
|
# class PermissionForm(forms.ModelForm):
|
|
# """
|
|
# Represents a form for managing permissions using a multiple-choice field.
|
|
|
|
# This class is a Django ModelForm that is used to handle permission
|
|
# assignments. It provides a multiple selection widget pre-populated with
|
|
# permissions based on specific app labels. The form offers a way to submit
|
|
# and validate permission data for further processing.
|
|
|
|
# :ivar name: A multiple-choice field that allows users to select permissions
|
|
# related to specific app labels (`inventory` and `django_ledger`).
|
|
# :type name: ModelMultipleChoiceField
|
|
# """
|
|
|
|
# name = forms.ModelMultipleChoiceField(
|
|
# queryset=cache.get(
|
|
# "permissions_queryset",
|
|
# Permission.objects.filter(
|
|
# content_type__app_label__in=["inventory", "django_ledger"]
|
|
# ),
|
|
# ),
|
|
# widget=forms.CheckboxSelectMultiple(),
|
|
# required=True,
|
|
# )
|
|
|
|
# def __init__(self, *args, **kwargs):
|
|
# super().__init__(*args, **kwargs)
|
|
# cache.set(
|
|
# "permissions_queryset",
|
|
# Permission.objects.filter(
|
|
# content_type__app_label__in=["inventory", "django_ledger"]
|
|
# ),
|
|
# 60 * 60,
|
|
# )
|
|
|
|
|
|
# class Meta:
|
|
# model = Permission
|
|
# fields = ["name"]
|
|
class PermissionForm(forms.ModelForm):
|
|
"""
|
|
Form for managing permissions with grouped checkboxes by app and model.
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
EXCLUDED_MODELS = [
|
|
"inventory.car",
|
|
"inventory.carfinance",
|
|
"inventory.carlocation",
|
|
"inventory.customcard",
|
|
"inventory.cartransfer",
|
|
"inventory.carcolors",
|
|
"inventory.carequipment",
|
|
"inventory.interiorcolors",
|
|
"inventory.exteriorcolors",
|
|
"inventory.lead",
|
|
"inventory.customgroup",
|
|
"inventory.saleorder",
|
|
"inventory.payment",
|
|
"inventory.staff",
|
|
"inventory.schedule",
|
|
"inventory.activity",
|
|
"inventory.opportunity",
|
|
"inventory.carreservationinventory.customer",
|
|
"inventory.organization",
|
|
# "inventory.salequotation",
|
|
# "inventory.salequotationcar"
|
|
"django_ledger.purchaseordermodeldjango_ledger.bankaccountmodel",
|
|
"django_ledger.estimatemodel",
|
|
"django_ledger.accountmodel",
|
|
"django_ledger.chartofaccountmodel",
|
|
"django_ledger.billmodeldjango_ledger.itemmodel",
|
|
"django_ledger.invoicemodel",
|
|
"django_ledger.vendormodel",
|
|
"django_ledger.journalentrymodeldjango_ledger.purchaseordermodel",
|
|
]
|
|
|
|
permissions = cache.get(
|
|
"permissions_queryset",
|
|
Permission.objects.filter(
|
|
content_type__app_label__in=[m.split(".")[0] for m in EXCLUDED_MODELS],
|
|
content_type__model__in=[m.split(".")[1] for m in EXCLUDED_MODELS],
|
|
).select_related("content_type"),
|
|
)
|
|
|
|
# Group permissions by app_label and model
|
|
self.grouped_permissions = {}
|
|
for perm in permissions:
|
|
app_label = perm.content_type.app_label
|
|
model = perm.content_type.model
|
|
if app_label not in self.grouped_permissions:
|
|
self.grouped_permissions[app_label] = {}
|
|
if model not in self.grouped_permissions[app_label]:
|
|
self.grouped_permissions[app_label][model] = []
|
|
self.grouped_permissions[app_label][model].append(perm)
|
|
|
|
# Create a multiple choice field (hidden, will use custom rendering)
|
|
self.fields["permissions"] = forms.ModelMultipleChoiceField(
|
|
queryset=permissions,
|
|
widget=forms.MultipleHiddenInput(),
|
|
required=False,
|
|
initial=self.instance.permissions.all() if self.instance.pk else [],
|
|
)
|
|
|
|
class Meta:
|
|
model = Permission
|
|
fields = []
|
|
|
|
|
|
class UserGroupForm(forms.ModelForm):
|
|
"""
|
|
Represents a form for selecting user groups.
|
|
|
|
This class is a Django ModelForm that is used to facilitate the selection of
|
|
multiple user groups. It utilizes a checkbox input for group selection, backed
|
|
by a queryset of CustomGroup instances. This form is specifically designed to
|
|
handle input related to CustomGroup model objects.
|
|
|
|
:ivar name: A field for selecting multiple groups using checkboxes.
|
|
:type name: ModelMultipleChoiceField
|
|
"""
|
|
|
|
name = forms.ModelMultipleChoiceField(
|
|
queryset=CustomGroup.objects.all(),
|
|
widget=forms.CheckboxSelectMultiple(),
|
|
required=True,
|
|
)
|
|
|
|
class Meta:
|
|
model = CustomGroup
|
|
fields = ["name"]
|
|
|
|
|
|
class DealerSettingsForm(forms.ModelForm):
|
|
"""
|
|
Represents a form for managing DealerSettings.
|
|
|
|
This class provides a form for creating or updating `DealerSettings`
|
|
instances. It automatically includes all fields defined in the
|
|
`DealerSettings` model and can be used within a Django web application.
|
|
|
|
:ivar model: Associated model for the form.
|
|
:type model: DealerSettings
|
|
:ivar fields: Fields to include in the form. The value "__all__" indicates
|
|
that all model fields should be included.
|
|
:type fields: str
|
|
"""
|
|
|
|
class Meta:
|
|
model = DealerSettings
|
|
fields = "__all__"
|
|
|
|
|
|
class LeadTransferForm(forms.Form):
|
|
"""
|
|
Represents a form for transferring leads between staff members.
|
|
|
|
This class defines a Django form used for handling the transfer of leads
|
|
to a specific staff member. It provides a mechanism to select the staff
|
|
member to whom the lead should be transferred by offering a dropdown
|
|
field populated by a queryset of staff objects.
|
|
|
|
:ivar transfer_to: Field for selecting the staff to transfer the lead to.
|
|
:type transfer_to: ModelChoiceField
|
|
"""
|
|
|
|
transfer_to = forms.ModelChoiceField(label="to", queryset=Staff.objects.all())
|
|
|
|
|
|
class DealersMakeForm(forms.Form):
|
|
"""
|
|
Form for selecting car makes related to a dealer.
|
|
|
|
This form allows dealers to select multiple car makes from a predefined
|
|
set of car makes that are marked as suitable for SA import. The selected
|
|
car makes can then be tied to the dealer.
|
|
|
|
:ivar dealer: The dealer instance associated with the form. This instance
|
|
is used to filter and save car makes for a specific dealer.
|
|
:type dealer: Dealer or None
|
|
"""
|
|
|
|
car_makes = forms.ModelMultipleChoiceField(
|
|
queryset=CarMake.objects.filter(is_sa_import=True),
|
|
widget=forms.CheckboxSelectMultiple(attrs={"class": "car-makes-grid"}),
|
|
required=True,
|
|
label=_("Select Car Makes"),
|
|
)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.dealer = kwargs.pop("dealer", None) # Pass dealer instance
|
|
super().__init__(*args, **kwargs)
|
|
|
|
def save(self):
|
|
if self.dealer:
|
|
DealersMake.objects.filter(dealer=self.dealer).delete()
|
|
|
|
for car_make in self.cleaned_data["car_makes"]:
|
|
DealersMake.objects.create(dealer=self.dealer, car_make=car_make)
|
|
|
|
|
|
class JournalEntryModelCreateForm(JournalEntryModelCreateFormBase):
|
|
"""
|
|
Represents a form model for creating journal entries.
|
|
|
|
This class serves as a base for creating a form model specifically designed
|
|
for creating new journal entries. It inherits properties and functionalities
|
|
from `JournalEntryModelCreateFormBase`.
|
|
|
|
:ivar foo: Description of foo attribute.
|
|
:type foo: str
|
|
:ivar bar: Description of bar attribute.
|
|
:type bar: int
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
class PlanPricingForm(forms.ModelForm):
|
|
"""
|
|
Represents a form for managing plan pricing.
|
|
|
|
This class provides a form for creating or updating `PlanPricing` instances.
|
|
It automatically includes all fields defined in the `PlanPricing` model and
|
|
can be used within a Django web application.
|
|
|
|
:ivar model: Associated model for the form.
|
|
:type model: PlanPricing
|
|
:ivar fields: Fields to include in the form. The value "__all__" indicates
|
|
that all model fields should be included.
|
|
:type fields: str
|
|
"""
|
|
|
|
class Meta:
|
|
model = PlanPricing
|
|
fields = ["plan", "pricing", "price"]
|
|
|
|
|
|
class CreditCardField(forms.CharField):
|
|
def clean(self, value):
|
|
value = super().clean(value)
|
|
if value:
|
|
# Remove all non-digit characters
|
|
cleaned_value = "".join(c for c in value if c.isdigit())
|
|
|
|
# Validate using Luhn algorithm
|
|
if not Luhn.check_luhn(cleaned_value):
|
|
raise forms.ValidationError(
|
|
_("Please enter a valid credit card number")
|
|
)
|
|
|
|
# Add basic card type detection (optional)
|
|
if cleaned_value.startswith("4"):
|
|
self.card_type = "visa"
|
|
elif cleaned_value.startswith(("51", "52", "53", "54", "55")):
|
|
self.card_type = "mastercard"
|
|
elif cleaned_value.startswith(("34", "37")):
|
|
self.card_type = "amex"
|
|
else:
|
|
self.card_type = "unknown"
|
|
|
|
return value
|
|
return value
|
|
|
|
|
|
class ExpiryDateField(forms.CharField):
|
|
def clean(self, value):
|
|
value = super().clean(value)
|
|
if value:
|
|
try:
|
|
month, year = value.split("/")
|
|
month = int(month.strip())
|
|
year = int(year.strip())
|
|
|
|
# Handle 2-digit year
|
|
if year < 100:
|
|
year += 2000
|
|
|
|
# Validate month
|
|
if month < 1 or month > 12:
|
|
raise forms.ValidationError(_("Please enter a valid month (01-12)"))
|
|
|
|
# Validate not expired
|
|
current_year = datetime.now().year
|
|
current_month = datetime.now().month
|
|
|
|
if year < current_year or (
|
|
year == current_year and month < current_month
|
|
):
|
|
raise forms.ValidationError(_("This card appears to be expired"))
|
|
|
|
except (ValueError, AttributeError):
|
|
raise forms.ValidationError(
|
|
_("Please enter a valid expiry date in MM/YY format")
|
|
)
|
|
|
|
return value
|
|
|
|
|
|
class CVVField(forms.CharField):
|
|
def clean(self, value):
|
|
value = super().clean(value)
|
|
if value:
|
|
if not value.isdigit():
|
|
raise forms.ValidationError(_("CVV must contain only digits"))
|
|
if len(value) not in (3, 4):
|
|
raise forms.ValidationError(_("CVV must be 3 or 4 digits"))
|
|
return value
|
|
|
|
|
|
class PaymentPlanForm(forms.Form):
|
|
# Customer Information
|
|
first_name = forms.CharField(
|
|
max_length=100,
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": "form-control",
|
|
"placeholder": _("First Name"),
|
|
"id": "first-name",
|
|
}
|
|
),
|
|
label=_("First Name"),
|
|
)
|
|
|
|
last_name = forms.CharField(
|
|
max_length=100,
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": "form-control",
|
|
"placeholder": _("Last Name"),
|
|
"id": "last-name",
|
|
}
|
|
),
|
|
label=_("Last Name"),
|
|
)
|
|
|
|
email = forms.EmailField(
|
|
widget=forms.EmailInput(
|
|
attrs={"class": "form-control", "placeholder": _("Email"), "id": "email"}
|
|
),
|
|
label=_("Email Address"),
|
|
)
|
|
|
|
phone_number = SaudiPhoneNumberField(label=_("Phone Number"))
|
|
|
|
# Credit Card Fields (not saved to database)
|
|
card_number = CreditCardField(
|
|
required=True,
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": "form-control",
|
|
"placeholder": "1234 5678 9012 3456",
|
|
"id": "card-number",
|
|
}
|
|
),
|
|
label=_("Card Number"),
|
|
)
|
|
|
|
expiry_date = ExpiryDateField(
|
|
required=True,
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": "form-control",
|
|
"placeholder": "MM/YY",
|
|
"id": "expiry",
|
|
}
|
|
),
|
|
label=_("Expiration Date"),
|
|
)
|
|
|
|
cvv = CVVField(
|
|
required=True,
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": "form-control",
|
|
"placeholder": "123",
|
|
"id": "cvv",
|
|
}
|
|
),
|
|
label=_("Security Code (CVV)"),
|
|
)
|
|
|
|
card_name = forms.CharField(
|
|
required=True,
|
|
max_length=100,
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": "form-control",
|
|
"placeholder": "John Doe",
|
|
"id": "card-name",
|
|
}
|
|
),
|
|
label=_("Name on Card"),
|
|
)
|
|
|
|
# Terms and conditions
|
|
terms = forms.BooleanField(
|
|
required=True,
|
|
widget=forms.CheckboxInput(attrs={"class": "form-check-input", "id": "terms"}),
|
|
label=_("I agree to the Terms and Conditions"),
|
|
error_messages={"required": _("You must accept the terms and conditions")},
|
|
)
|
|
|
|
def clean(self):
|
|
cleaned_data = super().clean()
|
|
payment_method = self.data.get("payment-method")
|
|
|
|
if payment_method == "credit-card":
|
|
if not all(
|
|
[
|
|
cleaned_data.get("card_number"),
|
|
cleaned_data.get("expiry_date"),
|
|
cleaned_data.get("cvv"),
|
|
cleaned_data.get("card_name"),
|
|
]
|
|
):
|
|
raise forms.ValidationError("Please complete all credit card fields")
|
|
|
|
return cleaned_data
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
user = kwargs.pop("user", None)
|
|
super().__init__(*args, **kwargs)
|
|
|
|
if user and user.is_authenticated:
|
|
# Pre-fill form with user data if available
|
|
self.fields["first_name"].initial = user.first_name
|
|
self.fields["last_name"].initial = user.last_name
|
|
self.fields["email"].initial = user.email
|
|
|
|
|
|
# class ActivityHistoryForm(forms.Form):
|
|
# activity_type = forms.ChoiceField(
|
|
# choices=[
|
|
# ('note', 'Note'),
|
|
# ('call', 'Call'),
|
|
# ('email', 'Email'),
|
|
# ('meeting', 'Meeting'),],
|
|
# widget=forms.Select(attrs={
|
|
# 'class': 'form-control',
|
|
# 'id': 'activity-type'
|
|
# }),
|
|
# label=_('Activity Type')
|
|
# )
|
|
# description = forms.CharField(
|
|
# widget=forms.Textarea(attrs={
|
|
# 'class': 'form-control',
|
|
# 'id': 'activity-description'
|
|
# }),
|
|
# label=_('Description')
|
|
# )
|
|
|
|
|
|
class StaffTaskForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Tasks
|
|
fields = ["title", "due_date", "description"]
|
|
widgets = {
|
|
"due_date": forms.DateTimeInput(attrs={"type": "date"}),
|
|
}
|
|
|
|
|
|
#############################################################
|
|
|
|
|
|
class ItemInventoryForm(forms.Form):
|
|
make = forms.ModelChoiceField(
|
|
queryset=CarMake.objects.filter(is_sa_import=True),
|
|
widget=forms.Select(attrs={"class": "form-control", "id": "make"}),
|
|
label=_("Make"),
|
|
)
|
|
model = forms.ModelChoiceField(
|
|
queryset=CarModel.objects.none(),
|
|
widget=forms.Select(attrs={"class": "form-control", "id": "model"}),
|
|
label=_("Model"),
|
|
)
|
|
serie = forms.ModelChoiceField(
|
|
queryset=CarSerie.objects.none(),
|
|
widget=forms.Select(attrs={"class": "form-control", "id": "serie"}),
|
|
label=_("Serie"),
|
|
)
|
|
trim = forms.ModelChoiceField(
|
|
queryset=CarTrim.objects.none(),
|
|
widget=forms.Select(attrs={"class": "form-control", "id": "trim"}),
|
|
label=_("Trim"),
|
|
)
|
|
|
|
|
|
#####################################################################
|
|
|
|
|
|
class CSVUploadForm(forms.Form):
|
|
dealer = forms.ModelChoiceField(
|
|
queryset=Dealer.objects.all(), label=_("Dealer"), widget=forms.HiddenInput()
|
|
)
|
|
vendor = forms.ModelChoiceField(
|
|
queryset=Vendor.objects.all(),
|
|
label=_("Vendor"),
|
|
widget=forms.Select(attrs={"class": "form-select"}),
|
|
required=True,
|
|
)
|
|
year = forms.IntegerField(
|
|
label=_("Year"),
|
|
widget=forms.NumberInput(
|
|
attrs={
|
|
"class": "form-control",
|
|
"hx-get": "",
|
|
"hx-target": "#serie",
|
|
"hx-select": "#serie",
|
|
"hx-include": "#model",
|
|
"hx-trigger": "input delay:500ms",
|
|
"hx-swap": "outerHTML",
|
|
}
|
|
),
|
|
required=True,
|
|
)
|
|
exterior = forms.ModelChoiceField(
|
|
queryset=ExteriorColors.objects.all(),
|
|
label=_("Exterior Color"),
|
|
widget=forms.RadioSelect(attrs={"class": "form-select"}),
|
|
required=True,
|
|
)
|
|
interior = forms.ModelChoiceField(
|
|
queryset=InteriorColors.objects.all(),
|
|
label=_("Interior Color"),
|
|
widget=forms.RadioSelect(attrs={"class": "form-select"}),
|
|
required=True,
|
|
)
|
|
receiving_date = forms.DateField(
|
|
label=_("Receiving Date"),
|
|
widget=forms.DateInput(attrs={"type": "date", "class": "form-control"}),
|
|
required=True,
|
|
)
|
|
|
|
def clean_csv_file(self):
|
|
csv_file = self.cleaned_data["csv_file"]
|
|
if not csv_file.name.endswith(".csv"):
|
|
raise forms.ValidationError(_("File is not a CSV file"))
|
|
|
|
# Read and validate CSV structure
|
|
try:
|
|
csv_data = TextIOWrapper(csv_file.file, encoding="utf-8")
|
|
reader = csv.DictReader(csv_data)
|
|
|
|
required_fields = ["vin", "make", "model", "year"]
|
|
if not all(field in reader.fieldnames for field in required_fields):
|
|
missing = set(required_fields) - set(reader.fieldnames)
|
|
raise forms.ValidationError(
|
|
_("CSV is missing required columns: %(missing)s"),
|
|
params={"missing": ", ".join(missing)},
|
|
)
|
|
except Exception as e:
|
|
raise forms.ValidationError(
|
|
_("Error reading CSV file: %(error)s") % {"error": str(e)}
|
|
)
|
|
|
|
# Reset file pointer for later processing
|
|
csv_file.file.seek(0)
|
|
return csv_file
|
|
|
|
|
|
class AdditionalFinancesForm(forms.Form):
|
|
additional_finances = forms.ModelMultipleChoiceField(
|
|
queryset=AdditionalServices.objects.all(),
|
|
widget=forms.CheckboxSelectMultiple(attrs={"class": "form-check-input"}),
|
|
required=False,
|
|
)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
for field in self.fields.values():
|
|
if isinstance(field, forms.ModelMultipleChoiceField):
|
|
field.widget.choices = [
|
|
(obj.pk, f"{obj.name} - {obj.price:.2f}") for obj in field.queryset
|
|
]
|
|
|
|
|
|
class VatRateForm(forms.ModelForm):
|
|
rate = forms.DecimalField(
|
|
label="VAT Rate",
|
|
max_digits=5,
|
|
decimal_places=2,
|
|
validators=[vat_rate_validator],
|
|
help_text=_("VAT rate as decimal between 0 and 1 (e.g., 0.2 for 20%)"),
|
|
)
|
|
|
|
class Meta:
|
|
model = VatRate
|
|
fields = ["rate"]
|
|
|
|
|
|
class CustomSetPasswordForm(SetPasswordForm):
|
|
new_password1 = forms.CharField(
|
|
label="New Password",
|
|
widget=forms.PasswordInput(
|
|
attrs={"class": "form-control", "placeholder": "New Password"}
|
|
),
|
|
)
|
|
new_password2 = forms.CharField(
|
|
label="Confirm New Password",
|
|
widget=forms.PasswordInput(
|
|
attrs={"class": "form-control", "placeholder": "Confirm New Password"}
|
|
),
|
|
)
|
|
|
|
|
|
# forms.py
|
|
class RecallFilterForm(forms.Form):
|
|
make = forms.ModelChoiceField(
|
|
queryset=CarMake.objects.all(),
|
|
required=False,
|
|
label=_("Make"),
|
|
widget=forms.Select(attrs={"class": "form-control"}),
|
|
)
|
|
model = forms.ModelChoiceField(
|
|
queryset=CarModel.objects.none(),
|
|
required=False,
|
|
label=_("Model"),
|
|
widget=forms.Select(attrs={"class": "form-control"}),
|
|
)
|
|
serie = forms.ModelChoiceField(
|
|
queryset=CarSerie.objects.none(),
|
|
required=False,
|
|
label=_("Series"),
|
|
widget=forms.Select(attrs={"class": "form-control"}),
|
|
)
|
|
trim = forms.ModelChoiceField(
|
|
queryset=CarTrim.objects.none(),
|
|
required=False,
|
|
label=_("Trim"),
|
|
widget=forms.Select(attrs={"class": "form-control"}),
|
|
)
|
|
year_from = forms.IntegerField(
|
|
required=False,
|
|
label=_("From Year"),
|
|
widget=forms.NumberInput(attrs={"class": "form-control"}),
|
|
)
|
|
year_to = forms.IntegerField(
|
|
required=False,
|
|
label=_("To Year"),
|
|
widget=forms.NumberInput(attrs={"class": "form-control"}),
|
|
)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
make_id = kwargs.pop("make_id", None)
|
|
model_id = kwargs.pop("model_id", None)
|
|
serie_id = kwargs.pop("serie_id", None)
|
|
super().__init__(*args, **kwargs)
|
|
|
|
if make_id:
|
|
self.fields["model"].queryset = CarModel.objects.filter(
|
|
id_car_make_id=make_id
|
|
)
|
|
if model_id:
|
|
self.fields["serie"].queryset = CarSerie.objects.filter(
|
|
id_car_model_id=model_id
|
|
)
|
|
if serie_id:
|
|
self.fields["trim"].queryset = CarTrim.objects.filter(
|
|
id_car_serie_id=serie_id
|
|
)
|
|
|
|
|
|
class RecallCreateForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Recall
|
|
fields = [
|
|
"title",
|
|
"description",
|
|
"make",
|
|
"model",
|
|
"serie",
|
|
"trim",
|
|
"year_from",
|
|
"year_to",
|
|
]
|
|
widgets = {
|
|
"make": forms.Select(attrs={"class": "form-control"}),
|
|
"model": forms.Select(attrs={"class": "form-control"}),
|
|
"serie": forms.Select(attrs={"class": "form-control"}),
|
|
"trim": forms.Select(attrs={"class": "form-control"}),
|
|
"title": forms.TextInput(attrs={"class": "form-control"}),
|
|
"description": forms.Textarea(attrs={"class": "form-control"}),
|
|
"year_from": forms.NumberInput(attrs={"class": "form-control"}),
|
|
"year_to": forms.NumberInput(attrs={"class": "form-control"}),
|
|
}
|
|
|
|
|
|
class TicketForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Ticket
|
|
fields = ["subject", "description", "priority"]
|
|
widgets = {
|
|
"description": forms.Textarea(attrs={"class": "form-control", "rows": 10}),
|
|
}
|
|
|
|
|
|
class TicketResolutionForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Ticket
|
|
fields = ["status", "resolution_notes"]
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
# Limit status choices to resolution options
|
|
self.fields["status"].choices = [("resolved", "Resolved"), ("closed", "Closed")]
|
|
|
|
|
|
class CarDealershipRegistrationForm(forms.ModelForm):
|
|
# Add additional fields for the registration form
|
|
|
|
class Meta:
|
|
model = UserRegistration
|
|
fields = (
|
|
"name",
|
|
"arabic_name",
|
|
"email",
|
|
"phone_number",
|
|
"crn",
|
|
"vrn",
|
|
"address",
|
|
)
|
|
|
|
|
|
class CarDetailsEstimateCreate(forms.Form):
|
|
customer = forms.ModelChoiceField(
|
|
queryset=Customer.objects.filter(active=True),
|
|
required=True,
|
|
label="Customer",
|
|
widget=forms.Select(attrs={"class": "form-control"}),
|
|
)
|