From 9f4e65023fde49bcd0844e8b21a6a0fe4f4e5352 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Mon, 2 Feb 2026 13:43:52 +0530 Subject: [PATCH] fix(api): make POST /departments atomic + handle integrity errors --- backend/app/api/org.py | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/backend/app/api/org.py b/backend/app/api/org.py index ed94c42..1395f68 100644 --- a/backend/app/api/org.py +++ b/backend/app/api/org.py @@ -1,6 +1,7 @@ from __future__ import annotations from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.exc import IntegrityError from sqlmodel import Session, select from app.api.utils import log_activity, get_actor_employee_id @@ -17,16 +18,40 @@ def list_departments(session: Session = Depends(get_session)): @router.post("/departments", response_model=Department) -def create_department(payload: DepartmentCreate, session: Session = Depends(get_session), actor_employee_id: int = Depends(get_actor_employee_id)): +def create_department( + payload: DepartmentCreate, + session: Session = Depends(get_session), + actor_employee_id: int = Depends(get_actor_employee_id), +): + """Create a department. + + Important: keep the operation atomic. We flush to get dept.id, log the activity, + then commit once. We also translate common DB integrity errors into 409s. + """ + dept = Department(name=payload.name, head_employee_id=payload.head_employee_id) session.add(dept) - session.commit() + + try: + session.flush() # assigns dept.id without committing + log_activity( + session, + actor_employee_id=actor_employee_id, + entity_type="department", + entity_id=dept.id, + verb="created", + payload={"name": dept.name}, + ) + session.commit() + except IntegrityError: + session.rollback() + raise HTTPException(status_code=409, detail="Department already exists or violates constraints") + session.refresh(dept) - log_activity(session, actor_employee_id=actor_employee_id, entity_type="department", entity_id=dept.id, verb="created", payload={"name": dept.name}) - session.commit() return dept + @router.patch("/departments/{department_id}", response_model=Department) def update_department(department_id: int, payload: DepartmentUpdate, session: Session = Depends(get_session), actor_employee_id: int = Depends(get_actor_employee_id)): dept = session.get(Department, department_id)