Add Excel import export and code linking for task categories

This commit is contained in:
A R R R Associates
2026-08-31 12:52:50 +05:30
parent af4f6e8b28
commit fbb8607842
6 changed files with 551 additions and 14 deletions
+87
View File
@@ -27,6 +27,7 @@ from app.modules.services.bulk_imports import (
from app.modules.services.catalogue_export import build_service_catalogue_export
from app.modules.services.default_tasks_export import build_default_tasks_export
from app.modules.services.firm_tasks_export import build_firm_tasks_export
from app.modules.services.task_categories_excel import build_task_categories_export, import_task_categories
from app.modules.services.task_tools import list_task_tools, normalize_task_tool_code
from app.modules.services.task_duplicates import find_duplicate_pairs, merge_default_tasks, merge_firm_tasks
from app.modules.services.due_dates import (
@@ -2157,6 +2158,50 @@ def firm_task_categories_page(request: Request, catalogue_id: int):
db.close()
@router.get('/templates/{catalogue_id}/categories/export')
def firm_task_categories_export(request: Request, catalogue_id: int):
db = CommonSessionLocal()
try:
user = get_current_user(request, db=db)
if not user:
return RedirectResponse(url='/login', status_code=303)
if not (_has_perm(db, user, 'service_tasks.view') or _can_manage_firm_tasks(db, user)):
return _redirect_denied()
tenant_id = _active_tenant_id(request, user)
service = get_catalogue(db, catalogue_id)
selection = get_firm_selection(db, tenant_id=tenant_id, catalogue_id=catalogue_id)
if not service or not selection or not selection.is_enabled:
return RedirectResponse(url='/services/templates', status_code=303)
payload = build_task_categories_export(db, catalogue_id=catalogue_id, tenant_id=tenant_id)
filename = f"{service.service_code}_firm_task_categories_{datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')}.xlsx"
return StreamingResponse(iter([payload]), media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', headers={'Content-Disposition': f'attachment; filename="{filename}"'})
finally:
db.close()
@router.post('/templates/{catalogue_id}/categories/import')
def firm_task_categories_import(request: Request, catalogue_id: int, file: UploadFile = File(...), csrf_token: str = Form(...)):
validate_csrf(request, csrf_token)
db = CommonSessionLocal()
try:
user = get_current_user(request, db=db)
if not user:
return RedirectResponse(url='/login', status_code=303)
if not _can_manage_firm_tasks(db, user):
return _redirect_denied()
tenant_id = _active_tenant_id(request, user)
service = get_catalogue(db, catalogue_id)
selection = get_firm_selection(db, tenant_id=tenant_id, catalogue_id=catalogue_id)
if not service or not selection or not selection.is_enabled:
return RedirectResponse(url='/services/templates', status_code=303)
result = import_task_categories(db, current_user=user, tenant_id=tenant_id, file_bytes=file.file.read(), expected_service_code=service.service_code)
if result.get('errors'):
return RedirectResponse(url=f'/services/templates/{catalogue_id}/categories?import_error={len(result["errors"])}', status_code=303)
return RedirectResponse(url=f'/services/templates/{catalogue_id}/categories?imported=1&created={result.get("created", 0)}&updated={result.get("updated", 0)}', status_code=303)
finally:
db.close()
@router.post('/templates/{catalogue_id}/categories/new')
def firm_task_category_create(request: Request, catalogue_id: int, name: str = Form(...), code: str = Form(''), sort_order: int = Form(100), csrf_token: str = Form(...)):
validate_csrf(request, csrf_token)
@@ -2231,6 +2276,48 @@ def system_task_category_create(request: Request, catalogue_id: int, name: str =
finally: db.close()
@router.get('/catalogue/{catalogue_id}/task-categories/export')
def system_task_categories_export(request: Request, catalogue_id: int):
db = CommonSessionLocal()
try:
user = get_current_user(request, db=db)
if not user:
return RedirectResponse(url='/login', status_code=303)
require_permission(db, user, 'service_tasks.view')
if not _is_system_admin(db, user):
return _redirect_denied()
service = get_catalogue(db, catalogue_id)
if not service:
return RedirectResponse(url='/services/defaults', status_code=303)
payload = build_task_categories_export(db, catalogue_id=catalogue_id, tenant_id=None)
filename = f"{service.service_code}_default_task_categories_{datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')}.xlsx"
return StreamingResponse(iter([payload]), media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', headers={'Content-Disposition': f'attachment; filename="{filename}"'})
finally:
db.close()
@router.post('/catalogue/{catalogue_id}/task-categories/import')
def system_task_categories_import(request: Request, catalogue_id: int, file: UploadFile = File(...), csrf_token: str = Form(...)):
validate_csrf(request, csrf_token)
db = CommonSessionLocal()
try:
user = get_current_user(request, db=db)
if not user:
return RedirectResponse(url='/login', status_code=303)
require_permission(db, user, 'service_tasks.edit')
if not _is_system_admin(db, user):
return _redirect_denied()
service = get_catalogue(db, catalogue_id)
if not service:
return RedirectResponse(url='/services/defaults', status_code=303)
result = import_task_categories(db, current_user=user, tenant_id=None, file_bytes=file.file.read(), expected_service_code=service.service_code)
if result.get('errors'):
return RedirectResponse(url=f'/services/catalogue/{catalogue_id}/task-categories?import_error={len(result["errors"])}', status_code=303)
return RedirectResponse(url=f'/services/catalogue/{catalogue_id}/task-categories?imported=1&created={result.get("created", 0)}&updated={result.get("updated", 0)}', status_code=303)
finally:
db.close()
@router.post('/catalogue/{catalogue_id}/task-categories/{category_id}/update')
def system_task_category_update(request: Request, catalogue_id: int, category_id: int, name: str = Form(...), code: str = Form(''), sort_order: int = Form(100), is_active: str | None = Form(None), csrf_token: str = Form(...)):
validate_csrf(request,csrf_token); db=CommonSessionLocal()