Auto-sync system default tasks to firms and open engagements

This commit is contained in:
A R R R Associates
2026-09-19 12:47:53 +05:30
parent f6776cc96a
commit 2d6043962b
9 changed files with 857 additions and 313 deletions
+37 -1
View File
@@ -1066,6 +1066,7 @@ def import_system_default_tasks(db: Session, *, current_user, file_bytes: bytes,
return {"created": 0, "updated": 0, "skipped": 0, "errors": [{"row": 1, "message": f"Missing columns: {', '.join(missing)}"}]}
created = updated = skipped = 0
errors: list[dict] = []
touched_catalogue_ids: set[int] = set()
for row_no, row in enumerate(ws.iter_rows(min_row=2, values_only=True), start=2):
if not any(v not in (None, "") for v in row):
continue
@@ -1078,6 +1079,7 @@ def import_system_default_tasks(db: Session, *, current_user, file_bytes: bytes,
catalogue = find_service(db, service_code=service_code)
if not catalogue:
raise ValueError("Service code not found in service catalogue.")
touched_catalogue_ids.add(int(catalogue.id))
if not sequence_no or not task_name:
raise ValueError("sequence_no and task_name are required.")
task = db.execute(
@@ -1128,11 +1130,42 @@ def import_system_default_tasks(db: Session, *, current_user, file_bytes: bytes,
task.is_active = _bool(_cell(row, headers, "is_active"), True)
except Exception as exc:
errors.append({"row": row_no, "message": str(exc)})
rollout_summary = {
"firms_processed": 0,
"firms_changed": 0,
"custom_updates_available": 0,
"engagement_created": 0,
"engagement_updated_pending": 0,
"engagement_deactivated_pending": 0,
}
if not errors:
# Apply the same transaction to every enabled firm and safely refresh only
# unstarted tasks in open/unlocked engagements. Customized firm tasks are
# never overwritten; they receive an update-available decision instead.
from app.modules.services.default_task_sync import sync_system_defaults_to_all_firms
for catalogue_id in sorted(touched_catalogue_ids):
rollout = sync_system_defaults_to_all_firms(
db,
service_catalogue_id=catalogue_id,
updated_by_user_id=current_user.id,
sync_open_engagements=True,
)
rollout_summary["firms_processed"] += rollout.firms_processed
rollout_summary["firms_changed"] += rollout.firms_changed
rollout_summary["custom_updates_available"] += rollout.custom_updates_available
rollout_summary["engagement_created"] += rollout.engagement_created
rollout_summary["engagement_updated_pending"] += rollout.engagement_updated_pending
rollout_summary["engagement_deactivated_pending"] += rollout.engagement_deactivated_pending
db.commit()
else:
db.rollback()
return {"created": created if not errors else 0, "updated": updated if not errors else 0, "skipped": skipped, "errors": errors}
return {
"created": created if not errors else 0,
"updated": updated if not errors else 0,
"skipped": skipped,
"errors": errors,
**(rollout_summary if not errors else {}),
}
def import_firm_task_templates(db: Session, *, current_user, tenant_id: int, file_bytes: bytes, update_existing: bool = True, expected_service_code: str | None = None) -> dict:
@@ -1210,6 +1243,9 @@ def import_firm_task_templates(db: Session, *, current_user, tenant_id: int, fil
task.aqmm_blocks_final_release = _bool(_cell(row, headers, "aqmm_blocks_final_release"), False)
task.aqmm_reference = _clean(_cell(row, headers, "aqmm_reference")) or None
task.is_active = _bool(_cell(row, headers, "is_active"), True)
# A firm-level workbook import is an explicit firm customization. Linked
# system defaults remain available for later Firm Admin upgrade review.
task.is_customized = True
task.updated_by_user_id = current_user.id
except Exception as exc:
errors.append({"row": row_no, "message": str(exc)})