Add periodic engagements and statutory due dates

This commit is contained in:
A R R R Associates
2026-08-05 12:31:39 +05:30
parent dd2a69dbaa
commit c35c89982e
9 changed files with 415 additions and 94 deletions
+157 -84
View File
@@ -27,6 +27,9 @@ from app.modules.services.client_services import (
get_existing_subscription,
get_subscription,
normalize_financial_year,
normalize_period_label,
period_choices_for_service,
recurrence_requires_period,
list_assignable_users,
list_clients_for_assignment,
list_enabled_services_for_assignment,
@@ -241,6 +244,7 @@ def subscription_create_page(request: Request, client_id: int | None = None):
review_partners=review_partners,
selected_client_id=client_id,
financial_year=_active_financial_year(request),
period_choices=[],
)
finally:
db.close()
@@ -257,6 +261,8 @@ def subscription_create_submit(
performing_partner_user_id: str = Form(""),
review_partner_user_id: str = Form(""),
financial_year: str = Form(""),
period_label: str = Form(""),
generate_all_periods: str | None = Form(None),
start_date: str = Form(""),
end_date: str = Form(""),
expiry_date: str = Form(""),
@@ -285,62 +291,89 @@ def subscription_create_submit(
if not firm_selection:
return RedirectResponse(url="/services/engagements/new", status_code=303)
existing = get_existing_subscription(
db,
tenant_id=tenant_id,
client_id=client_id,
service_catalogue_id=service_catalogue_id,
financial_year=selected_financial_year,
)
recurrence_type = getattr(firm_selection.catalogue, "recurrence_type", None)
if recurrence_requires_period(recurrence_type):
available_periods = period_choices_for_service(selected_financial_year, recurrence_type)
if generate_all_periods is not None:
requested_periods = [code for code, _label in available_periods]
else:
try:
requested_periods = [normalize_period_label(period_label, financial_year=selected_financial_year, recurrence_type=recurrence_type)]
except ValueError:
return RedirectResponse(url="/services/engagements/new?error=period", status_code=303)
else:
requested_periods = [""]
client = db.get(Client, client_id)
if not client or client.tenant_id != tenant_id:
return RedirectResponse(url="/services/engagements/new", status_code=303)
if existing:
row = existing
else:
created_rows = []
existing_rows = []
for requested_period in requested_periods:
existing = get_existing_subscription(
db,
tenant_id=tenant_id,
client_id=client_id,
service_catalogue_id=service_catalogue_id,
financial_year=selected_financial_year,
period_label=requested_period,
)
if existing:
existing_rows.append(existing)
continue
row = ClientServiceSubscription(
tenant_id=tenant_id,
client_id=client_id,
service_catalogue_id=service_catalogue_id,
financial_year=selected_financial_year,
period_label=requested_period,
assessment_year=assessment_year_from_financial_year(selected_financial_year),
engagement_type=getattr(firm_selection.catalogue, "engagement_type", "non_assurance") or "non_assurance",
created_by_user_id=user.id,
)
db.add(row)
created_rows.append(row)
if getattr(row, "is_locked", False) or is_row_financial_year_locked(db, row):
return RedirectResponse(url=f"/services/engagements/{row.id}?year_locked=1", status_code=303)
row.financial_year = selected_financial_year
row.assessment_year = assessment_year_from_financial_year(selected_financial_year)
if not getattr(row, "engagement_type", None):
row.engagement_type = getattr(firm_selection.catalogue, "engagement_type", "non_assurance") or "non_assurance"
row.branch_id = firm_selection.default_branch_id or getattr(user, "branch_id", None)
row.firm_service_selection_id = firm_selection.id
row.assigned_partner_user_id = int(assigned_partner_user_id) if assigned_partner_user_id.strip() else None
requested_performing_partner_id = int(performing_partner_user_id) if performing_partner_user_id.strip() else None
row.performing_partner_user_id = requested_performing_partner_id or getattr(client, "default_performing_partner_user_id", None) or row.assigned_partner_user_id
row.assigned_manager_user_id = int(assigned_manager_user_id) if assigned_manager_user_id.strip() else None
row.assigned_staff_user_id = int(assigned_staff_user_id) if assigned_staff_user_id.strip() else None
if review_partner_required_for_engagement(db, tenant_id=tenant_id, engagement_type=row.engagement_type):
row.review_partner_user_id = int(review_partner_user_id) if review_partner_user_id.strip() else getattr(client, "default_review_partner_user_id", None)
if not created_rows:
row = existing_rows[0]
else:
row.review_partner_user_id = None
row.start_date = parse_date(start_date)
row.end_date = parse_date(end_date)
row.expiry_date = parse_date(expiry_date)
row.status = status or "active"
row.remarks = remarks.strip() or None
row.is_active = is_active is not None
row.updated_by_user_id = user.id
apply_due_date_rule_to_subscription(db, row)
ensure_engagement_quality_workflow(db, subscription=row, actor_user_id=user.id, create_declarations=False)
enforce_quality_gate_on_subscription(row)
row = created_rows[0]
rows_to_update = created_rows or [row]
for row in rows_to_update:
if getattr(row, "is_locked", False) or is_row_financial_year_locked(db, row):
return RedirectResponse(url=f"/services/engagements/{row.id}?year_locked=1", status_code=303)
row.financial_year = selected_financial_year
row.assessment_year = assessment_year_from_financial_year(selected_financial_year)
if not getattr(row, "engagement_type", None):
row.engagement_type = getattr(firm_selection.catalogue, "engagement_type", "non_assurance") or "non_assurance"
row.branch_id = firm_selection.default_branch_id or getattr(user, "branch_id", None)
row.firm_service_selection_id = firm_selection.id
row.assigned_partner_user_id = int(assigned_partner_user_id) if assigned_partner_user_id.strip() else None
requested_performing_partner_id = int(performing_partner_user_id) if performing_partner_user_id.strip() else None
row.performing_partner_user_id = requested_performing_partner_id or getattr(client, "default_performing_partner_user_id", None) or row.assigned_partner_user_id
row.assigned_manager_user_id = int(assigned_manager_user_id) if assigned_manager_user_id.strip() else None
row.assigned_staff_user_id = int(assigned_staff_user_id) if assigned_staff_user_id.strip() else None
if review_partner_required_for_engagement(db, tenant_id=tenant_id, engagement_type=row.engagement_type):
row.review_partner_user_id = int(review_partner_user_id) if review_partner_user_id.strip() else getattr(client, "default_review_partner_user_id", None)
else:
row.review_partner_user_id = None
row.start_date = parse_date(start_date)
row.end_date = parse_date(end_date)
row.expiry_date = parse_date(expiry_date)
row.status = status or "active"
row.remarks = remarks.strip() or None
row.is_active = is_active is not None
row.updated_by_user_id = user.id
apply_due_date_rule_to_subscription(db, row, force=True)
ensure_engagement_quality_workflow(db, subscription=row, actor_user_id=user.id, create_declarations=False)
enforce_quality_gate_on_subscription(row)
db.commit()
db.refresh(row)
if len(created_rows) > 1:
return RedirectResponse(url=f"/services/engagements?financial_year={selected_financial_year}&bulk_created={len(created_rows)}", status_code=303)
return RedirectResponse(url=f"/services/engagements/{row.id}", status_code=303)
finally:
db.close()
@@ -398,6 +431,7 @@ def subscription_bulk_create_page(request: Request, error: str = ""):
"review_partner": "The selected Review Partner is not available for this firm.",
"review_partner_required": "Review Partner is mandatory for an assurance engagement.",
"clients": "Select at least one permitted client.",
"period": "Select a valid month or quarter for the recurring service.",
}.get(error),
)
finally:
@@ -415,6 +449,8 @@ def subscription_bulk_create_submit(
performing_partner_user_id: str = Form(""),
review_partner_user_id: str = Form(""),
financial_year: str = Form(""),
period_label: str = Form(""),
generate_all_periods: str | None = Form(None),
remarks: str = Form(""),
csrf_token: str = Form(...),
):
@@ -451,6 +487,19 @@ def subscription_bulk_create_submit(
if not firm_selection:
return RedirectResponse(url="/services/engagements/bulk-new?error=service", status_code=303)
recurrence_type = getattr(firm_selection.catalogue, "recurrence_type", None)
if recurrence_requires_period(recurrence_type):
available_periods = period_choices_for_service(selected_financial_year, recurrence_type)
if generate_all_periods is not None:
requested_periods = [code for code, _label in available_periods]
else:
try:
requested_periods = [normalize_period_label(period_label, financial_year=selected_financial_year, recurrence_type=recurrence_type)]
except ValueError:
return RedirectResponse(url="/services/engagements/bulk-new?error=period", status_code=303)
else:
requested_periods = [""]
partners = list_assignable_users(db, tenant_id=tenant_id, role_names=("Partner",))
managers = list_assignable_users(db, tenant_id=tenant_id, role_names=("Branch Manager",))
staff_users = list_assignable_users(db, tenant_id=tenant_id, role_names=("Staff",))
@@ -521,56 +570,59 @@ def subscription_bulk_create_submit(
)
for client_id in selected_client_ids:
existing = get_existing_subscription(
db,
tenant_id=tenant_id,
client_id=client_id,
service_catalogue_id=service_catalogue_id,
financial_year=selected_financial_year,
)
if existing:
existing_count += 1
continue
client = db.get(Client, client_id)
if not client or client.tenant_id != tenant_id:
skipped_count += 1
skipped_count += len(requested_periods)
continue
row = ClientServiceSubscription(
tenant_id=tenant_id,
branch_id=engagement_branch_id,
client_id=client_id,
service_catalogue_id=service_catalogue_id,
firm_service_selection_id=firm_selection.id,
assigned_partner_user_id=assigned_partner_user_id,
performing_partner_user_id=performing_partner_id or getattr(client, "default_performing_partner_user_id", None) or assigned_partner_user_id,
assigned_manager_user_id=manager_id,
assigned_staff_user_id=staff_id,
review_partner_user_id=(
review_partner_id
if review_partner_id is not None
else (
getattr(client, "default_review_partner_user_id", None)
if requires_review_partner
else None
)
),
financial_year=selected_financial_year,
assessment_year=assessment_year_from_financial_year(selected_financial_year),
engagement_type=engagement_type,
status="active",
remarks=remarks.strip() or None,
is_active=True,
created_by_user_id=user.id,
updated_by_user_id=user.id,
)
db.add(row)
db.flush()
apply_due_date_rule_to_subscription(db, row)
ensure_engagement_quality_workflow(db, subscription=row, actor_user_id=user.id, create_declarations=False)
enforce_quality_gate_on_subscription(row)
created_count += 1
for requested_period in requested_periods:
existing = get_existing_subscription(
db,
tenant_id=tenant_id,
client_id=client_id,
service_catalogue_id=service_catalogue_id,
financial_year=selected_financial_year,
period_label=requested_period,
)
if existing:
existing_count += 1
continue
row = ClientServiceSubscription(
tenant_id=tenant_id,
branch_id=engagement_branch_id,
client_id=client_id,
service_catalogue_id=service_catalogue_id,
firm_service_selection_id=firm_selection.id,
assigned_partner_user_id=assigned_partner_user_id,
performing_partner_user_id=performing_partner_id or getattr(client, "default_performing_partner_user_id", None) or assigned_partner_user_id,
assigned_manager_user_id=manager_id,
assigned_staff_user_id=staff_id,
review_partner_user_id=(
review_partner_id
if review_partner_id is not None
else (
getattr(client, "default_review_partner_user_id", None)
if requires_review_partner
else None
)
),
financial_year=selected_financial_year,
period_label=requested_period,
assessment_year=assessment_year_from_financial_year(selected_financial_year),
engagement_type=engagement_type,
status="active",
remarks=remarks.strip() or None,
is_active=True,
created_by_user_id=user.id,
updated_by_user_id=user.id,
)
db.add(row)
db.flush()
apply_due_date_rule_to_subscription(db, row, force=True)
ensure_engagement_quality_workflow(db, subscription=row, actor_user_id=user.id, create_declarations=False)
enforce_quality_gate_on_subscription(row)
created_count += 1
db.commit()
return RedirectResponse(
@@ -1011,6 +1063,7 @@ def subscription_edit_page(request: Request, subscription_id: int):
review_partners=review_partners,
selected_client_id=row.client_id,
financial_year=row.financial_year,
period_choices=period_choices_for_service(row.financial_year, getattr(row.catalogue, "recurrence_type", None)),
)
finally:
db.close()
@@ -1025,6 +1078,7 @@ def subscription_edit_submit(
assigned_staff_user_id: str = Form(""),
performing_partner_user_id: str = Form(""),
review_partner_user_id: str = Form(""),
period_label: str = Form(""),
start_date: str = Form(""),
end_date: str = Form(""),
expiry_date: str = Form(""),
@@ -1054,6 +1108,25 @@ def subscription_edit_submit(
if getattr(row, "is_locked", False) or is_row_financial_year_locked(db, row):
return RedirectResponse(url=f"/services/engagements/{row.id}?year_locked=1", status_code=303)
try:
new_period_label = normalize_period_label(
period_label,
financial_year=row.financial_year,
recurrence_type=getattr(row.catalogue, "recurrence_type", None),
)
except ValueError:
return RedirectResponse(url=f"/services/engagements/{row.id}/edit?error=period", status_code=303)
duplicate = get_existing_subscription(
db,
tenant_id=tenant_id,
client_id=row.client_id,
service_catalogue_id=row.service_catalogue_id,
financial_year=row.financial_year,
period_label=new_period_label,
)
if duplicate and duplicate.id != row.id:
return RedirectResponse(url=f"/services/engagements/{row.id}/edit?error=duplicate_period", status_code=303)
row.period_label = new_period_label
row.assigned_partner_user_id = int(assigned_partner_user_id) if assigned_partner_user_id.strip() else None
requested_performing_partner_id = int(performing_partner_user_id) if performing_partner_user_id.strip() else None
row.performing_partner_user_id = requested_performing_partner_id or getattr(row.client, "default_performing_partner_user_id", None) or row.assigned_partner_user_id
@@ -1070,7 +1143,7 @@ def subscription_edit_submit(
row.remarks = remarks.strip() or None
row.is_active = is_active is not None
row.updated_by_user_id = user.id
apply_due_date_rule_to_subscription(db, row)
apply_due_date_rule_to_subscription(db, row, force=True)
ensure_engagement_quality_workflow(db, subscription=row, actor_user_id=user.id, create_declarations=False)
enforce_quality_gate_on_subscription(row)
db.commit()