haikal/inventory/forms.py
2025-04-28 14:40:08 +03:00

1430 lines
51 KiB
Python

from django.core.cache import cache
from django.contrib.auth.models import Permission
from appointment.models import Service
from phonenumber_field.formfields import PhoneNumberField
from django.core.validators import MinLengthValidator
from django import forms
from django.contrib.auth import get_user_model
from .models import CustomGroup, Status, Stage
from .mixins import AddClassMixin
from django_ledger.forms.invoice import (
InvoiceModelCreateForm as InvoiceModelCreateFormBase,
)
from django_ledger.forms.estimate import (
EstimateModelCreateForm as EstimateModelCreateFormBase,
)
from django_ledger.forms.bill import BillModelCreateForm as BillModelCreateFormBase
from django_ledger.forms.journal_entry import JournalEntryModelCreateForm as JournalEntryModelCreateFormBase
from .models import (
Dealer,
DealersMake,
Vendor,
Schedule,
Car,
CarTransfer,
CarFinance,
CustomCard,
CarRegistration,
CarColors,
ExteriorColors,
InteriorColors,
CarLocation,
Representative,
AdditionalServices,
Staff,
Opportunity,
Lead,
Activity,
Notes,
CarModel,
SaleOrder,
CarMake,
DealerSettings
)
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 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,)
class Meta:
model = Staff
fields = ["name", "arabic_name", "phone_number", "staff_type"]
# 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
"""
class Meta:
model = Dealer
fields = [
"name",
"arabic_name",
"crn",
"vrn",
"phone_number",
"address",
"logo",
]
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,
)
national_id = forms.CharField(max_length=10,required=False)
crn = forms.CharField(required=False)
vrn = forms.CharField(required=False)
address = forms.CharField()
class OrganizationForm(CustomerForm):
"""
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
"""
contact_person = forms.CharField(required=False)
logo = forms.ImageField(required=False)
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",
]
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 = ledger_models.VendorModel.objects.filter(
active=True
)
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.
"""
additional_finances = forms.ModelMultipleChoiceField(
queryset=AdditionalServices.objects.all(),
widget=forms.CheckboxSelectMultiple(attrs={"class": "form-check-input"}),
required=False,
)
class Meta:
model = CarFinance
exclude = [
"car",
"profit_margin",
"vat_amount",
"total",
"additional_services",
]
def save(self, commit=True):
instance = super().save()
instance.additional_services.set(self.cleaned_data["additional_finances"])
instance.save()
return instance
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]
"""
class Meta:
model = Vendor
fields = [
"name",
"arabic_name",
"crn",
"vrn",
"email",
"phone_number",
"contact_person",
"address",
"logo",
]
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
"""
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,
)
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
"""
id_car_make = forms.ModelChoiceField(
label="Make",
queryset=CarMake.objects.filter(is_sa_import=True),
widget=forms.Select(
attrs={
"class": "form-control form-control-sm",
"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')",
}
),
required=True,
)
id_car_model = forms.ModelChoiceField(
label="Model",
queryset=CarModel.objects.none(),
widget=forms.Select(attrs={"class": "form-control form-control-sm"}),
required=True,
)
class Meta:
model = Lead
fields = [
"first_name",
"last_name",
"email",
"phone_number",
"lead_type",
"address",
"id_car_make",
"id_car_model",
"crn",
"vrn",
"year",
"source",
"channel",
"staff",
"priority",
]
def __init__(self, *args, **kwargs):
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
]
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"})
)
class Meta:
model = Schedule
fields = ["purpose", "scheduled_type", "scheduled_at", "duration", "notes",]
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
"""
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
"""
class Meta:
model = Opportunity
fields = ["customer", "car", "stage", "probability", "staff", "closing_date"]
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 = ["estimate", "payment_method", "comments"]
widgets = {
"comments": forms.Textarea(attrs={"rows": 3}),
}
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 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