refactor(assignments): move workflows into use cases
This commit is contained in:
@@ -0,0 +1,341 @@
|
||||
import prisma from "@/lib/prisma"
|
||||
import type {
|
||||
CreateAssignmentData,
|
||||
UpdateAssignmentData,
|
||||
} from "@/schemas/assignment.schema"
|
||||
import { AssetService } from "@/services/asset.service"
|
||||
import { AssignmentService } from "@/services/assignment.service"
|
||||
import { ItemService } from "@/services/item.service"
|
||||
import { MovementService } from "@/services/movement.service"
|
||||
|
||||
type FieldErrors = Record<string, string[]>
|
||||
|
||||
type CreateAssignmentUseCaseInput = CreateAssignmentData & {
|
||||
actorId: string
|
||||
}
|
||||
|
||||
type ReturnAssignmentUseCaseInput = {
|
||||
id: string
|
||||
actorId: string
|
||||
}
|
||||
|
||||
type UpdateAssignmentUseCaseInput = UpdateAssignmentData & {
|
||||
actorId: string
|
||||
}
|
||||
|
||||
type CreateAssignmentUseCaseResult =
|
||||
| {
|
||||
success: true
|
||||
assignmentId: string
|
||||
}
|
||||
| {
|
||||
success: false
|
||||
errors: FieldErrors
|
||||
}
|
||||
|
||||
type ReturnAssignmentUseCaseResult =
|
||||
| {
|
||||
success: true
|
||||
}
|
||||
| {
|
||||
success: false
|
||||
errors: FieldErrors
|
||||
}
|
||||
|
||||
type UpdateAssignmentUseCaseResult =
|
||||
| {
|
||||
success: true
|
||||
}
|
||||
| {
|
||||
success: false
|
||||
errors: FieldErrors
|
||||
}
|
||||
|
||||
function createAssignmentError(
|
||||
errors: FieldErrors,
|
||||
): CreateAssignmentUseCaseResult {
|
||||
return {
|
||||
success: false,
|
||||
errors,
|
||||
}
|
||||
}
|
||||
|
||||
function returnAssignmentError(
|
||||
errors: FieldErrors,
|
||||
): ReturnAssignmentUseCaseResult {
|
||||
return {
|
||||
success: false,
|
||||
errors,
|
||||
}
|
||||
}
|
||||
|
||||
function updateAssignmentError(
|
||||
errors: FieldErrors,
|
||||
): UpdateAssignmentUseCaseResult {
|
||||
return {
|
||||
success: false,
|
||||
errors,
|
||||
}
|
||||
}
|
||||
|
||||
export async function createAssignmentUseCase(
|
||||
input: CreateAssignmentUseCaseInput,
|
||||
): Promise<CreateAssignmentUseCaseResult> {
|
||||
const { actorId, itemId, assetId, quantity, recipientId } = input
|
||||
|
||||
if (!itemId || !recipientId || quantity <= 0) {
|
||||
return createAssignmentError({ error: ["Invalid assignment data"] })
|
||||
}
|
||||
|
||||
return prisma.$transaction(async (tx) => {
|
||||
const item = await ItemService.findById(itemId, tx)
|
||||
|
||||
if (!item) {
|
||||
return createAssignmentError({ itemId: ["Item not found"] })
|
||||
}
|
||||
|
||||
if (item.stock < quantity) {
|
||||
return createAssignmentError({
|
||||
quantity: ["Item does not have enough stock"],
|
||||
})
|
||||
}
|
||||
|
||||
if (assetId) {
|
||||
const asset = await AssetService.findById(assetId, tx)
|
||||
|
||||
if (!asset) {
|
||||
return createAssignmentError({ assetId: ["Asset not found"] })
|
||||
}
|
||||
|
||||
if (asset.itemId !== item.id) {
|
||||
return createAssignmentError({
|
||||
assetId: ["Asset does not belong to item"],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const stockWasDecremented = await ItemService.decrementStockIfAvailable(
|
||||
itemId,
|
||||
quantity,
|
||||
tx,
|
||||
)
|
||||
|
||||
if (!stockWasDecremented) {
|
||||
return createAssignmentError({
|
||||
quantity: ["Item does not have enough stock"],
|
||||
})
|
||||
}
|
||||
|
||||
if (assetId) {
|
||||
await AssetService.update(
|
||||
assetId,
|
||||
{
|
||||
status: "ASSIGNED",
|
||||
},
|
||||
tx,
|
||||
)
|
||||
}
|
||||
|
||||
const createdAssignment = await AssignmentService.create(
|
||||
{
|
||||
itemId,
|
||||
assetId: assetId || undefined,
|
||||
quantity,
|
||||
recipientId,
|
||||
notes: input.notes,
|
||||
assignmentDate: input.assignmentDate,
|
||||
createdBy: actorId,
|
||||
},
|
||||
tx,
|
||||
)
|
||||
|
||||
await MovementService.create(
|
||||
{
|
||||
itemId,
|
||||
assetId: assetId || undefined,
|
||||
quantity,
|
||||
type: "ASSIGNMENT",
|
||||
recipientId,
|
||||
assignmentId: createdAssignment.id,
|
||||
userId: actorId,
|
||||
},
|
||||
tx,
|
||||
)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
assignmentId: createdAssignment.id,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export async function updateAssignmentUseCase(
|
||||
input: UpdateAssignmentUseCaseInput,
|
||||
): Promise<UpdateAssignmentUseCaseResult> {
|
||||
const {
|
||||
actorId,
|
||||
id,
|
||||
recipientId,
|
||||
itemId,
|
||||
assetId,
|
||||
quantity,
|
||||
notes,
|
||||
assignmentDate,
|
||||
} = input
|
||||
|
||||
if (!id) {
|
||||
return updateAssignmentError({ id: ["Assignment ID is required"] })
|
||||
}
|
||||
|
||||
return prisma.$transaction(async (tx) => {
|
||||
const assignment = await AssignmentService.findById(id, tx)
|
||||
|
||||
if (!assignment) {
|
||||
return updateAssignmentError({ id: ["Assignment not found"] })
|
||||
}
|
||||
|
||||
if (itemId) {
|
||||
const item = await ItemService.findById(itemId, tx)
|
||||
|
||||
if (!item) {
|
||||
return updateAssignmentError({ itemId: ["Item not found"] })
|
||||
}
|
||||
}
|
||||
|
||||
if (assetId) {
|
||||
const asset = await AssetService.findById(assetId, tx)
|
||||
|
||||
if (!asset) {
|
||||
return updateAssignmentError({ assetId: ["Asset not found"] })
|
||||
}
|
||||
|
||||
if (asset.itemId !== itemId) {
|
||||
return updateAssignmentError({
|
||||
assetId: ["Asset does not belong to item"],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (assignment.recipientId !== recipientId) {
|
||||
await MovementService.create(
|
||||
{
|
||||
type: "RETURN",
|
||||
quantity: assignment.quantity || 1,
|
||||
itemId: assignment.itemId || undefined,
|
||||
assetId: assignment.assetId || undefined,
|
||||
recipientId: assignment.recipientId || undefined,
|
||||
assignmentId: id,
|
||||
userId: actorId,
|
||||
},
|
||||
tx,
|
||||
)
|
||||
|
||||
await MovementService.create(
|
||||
{
|
||||
type: "ASSIGNMENT",
|
||||
quantity,
|
||||
itemId,
|
||||
assetId: assetId || undefined,
|
||||
recipientId,
|
||||
assignmentId: id,
|
||||
userId: actorId,
|
||||
},
|
||||
tx,
|
||||
)
|
||||
|
||||
await AssignmentService.update(
|
||||
id,
|
||||
{
|
||||
createdBy: actorId,
|
||||
recipientId,
|
||||
itemId,
|
||||
assetId,
|
||||
quantity,
|
||||
notes,
|
||||
assignmentDate,
|
||||
returnDate: null,
|
||||
},
|
||||
tx,
|
||||
)
|
||||
} else {
|
||||
await AssignmentService.update(
|
||||
id,
|
||||
{
|
||||
recipientId,
|
||||
itemId,
|
||||
assetId,
|
||||
quantity,
|
||||
notes,
|
||||
assignmentDate,
|
||||
returnDate: null,
|
||||
},
|
||||
tx,
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export async function returnAssignmentUseCase(
|
||||
input: ReturnAssignmentUseCaseInput,
|
||||
): Promise<ReturnAssignmentUseCaseResult> {
|
||||
const { id, actorId } = input
|
||||
|
||||
if (!id) {
|
||||
return returnAssignmentError({ id: ["Assignment ID is required"] })
|
||||
}
|
||||
|
||||
return prisma.$transaction(async (tx) => {
|
||||
const assignment = await AssignmentService.findById(id, tx)
|
||||
|
||||
if (!assignment) {
|
||||
return returnAssignmentError({ id: ["Assignment not found"] })
|
||||
}
|
||||
|
||||
if (assignment.returnDate) {
|
||||
return returnAssignmentError({ id: ["Assignment already returned"] })
|
||||
}
|
||||
|
||||
const assignmentWasReturned = await AssignmentService.markReturnedIfActive(
|
||||
id,
|
||||
tx,
|
||||
)
|
||||
|
||||
if (!assignmentWasReturned) {
|
||||
return returnAssignmentError({ id: ["Assignment already returned"] })
|
||||
}
|
||||
|
||||
if (assignment.itemId && assignment.quantity) {
|
||||
await ItemService.updateStock(assignment.itemId, assignment.quantity, tx)
|
||||
}
|
||||
|
||||
if (assignment.assetId) {
|
||||
await AssetService.update(
|
||||
assignment.assetId,
|
||||
{
|
||||
status: "AVAILABLE",
|
||||
},
|
||||
tx,
|
||||
)
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user