Add periodic engagements and statutory due dates
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user