Use registration master for service catalogue scope types
This commit is contained in:
@@ -19,11 +19,18 @@
|
||||
<div id="registration-type-field"><label class="mb-2 block text-sm font-medium text-slate-700">Required Registration Type</label>
|
||||
<select name="required_registration_type" class="w-full rounded-xl border border-slate-300 px-4 py-2 text-sm">
|
||||
<option value="">Not applicable</option>
|
||||
{% for value in ['GST','TAN','PF','ESI','PT','IEC','FSSAI','UDYAM','OTHER'] %}
|
||||
<option value="{{ value }}" {% if catalogue and catalogue.required_registration_type == value %}selected{% endif %}>{{ value }}</option>
|
||||
{% set current_registration_type = catalogue.required_registration_type if catalogue and catalogue.required_registration_type else '' %}
|
||||
{% if current_registration_type and current_registration_type not in registration_type_codes %}
|
||||
<option value="{{ current_registration_type }}" selected>{{ current_registration_type }} — Existing legacy value</option>
|
||||
{% endif %}
|
||||
{% for registration_type in registration_types %}
|
||||
<option value="{{ registration_type.code }}" {% if current_registration_type == registration_type.code %}selected{% endif %}>{{ registration_type.code }} — {{ registration_type.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<p class="mt-1 text-xs text-slate-500">Used only for Registration-level services.</p>
|
||||
<p class="mt-1 text-xs text-slate-500">Used only for Registration-level services. Values come from the active Registration Type Master.</p>
|
||||
{% if request.query_params.get('error') == 'registration_type' %}
|
||||
<p class="mt-1 text-xs font-medium text-red-600">Select an active Registration Type Master value.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div><label class="mb-2 block text-sm font-medium text-slate-700">Sort Order</label><input type="number" name="sort_order" value="{{ catalogue.sort_order if catalogue else 100 }}" class="w-full rounded-xl border border-slate-300 px-4 py-2 text-sm"></div>
|
||||
<div class="flex items-center gap-6 pt-7 text-sm text-slate-700"><label class="inline-flex items-center gap-2"><input type="checkbox" name="is_active" {% if not catalogue or catalogue.is_active %}checked{% endif %}> Active</label><label class="inline-flex items-center gap-2"><input type="checkbox" name="is_client_requestable" {% if catalogue and catalogue.is_client_requestable %}checked{% endif %}> Client Requestable</label><label class="inline-flex items-center gap-2"><input type="checkbox" name="is_consultant_requestable" {% if catalogue and catalogue.is_consultant_requestable %}checked{% endif %}> Consultant Requestable</label></div>
|
||||
|
||||
@@ -14,6 +14,7 @@ from app.modules.core.audit.service import model_snapshot, pair_before_after, wr
|
||||
from app.modules.core.rbac.deps import get_user_permissions, get_user_roles
|
||||
from app.modules.core.rbac.permission_guard import require_permission
|
||||
from app.modules.core.tenancy.models import Branch
|
||||
from app.modules.registrations.models import RegistrationType
|
||||
from app.modules.services.models import FirmServiceSelection, FirmServiceTaskTemplate, ServiceCatalogue, ServiceCategory, ServiceDefaultTaskTemplate, ServiceDueDateRule, FirmTaskDocumentRequirement, FirmTaskDocumentTemplate
|
||||
from app.modules.services.bulk_imports import (
|
||||
build_template as build_bulk_import_template,
|
||||
@@ -87,6 +88,50 @@ def _render(request: Request, template: str, db, user, **ctx):
|
||||
return templates.TemplateResponse(template, _base_ctx(request, user, db, **ctx))
|
||||
|
||||
|
||||
def _active_registration_types(db):
|
||||
"""Return the active Registration Type master rows used by Service Catalogue forms."""
|
||||
return db.execute(
|
||||
select(RegistrationType)
|
||||
.where(RegistrationType.is_active.is_(True))
|
||||
.order_by(RegistrationType.sort_order, RegistrationType.name, RegistrationType.code)
|
||||
).scalars().all()
|
||||
|
||||
|
||||
def _registration_type_form_context(db):
|
||||
registration_types = _active_registration_types(db)
|
||||
return {
|
||||
"registration_types": registration_types,
|
||||
"registration_type_codes": [row.code.upper() for row in registration_types],
|
||||
}
|
||||
|
||||
|
||||
def _validated_registration_type_code(db, value: str, *, existing_value: str | None = None) -> str | None:
|
||||
"""Validate a submitted catalogue registration code against Registration Type master.
|
||||
|
||||
Existing non-master legacy values are allowed only when left unchanged so editing an
|
||||
unrelated field never destroys historical catalogue data. New/changed values must be
|
||||
active Registration Type master codes.
|
||||
"""
|
||||
code = (value or "").strip().upper()
|
||||
if not code:
|
||||
return None
|
||||
|
||||
exists = db.execute(
|
||||
select(RegistrationType.id).where(
|
||||
RegistrationType.code == code,
|
||||
RegistrationType.is_active.is_(True),
|
||||
)
|
||||
).scalar_one_or_none()
|
||||
if exists is not None:
|
||||
return code
|
||||
|
||||
current = (existing_value or "").strip().upper()
|
||||
if current and code == current:
|
||||
return code
|
||||
|
||||
raise ValueError("registration_type")
|
||||
|
||||
|
||||
def _redirect_denied():
|
||||
from app.core.http_responses import ui_access_denied
|
||||
return ui_access_denied()
|
||||
@@ -476,7 +521,7 @@ def catalogue_create_page(request: Request):
|
||||
if not _is_system_admin(db, user):
|
||||
return _redirect_denied()
|
||||
require_permission(db, user, 'services.create')
|
||||
return _render(request, 'modules/services/templates/services/catalogue_form.html', db, user, title='Create Service Catalogue', mode='create', catalogue=None, categories=list_categories(db))
|
||||
return _render(request, 'modules/services/templates/services/catalogue_form.html', db, user, title='Create Service Catalogue', mode='create', catalogue=None, categories=list_categories(db), **_registration_type_form_context(db))
|
||||
except Exception:
|
||||
return _redirect_denied()
|
||||
finally:
|
||||
@@ -495,6 +540,10 @@ def catalogue_create_submit(request: Request, service_code: str = Form(...), ser
|
||||
return _redirect_denied()
|
||||
require_permission(db, user, 'services.create')
|
||||
selected_category = get_category(db, int(category_id)) if str(category_id).strip() else None
|
||||
try:
|
||||
registration_type_code = _validated_registration_type_code(db, required_registration_type)
|
||||
except ValueError:
|
||||
return RedirectResponse(url='/services/catalogue/new?error=registration_type', status_code=303)
|
||||
row = ServiceCatalogue(
|
||||
service_code=normalize_code(service_code),
|
||||
service_name=service_name.strip(),
|
||||
@@ -503,7 +552,7 @@ def catalogue_create_submit(request: Request, service_code: str = Form(...), ser
|
||||
recurrence_type=recurrence_type.strip() or None,
|
||||
engagement_type=normalize_engagement_type(engagement_type),
|
||||
service_scope_type=(service_scope_type or 'client').strip().lower(),
|
||||
required_registration_type=(required_registration_type or '').strip().upper() or None,
|
||||
required_registration_type=registration_type_code,
|
||||
sort_order=sort_order,
|
||||
description=description.strip() or None,
|
||||
applicable_individual=applicable_individual is not None,
|
||||
@@ -806,7 +855,7 @@ def catalogue_edit_page(request: Request, catalogue_id: int):
|
||||
row = get_catalogue(db, catalogue_id)
|
||||
if not row or not row.is_active:
|
||||
return RedirectResponse(url='/services/catalogue', status_code=303)
|
||||
return _render(request, 'modules/services/templates/services/catalogue_form.html', db, user, title='Edit Service Catalogue', mode='edit', catalogue=row, categories=list_categories(db))
|
||||
return _render(request, 'modules/services/templates/services/catalogue_form.html', db, user, title='Edit Service Catalogue', mode='edit', catalogue=row, categories=list_categories(db), **_registration_type_form_context(db))
|
||||
except Exception:
|
||||
return _redirect_denied()
|
||||
finally:
|
||||
@@ -828,13 +877,22 @@ def catalogue_edit_submit(request: Request, catalogue_id: int, service_name: str
|
||||
if not row or not row.is_active:
|
||||
return RedirectResponse(url='/services/catalogue', status_code=303)
|
||||
selected_category = get_category(db, int(category_id)) if str(category_id).strip() else None
|
||||
try:
|
||||
registration_type_code = _validated_registration_type_code(
|
||||
db, required_registration_type, existing_value=row.required_registration_type
|
||||
)
|
||||
except ValueError:
|
||||
return RedirectResponse(
|
||||
url=f'/services/catalogue/{catalogue_id}/edit?error=registration_type',
|
||||
status_code=303,
|
||||
)
|
||||
row.service_name = service_name.strip()
|
||||
row.category_id = selected_category.id if selected_category else None
|
||||
row.category = selected_category.name if selected_category else None
|
||||
row.recurrence_type = recurrence_type.strip() or None
|
||||
row.engagement_type = normalize_engagement_type(engagement_type)
|
||||
row.service_scope_type = (service_scope_type or 'client').strip().lower()
|
||||
row.required_registration_type = (required_registration_type or '').strip().upper() or None
|
||||
row.required_registration_type = registration_type_code
|
||||
row.sort_order = sort_order
|
||||
row.applicable_individual = applicable_individual is not None
|
||||
row.applicable_proprietorship = applicable_proprietorship is not None
|
||||
|
||||
Reference in New Issue
Block a user