Source code for ferrosoft.apps.ferrobase.forms.model_forms
# Copyright (c) 2025 Ferrosoft GmbH. All rights reserved.
from django import forms
from django.contrib.auth.models import Group, Permission
from django.utils.translation import gettext_lazy as _
from ferrosoft.apps.ferrobase.models import (
User,
Language,
IdSequenceConfig,
Organization,
Tenant,
SubscriptionPlan,
CompanySite,
FeatureAssignment,
APIToken,
)
from ferrosoft.apps.ferrobase.services.tenant_mgm import (
add_tenant,
TenantOptions,
STANDARD_FIXTURES,
)
[docs]
class APITokenForm(forms.ModelForm):
"""Form for displaying an API token's name field.
Token creation is intentionally blocked at the form level; tokens must be
generated through ``APIToken.create`` so that the JWT is returned to the
caller.
"""
[docs]
class CompanySiteForm(forms.ModelForm):
"""Form for creating and updating ``CompanySite`` records.
Renders ``additional_lines`` as a compact two-row textarea.
"""
[docs]
class Meta:
model = CompanySite
fields = [
"code",
"name",
"salutation",
"first_name",
"last_name",
"company",
"additional_lines",
"street",
"house_no",
"city",
"postal_code",
"country",
]
widgets = {
"additional_lines": forms.Textarea(attrs={"rows": 2}),
}
[docs]
class FeatureAssignmentForm(forms.ModelForm):
"""Form for assigning an ``AppFeature`` to a ``SubscriptionPlan`` with an optional limit.
The plan field is hidden because it is set programmatically from the
parent subscription context.
"""
[docs]
class Meta:
model = FeatureAssignment
fields = [
"plan",
"feature",
"limit",
]
widgets = {
"plan": forms.HiddenInput(),
}
[docs]
class IDSequenceForm(forms.ModelForm):
"""Form for configuring a named ``IdSequenceConfig`` entry (prefix and padding)."""
[docs]
class OrganizationForm(forms.ModelForm):
"""Form for creating and updating ``Organization`` master records."""
[docs]
class Meta:
model = Organization
fields = [
"name",
"contact_email",
"contact_first_name",
"contact_last_name",
"subscription",
]
[docs]
class SubscriptionPlanForm(forms.ModelForm):
"""Form for editing ``SubscriptionPlan`` tier settings (name, tonnage cap, feature flag)."""
[docs]
class Meta:
model = SubscriptionPlan
fields = [
"name",
"max_tonnage",
"enable_all_features",
]
[docs]
class TenantForm(forms.ModelForm):
"""Form for creating and updating ``Tenant`` records.
When ``enable_add_tenant=True`` is passed at instantiation, ``save``
delegates to ``add_tenant`` (which provisions the tenant database and loads
fixtures) instead of the standard ``ModelForm.save``. This flag is only
meaningful for new tenants; updates use the standard path regardless.
"""
[docs]
class Meta:
model = Tenant
fields = [
"organization",
"name",
"active",
"expiry_date",
]
widgets = {
"organization": forms.HiddenInput(),
"expiry_date": forms.DateTimeInput(
attrs={"type": "datetime-local"},
format="%Y-%m-%dT%H:%M",
),
}
language = forms.ModelChoiceField(
help_text=_("Language of built-in data catalogs"),
queryset=Language.objects.get_supported(),
required=True,
)
def __init__(self, *args, **kwargs):
self.enable_add_tenant = kwargs.pop("enable_add_tenant", False)
super().__init__(*args, **kwargs)
[docs]
def save(self, commit=True):
if not self.enable_add_tenant:
# Use normal logic, since add_tenant is for creation only.
return super().save(commit)
if not commit:
# add_tenant is very commit-y, cannot use it noncommittally.
return super().save(commit)
data = self.cleaned_data
language = data.pop("language")
return add_tenant(
TenantOptions(
id=None,
fixtures=STANDARD_FIXTURES,
language=language.iso_code,
**data,
)
)
[docs]
class UserPasswordForm(forms.ModelForm):
"""Base form mixin that adds an optional password field to any ``User`` form.
If the password field is left empty, the existing password is not changed.
"""
password = forms.CharField(
label=_("Password"),
help_text=_("Leave empty to not create or update password"),
widget=forms.PasswordInput(),
required=False,
)
[docs]
def save(self, commit=True):
user = super().save(commit)
password = self.cleaned_data.get("password", "")
if password != "":
user.set_password(password)
if commit:
user.save(update_fields=["password"])
return user
[docs]
class UserCreationForm(UserPasswordForm):
"""Form for creating new ``User`` accounts.
The organization field is hidden and fixed to the caller's organization
unless ``allow_organization_selection=True`` is passed (for superusers).
"""
def __init__(self, *args, **kwargs):
self._organization = kwargs.pop("organization")
self._allow_organization_selection = kwargs.pop("allow_organization_selection")
super().__init__(*args, **kwargs)
if not self._allow_organization_selection:
self.fields["organization"].required = False
self.fields["organization"].widget = forms.HiddenInput()
[docs]
def save(self, commit=True):
user = super().save(commit=False)
if not self._allow_organization_selection:
user.organization = self._organization
if commit:
user.save()
return user
[docs]
class UserUpdateForm(UserPasswordForm):
"""Form for editing an existing ``User`` account's profile and preferences."""
[docs]
class Meta:
model = User
fields = [
"username",
"is_active",
"first_name",
"last_name",
"email",
"language",
"timezone",
"color_theme",
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["language"].queryset = Language.objects.get_supported()
[docs]
class UserGroupsForm(forms.ModelForm):
"""Form for managing a user's Django group memberships via checkboxes."""
groups = forms.ModelMultipleChoiceField(
label=_("Groups"),
required=False,
queryset=Group.objects.all(),
widget=forms.CheckboxSelectMultiple(),
)
[docs]
class GroupPermissionsForm(forms.ModelForm):
"""Form for assigning Django permissions to a ``Group`` via checkboxes."""
permissions = forms.ModelMultipleChoiceField(
label=_("Permissions"),
required=False,
queryset=Permission.objects.select_related("content_type"),
widget=forms.CheckboxSelectMultiple(),
)