Auto-sync system default tasks to firms and open engagements
This commit is contained in:
@@ -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)})
|
||||
|
||||
Reference in New Issue
Block a user