Adding ability to update / patch by sku, adding uniqueness constraint violation checks
This commit is contained in:
1 parent
af5343da0c
commit
d8db87c13e
13 files changed
+541
-13
No files matched your search
@@ -33,6 +33,10 @@ async def create_attachment_point_name(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
actor: User = Depends(require_permission("attachment_point_names:write")),
|
||||
):
|
||||
if await apn_crud.get_by_name(db, payload.name):
|
||||
raise HTTPException(
|
||||
status_code=409, detail="Attachment point name already exists"
|
||||
)
|
||||
apn = await apn_crud.create(db, name=payload.name)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
@@ -68,6 +72,12 @@ async def update_attachment_point_name(
|
||||
if apn is None:
|
||||
raise HTTPException(status_code=404, detail="Attachment point name not found")
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
if "name" in changes:
|
||||
existing = await apn_crud.get_by_name(db, changes["name"])
|
||||
if existing is not None and existing.id != apn.id:
|
||||
raise HTTPException(
|
||||
status_code=409, detail="Attachment point name already exists"
|
||||
)
|
||||
apn = await apn_crud.update(db, apn, changes)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
|
||||
+24
-10
@@ -20,6 +20,15 @@ from app.services import augmented_part_sync
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
async def _get_augmented_part_by_identifier(db: AsyncSession, identifier: str):
|
||||
"""Resolve an AugmentedPart by numeric id, falling back to sku."""
|
||||
if identifier.isdigit():
|
||||
ap = await augmented_part_crud.get(db, int(identifier))
|
||||
if ap is not None:
|
||||
return ap
|
||||
return await augmented_part_crud.get_by_sku(db, identifier)
|
||||
|
||||
|
||||
@router.get("", response_model=list[AugmentedPartRead])
|
||||
async def list_augmented_parts(
|
||||
skip: int = 0,
|
||||
@@ -39,6 +48,8 @@ async def create_augmented_part(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
actor: User = Depends(require_permission("augmented_parts:write")),
|
||||
):
|
||||
if payload.sku and await augmented_part_crud.get_by_sku(db, payload.sku):
|
||||
raise HTTPException(status_code=409, detail="SKU already in use")
|
||||
ap = await augmented_part_crud.create(
|
||||
db,
|
||||
sku=payload.sku,
|
||||
@@ -94,17 +105,21 @@ async def get_augmented_part(
|
||||
return ap
|
||||
|
||||
|
||||
@router.patch("/{augmented_part_id}", response_model=AugmentedPartRead)
|
||||
@router.patch("/{identifier}", response_model=AugmentedPartRead)
|
||||
async def update_augmented_part(
|
||||
augmented_part_id: int,
|
||||
identifier: str,
|
||||
payload: AugmentedPartUpdate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
actor: User = Depends(require_permission("augmented_parts:write")),
|
||||
):
|
||||
ap = await augmented_part_crud.get(db, augmented_part_id)
|
||||
ap = await _get_augmented_part_by_identifier(db, identifier)
|
||||
if ap is None:
|
||||
raise HTTPException(status_code=404, detail="Augmented part not found")
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
if changes.get("sku"):
|
||||
existing = await augmented_part_crud.get_by_sku(db, changes["sku"])
|
||||
if existing is not None and existing.id != ap.id:
|
||||
raise HTTPException(status_code=409, detail="SKU already in use")
|
||||
ap = await augmented_part_crud.update(db, ap, changes)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
@@ -117,15 +132,16 @@ async def update_augmented_part(
|
||||
return ap
|
||||
|
||||
|
||||
@router.delete("/{augmented_part_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
@router.delete("/{identifier}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def delete_augmented_part(
|
||||
augmented_part_id: int,
|
||||
identifier: str,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
actor: User = Depends(require_permission("augmented_parts:delete")),
|
||||
):
|
||||
ap = await augmented_part_crud.get(db, augmented_part_id)
|
||||
ap = await _get_augmented_part_by_identifier(db, identifier)
|
||||
if ap is None:
|
||||
raise HTTPException(status_code=404, detail="Augmented part not found")
|
||||
resolved_id = ap.id
|
||||
label = ap.sku or ap.description
|
||||
await augmented_part_crud.delete(db, ap)
|
||||
await audit_crud.log(
|
||||
@@ -133,10 +149,8 @@ async def delete_augmented_part(
|
||||
actor=actor,
|
||||
action="delete",
|
||||
resource_type="AugmentedPart",
|
||||
resource_id=augmented_part_id,
|
||||
description=audit_crud.describe_delete(
|
||||
"AugmentedPart", augmented_part_id, label
|
||||
),
|
||||
resource_id=resolved_id,
|
||||
description=audit_crud.describe_delete("AugmentedPart", resolved_id, label),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ async def create_part_colour(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
actor: User = Depends(require_permission("part_colours:write")),
|
||||
):
|
||||
if await part_colour_crud.get_by_name(db, payload.name):
|
||||
raise HTTPException(status_code=409, detail="Part colour already exists")
|
||||
pc = await part_colour_crud.create(db, name=payload.name)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
@@ -62,6 +64,10 @@ async def update_part_colour(
|
||||
if pc is None:
|
||||
raise HTTPException(status_code=404, detail="Part colour not found")
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
if "name" in changes:
|
||||
existing = await part_colour_crud.get_by_name(db, changes["name"])
|
||||
if existing is not None and existing.id != pc.id:
|
||||
raise HTTPException(status_code=409, detail="Part colour already exists")
|
||||
pc = await part_colour_crud.update(db, pc, changes)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
|
||||
@@ -27,6 +27,10 @@ async def create_part_type(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
actor: User = Depends(require_permission("part_types:write")),
|
||||
):
|
||||
if await part_type_crud.get_by_name(db, payload.name):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT, detail="Part type already exists"
|
||||
)
|
||||
pt = await part_type_crud.create(db, name=payload.name)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
@@ -66,6 +70,12 @@ async def update_part_type(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Part type not found"
|
||||
)
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
if "name" in changes:
|
||||
existing = await part_type_crud.get_by_name(db, changes["name"])
|
||||
if existing is not None and existing.id != pt.id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT, detail="Part type already exists"
|
||||
)
|
||||
pt = await part_type_crud.update(db, pt, changes)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
|
||||
@@ -81,6 +81,12 @@ async def update_role(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Role not found"
|
||||
)
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
if "name" in changes:
|
||||
existing = await role_crud.get_by_name(db, changes["name"])
|
||||
if existing is not None and existing.id != role.id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT, detail="Role already exists"
|
||||
)
|
||||
role = await role_crud.update(db, role, changes)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
|
||||
@@ -33,6 +33,8 @@ async def create_static_connection(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
actor: User = Depends(require_permission("static_connections:write")),
|
||||
):
|
||||
if await static_connection_crud.get_by_od_mm(db, payload.od_mm):
|
||||
raise HTTPException(status_code=409, detail="Static connection already exists")
|
||||
sc = await static_connection_crud.create(
|
||||
db, od_mm=payload.od_mm, od_in=payload.od_in
|
||||
)
|
||||
@@ -72,6 +74,12 @@ async def update_static_connection(
|
||||
if sc is None:
|
||||
raise HTTPException(status_code=404, detail="Static connection not found")
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
if "od_mm" in changes:
|
||||
existing = await static_connection_crud.get_by_od_mm(db, changes["od_mm"])
|
||||
if existing is not None and existing.id != sc.id:
|
||||
raise HTTPException(
|
||||
status_code=409, detail="Static connection already exists"
|
||||
)
|
||||
sc = await static_connection_crud.update(db, sc, changes)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
|
||||
@@ -31,6 +31,10 @@ async def create_sub_part_type(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
actor: User = Depends(require_permission("sub_part_types:write")),
|
||||
):
|
||||
if await sub_part_type_crud.get_by_name_and_part_type(
|
||||
db, payload.name, payload.part_type_id
|
||||
):
|
||||
raise HTTPException(status_code=409, detail="Sub part type already exists")
|
||||
spt = await sub_part_type_crud.create(
|
||||
db, name=payload.name, part_type_id=payload.part_type_id
|
||||
)
|
||||
@@ -72,6 +76,14 @@ async def update_sub_part_type(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Sub part type not found"
|
||||
)
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
if "name" in changes or "part_type_id" in changes:
|
||||
name = changes.get("name", spt.name)
|
||||
part_type_id = changes.get("part_type_id", spt.part_type_id)
|
||||
existing = await sub_part_type_crud.get_by_name_and_part_type(
|
||||
db, name, part_type_id
|
||||
)
|
||||
if existing is not None and existing.id != spt.id:
|
||||
raise HTTPException(status_code=409, detail="Sub part type already exists")
|
||||
spt = await sub_part_type_crud.update(db, spt, changes)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
|
||||
@@ -82,6 +82,12 @@ async def update_user(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="User not found"
|
||||
)
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
if "email" in changes:
|
||||
existing = await user_crud.get_by_email(db, changes["email"])
|
||||
if existing is not None and existing.id != target.id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT, detail="Email already registered"
|
||||
)
|
||||
target = await user_crud.update(db, target, changes)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
|
||||
Reference in new issue
Block a user