feat(assignments): support line-based returns and authenticated updates

This commit is contained in:
2026-06-19 17:14:22 +02:00
parent 965a04a468
commit c1763ed007
5 changed files with 427 additions and 52 deletions
+85 -32
View File
@@ -7,20 +7,37 @@ import { AssetService } from "@/services/asset.service"
import { AssignmentService } from "@/services/assignment.service"
import { ItemService } from "@/services/item.service"
import { MovementService } from "@/services/movement.service"
import type {
AssignmentStockLineInput,
AssignmentStockReturnInput,
} from "@/types"
type FieldErrors = Record<string, string[]>
type CreateAssignmentUseCaseInput = CreateAssignmentData & {
type CreateAssignmentUseCaseInput = Omit<
CreateAssignmentData,
"itemId" | "quantity"
> & {
actorId: string
itemId?: string
quantity?: number
lines?: AssignmentStockLineInput[]
}
type ReturnAssignmentUseCaseInput = {
id: string
actorId: string
returns?: AssignmentStockReturnInput[]
}
type UpdateAssignmentUseCaseInput = UpdateAssignmentData & {
type UpdateAssignmentUseCaseInput = Omit<
UpdateAssignmentData,
"itemId" | "quantity"
> & {
actorId: string
itemId?: string
quantity?: number
lines?: AssignmentStockLineInput[]
}
type CreateAssignmentUseCaseResult =
@@ -78,10 +95,45 @@ function updateAssignmentError(
}
}
function resolveAssignmentLine(
input:
| Pick<
CreateAssignmentUseCaseInput,
"itemId" | "quantity" | "notes" | "lines"
>
| Pick<
UpdateAssignmentUseCaseInput,
"itemId" | "quantity" | "notes" | "lines"
>,
): AssignmentStockLineInput | null {
const providedLine = input.lines?.[0]
if (providedLine) {
return providedLine
}
if (!input.itemId || typeof input.quantity !== "number") {
return null
}
return {
itemId: input.itemId,
quantity: input.quantity,
notes: input.notes,
}
}
export async function createAssignmentUseCase(
input: CreateAssignmentUseCaseInput,
): Promise<CreateAssignmentUseCaseResult> {
const { actorId, itemId, assetId, quantity, personId } = input
const { actorId, assetId, personId } = input
const line = resolveAssignmentLine(input)
if (!line || !personId || line.quantity <= 0) {
return createAssignmentError({ error: ["Invalid assignment data"] })
}
const { itemId, quantity, notes } = line
if (!itemId || !personId || quantity <= 0) {
return createAssignmentError({ error: ["Invalid assignment data"] })
@@ -142,7 +194,7 @@ export async function createAssignmentUseCase(
assetId: assetId || undefined,
quantity,
personId,
notes: input.notes,
notes: notes ?? input.notes,
assignmentDate: input.assignmentDate,
createdBy: actorId,
},
@@ -172,16 +224,12 @@ export async function createAssignmentUseCase(
export async function updateAssignmentUseCase(
input: UpdateAssignmentUseCaseInput,
): Promise<UpdateAssignmentUseCaseResult> {
const {
actorId,
id,
personId,
itemId,
assetId,
quantity,
notes,
assignmentDate,
} = input
const { actorId, id, personId, assetId } = input
const line = resolveAssignmentLine(input)
const itemId = line?.itemId ?? input.itemId
const quantity = line?.quantity ?? input.quantity ?? 0
const notes = line?.notes ?? input.notes
const assignmentDate = input.assignmentDate
if (!id) {
return updateAssignmentError({ id: ["Assignment ID is required"] })
@@ -282,7 +330,7 @@ export async function updateAssignmentUseCase(
export async function returnAssignmentUseCase(
input: ReturnAssignmentUseCaseInput,
): Promise<ReturnAssignmentUseCaseResult> {
const { id, actorId } = input
const { id, actorId, returns } = input
if (!id) {
return returnAssignmentError({ id: ["Assignment ID is required"] })
@@ -299,18 +347,35 @@ export async function returnAssignmentUseCase(
return returnAssignmentError({ id: ["Assignment already returned"] })
}
const assignmentWasReturned = await AssignmentService.markReturnedIfActive(
const returnResult = await AssignmentService.returnStockAssignment(
id,
actorId,
returns,
tx,
)
if (!assignmentWasReturned) {
return returnAssignmentError({ id: ["Assignment already returned"] })
if (!returnResult) {
return returnAssignmentError({ error: ["Invalid assignment data"] })
}
if (assignment.itemId && assignment.quantity) {
await ItemService.updateStock(assignment.itemId, assignment.quantity, tx)
for (const returnedLine of returnResult.returnedLines) {
await ItemService.updateStock(
returnedLine.itemId,
returnedLine.quantity,
tx,
)
await MovementService.create(
{
type: "RETURN",
quantity: returnedLine.quantity,
itemId: returnedLine.itemId,
assetId: assignment.assetId || undefined,
assignmentId: id,
userId: actorId,
},
tx,
)
}
if (assignment.assetId) {
@@ -323,18 +388,6 @@ export async function returnAssignmentUseCase(
)
}
await MovementService.create(
{
type: "RETURN",
quantity: assignment.quantity || 1,
itemId: assignment.itemId || undefined,
assetId: assignment.assetId || undefined,
assignmentId: id,
userId: actorId,
},
tx,
)
return {
success: true,
}