from django.core.cache import cache from django.contrib.auth.models import Permission from django.contrib.auth.models import Group from appointment.models import Appointment, Service, StaffMember from django.urls import reverse from django_countries.widgets import CountrySelectWidget from django_ledger.models import CustomerModel from phonenumber_field.formfields import PhoneNumberField from django.core.validators import MinLengthValidator from django.core.validators import RegexValidator from django import forms from django.contrib.auth import get_user_model from phonenumber_field.phonenumber import PhoneNumber from .models import CustomGroup, Status, Stage from .mixins import AddClassMixin from django.forms.models import inlineformset_factory 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.vendor import VendorModelForm from .models import ( Dealer, # Branch, Vendor, Schedule, Customer, Car, CarTransfer, CarFinance, CustomCard, CarRegistration, CarColors, ExteriorColors, InteriorColors, # SaleQuotation, CarLocation, Organization, Representative, Payment, # SaleQuotationCar, AdditionalServices, Staff, Opportunity, Priority, Sources, Lead, Activity, Notes, CarModel, SaleOrder, CarMake, ) from django_ledger import models as ledger_models from django.forms import ( ModelMultipleChoiceField, ValidationError, DateInput, DateTimeInput, ) from django.utils.translation import gettext_lazy as _ import django_tables2 as tables from django.forms import formset_factory User = get_user_model() class AdditionalServiceForm(forms.ModelForm): class Meta: model = AdditionalServices fields = ["name", "price", "description", "taxable", "uom"] # class PaymentForm(forms.ModelForm): # invoice = forms.ModelChoiceField(queryset=InvoiceModel.objects.all(),label="Invoice", required=True) # class Meta: # model = Payment # fields = ['amount','payment_method', 'reference_number'] class StaffForm(forms.ModelForm): 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"] # def __init__(self, *args, **kwargs): # user_instance = kwargs.get("instance") # if user_instance and user_instance.user: # initial = kwargs.setdefault("initial", {}) # initial["email"] = user_instance.user.email # super().__init__(*args, **kwargs) # # def save(self, commit=True): # user_instance = super().save(commit=False) # user = user_instance.user # user.email = self.cleaned_data["email"] # if commit: # user.save() # user_instance.save() # return user_instance # Dealer Form class DealerForm(forms.ModelForm): class Meta: model = Dealer fields = [ "name", "arabic_name", "crn", "vrn", "phone_number", "address", "logo", ] class CustomerForm(forms.Form): first_name = forms.CharField() middle_name = forms.CharField() last_name = forms.CharField() national_id = forms.CharField(max_length=10) email = forms.EmailField() phone_number = PhoneNumberField(region="SA") address = forms.CharField() class OrganizationForm(forms.Form): name = forms.CharField() arabic_name = forms.CharField() email = forms.EmailField() phone_number = PhoneNumberField(region="SA") crn = forms.CharField() vrn = forms.CharField() address = forms.CharField() contact_person = forms.CharField(required=False) logo = forms.ImageField(required=False) # class CustomerForm(forms.ModelForm, AddClassMixin): # class Meta: # model = Customer # fields = [ # "title", # "first_name", # "middle_name", # "last_name", # "gender", # "dob", # "email", # "national_id", # "phone_number", # "address", # ] # widgets = { # "phone_number": forms.TextInput(attrs={"class": "phone"}), # "dob": forms.DateInput(attrs={"type": "date"}), # } class CarForm( forms.ModelForm, AddClassMixin, ): 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 = { "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 ) # queryset = self.fields["vendor"].queryset # self.fields["vendor"].choices = [ # (obj.pk, obj.get_local_name()) for obj in queryset # ] class CarUpdateForm(forms.ModelForm, AddClassMixin): 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) # if dealer and 'branch' in self.fields: # self.fields['branch'].queryset = Branch.objects.filter(dealer=dealer) # self.fields['branch'].choices = [ # (branch.id, branch.get_local_name()) for branch in self.fields['branch'].queryset # ] # if "vendor" in self.fields: # queryset = self.fields["vendor"].queryset # if queryset: # self.fields["vendor"].choices = [ # (vendor.id, vendor.get_local_name()) for vendor in queryset # ] class CarFinanceForm(forms.ModelForm): 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 __init__(self, *args, **kwargs): # super().__init__(*args, **kwargs) # if self.instance.pk: # self.fields[ # "additional_finances" # ].initial = self.instance.additional_services.all() 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): class Meta: model = CarLocation fields = ["showroom", "description"] widgets = { "description": forms.Textarea(attrs={"rows": 2, "class": "form-control"}), } class CarTransferForm(forms.ModelForm): 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): 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): 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): class Meta: model = Vendor fields = [ "name", "arabic_name", "crn", "vrn", "email", "phone_number", "contact_person", "address", "logo", ] class CarColorsForm(forms.ModelForm): 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 QuotationForm(forms.ModelForm): # cars = ModelMultipleChoiceField( # queryset=Car.objects.none(), # Default empty queryset # widget=forms.CheckboxSelectMultiple, # label="Select Cars", # ) # # class Meta: # model = SaleQuotation # fields = ["customer", "cars", "remarks"] # # def __init__(self, *args, **kwargs): # super().__init__(*args, **kwargs) # # self.fields["cars"].queryset = Car.objects.filter( # finances__isnull=False # ).distinct() # class OrganizationForm(forms.ModelForm): # class Meta: # model = Organization # fields = [ # "name", # "arabic_name", # "crn", # "vrn", # "phone_number", # "address", # "logo", # ] # def __init__(self, *args, **kwargs): # dealer = kwargs.pop("dealer", None) # super().__init__(*args, **kwargs) class RepresentativeForm(forms.ModelForm): 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): 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): 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): 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): # 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."), }, ) # def clean(self): # cleaned_data = super().clean() # password = cleaned_data.get("password") # confirm_password = cleaned_data.get("confirm_password") # if password != confirm_password: # raise forms.ValidationError("Passwords do not match.") # else: # return cleaned_data class ItemForm(forms.Form): item = forms.ModelChoiceField( queryset=ledger_models.ItemModel.objects.all(), label="Item", required=True, validators=[MinLengthValidator(5)], ) quantity = forms.DecimalField(label="Quantity", required=True) # unit = forms.DecimalField(label="Unit", required=True) # unit_cost = forms.DecimalField(label="Unit Cost", required=True) # unit_sales_price = forms.DecimalField(label="Unit Sales Price", required=True) class PaymentForm(forms.Form): 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): 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): 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", "address", "id_car_make", "id_car_model", "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): 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): class Meta: model = Notes fields = ["note"] class ActivityForm(forms.ModelForm): class Meta: model = Activity fields = ["activity_type", "notes"] class OpportunityForm(forms.ModelForm): class Meta: model = Opportunity fields = ["customer", "car", "stage", "probability", "staff", "closing_date"] class InvoiceModelCreateForm(InvoiceModelCreateFormBase): 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): 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"}) ) class SaleOrderForm(forms.ModelForm): class Meta: model = SaleOrder fields = ["estimate", "payment_method", "comments"] widgets = { "comments": forms.Textarea(attrs={"rows": 3}), } class EstimateModelCreateForm(EstimateModelCreateFormBase): 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): 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): class Meta: model = CustomGroup fields = ["name"] class PermissionForm(forms.ModelForm): 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): name = forms.ModelMultipleChoiceField( queryset= CustomGroup.objects.all(), widget=forms.CheckboxSelectMultiple(), required=True ) class Meta: model = CustomGroup fields = ["name"]