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 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 ) 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", ] 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", ] 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"}) ) probability = forms.IntegerField( label=_("Probability (%)"), widget=forms.NumberInput( attrs={ "type": "range", "min": "0", "max": "100", "step": "1", "class": "form-range", "oninput": "this.nextElementSibling.value = this.value", } ), initial=50, # Default value ) class Meta: model = Opportunity fields = [ "lead", "car", "stage", "probability", "amount", "expected_revenue", "expected_close_date", ] widgets = { "expected_revenue": forms.NumberInput(attrs={"readonly": "readonly"}), } 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.journalentrymodel" "django_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, ) class VatRateForm(forms.ModelForm): 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): resolution_notes = forms.CharField( widget=forms.Textarea(attrs={'rows': 3}), required=False, help_text="Optional notes about how the issue was resolved." ) class Meta: model = Ticket fields = ['status'] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Limit status choices to resolution options self.fields['status'].choices = [ ('resolved', 'Resolved'), ('closed', 'Closed') ]