feat(inventory): support line-based assignments and movements
This commit is contained in:
@@ -4,23 +4,79 @@ import prisma from "@/lib/prisma"
|
||||
import type { CreateAssignmentData } from "@/schemas/assignment.schema"
|
||||
import type { Assignment, AssignmentWithPersonItemAsset } from "@/types"
|
||||
|
||||
type LegacyAssignmentWriteData = CreateAssignmentData & {
|
||||
createdBy?: string
|
||||
createdById?: string
|
||||
}
|
||||
|
||||
type LegacyAssignmentUpdateData = Partial<LegacyAssignmentWriteData> & {
|
||||
returnDate?: Date | null
|
||||
}
|
||||
|
||||
type AssignmentWithLines = Prisma.AssignmentGetPayload<{
|
||||
include: {
|
||||
person: true
|
||||
stockLines: { include: { item: true } }
|
||||
assetLines: { include: { asset: { include: { item: true } } } }
|
||||
}
|
||||
}>
|
||||
|
||||
const assignmentInclude = {
|
||||
person: true,
|
||||
stockLines: { include: { item: true } },
|
||||
assetLines: { include: { asset: { include: { item: true } } } },
|
||||
} satisfies Prisma.AssignmentInclude
|
||||
|
||||
function toLegacyAssignment(
|
||||
assignment: AssignmentWithLines,
|
||||
): AssignmentWithPersonItemAsset {
|
||||
const stockLine = assignment.stockLines[0]
|
||||
const assetLine = assignment.assetLines[0]
|
||||
const item = stockLine?.item ?? assetLine?.asset.item ?? null
|
||||
const asset = assetLine?.asset ?? null
|
||||
const quantity = stockLine?.quantity ?? (assetLine ? 1 : null)
|
||||
const returnDate = assignment.closedAt ?? assetLine?.returnedAt ?? null
|
||||
|
||||
return {
|
||||
...assignment,
|
||||
assignmentDate: assignment.assignedAt,
|
||||
returnDate,
|
||||
person: assignment.person,
|
||||
item,
|
||||
asset,
|
||||
itemId: item?.id ?? null,
|
||||
assetId: asset?.id ?? null,
|
||||
quantity,
|
||||
}
|
||||
}
|
||||
|
||||
function toLegacyAssignmentRecord(
|
||||
assignment: Prisma.AssignmentGetPayload<{}>,
|
||||
data: Pick<LegacyAssignmentUpdateData, "itemId" | "assetId" | "quantity">,
|
||||
): Assignment {
|
||||
return {
|
||||
...assignment,
|
||||
assignmentDate: assignment.assignedAt,
|
||||
returnDate: assignment.closedAt,
|
||||
itemId: data.itemId ?? null,
|
||||
assetId: data.assetId ?? null,
|
||||
quantity: data.quantity ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
export const AssignmentService = {
|
||||
findAllWithPerson: async (): Promise<AssignmentWithPersonItemAsset[]> => {
|
||||
return prisma.assignment.findMany({
|
||||
const assignments = await prisma.assignment.findMany({
|
||||
where: {
|
||||
returnDate: {
|
||||
equals: null,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
person: true,
|
||||
item: true,
|
||||
asset: true,
|
||||
status: { in: ["OPEN", "PARTIALLY_RETURNED"] },
|
||||
},
|
||||
include: assignmentInclude,
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
})
|
||||
|
||||
return assignments.map(toLegacyAssignment)
|
||||
},
|
||||
findAllWithPersonPaginated: async ({
|
||||
page,
|
||||
@@ -31,14 +87,12 @@ export const AssignmentService = {
|
||||
pageSize?: number
|
||||
search?: string
|
||||
}) => {
|
||||
return paginate<AssignmentWithPersonItemAsset>({
|
||||
const result = await paginate<AssignmentWithLines>({
|
||||
model: prisma.assignment,
|
||||
page,
|
||||
pageSize,
|
||||
where: {
|
||||
returnDate: {
|
||||
equals: null,
|
||||
},
|
||||
status: { in: ["OPEN", "PARTIALLY_RETURNED"] },
|
||||
...(search
|
||||
? {
|
||||
OR: [
|
||||
@@ -56,93 +110,213 @@ export const AssignmentService = {
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
include: {
|
||||
person: true,
|
||||
item: true,
|
||||
asset: true,
|
||||
},
|
||||
include: assignmentInclude,
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
...result,
|
||||
data: result.data.map(toLegacyAssignment),
|
||||
}
|
||||
},
|
||||
findById: async (
|
||||
id: string,
|
||||
db: Prisma.TransactionClient | typeof prisma = prisma,
|
||||
): Promise<AssignmentWithPersonItemAsset | null> => {
|
||||
return db.assignment.findUnique({
|
||||
const assignment = await db.assignment.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
person: true,
|
||||
item: true,
|
||||
asset: true,
|
||||
},
|
||||
include: assignmentInclude,
|
||||
})
|
||||
|
||||
return assignment ? toLegacyAssignment(assignment) : null
|
||||
},
|
||||
findAllByPerson: async (
|
||||
personId: string,
|
||||
): Promise<AssignmentWithPersonItemAsset[]> => {
|
||||
return prisma.assignment.findMany({
|
||||
where: { personId: personId },
|
||||
include: {
|
||||
person: true,
|
||||
item: true,
|
||||
asset: true,
|
||||
},
|
||||
const assignments = await prisma.assignment.findMany({
|
||||
where: { personId },
|
||||
include: assignmentInclude,
|
||||
})
|
||||
|
||||
return assignments.map(toLegacyAssignment)
|
||||
},
|
||||
create: async (
|
||||
data: CreateAssignmentData & { createdBy: string },
|
||||
data: LegacyAssignmentWriteData,
|
||||
db: Prisma.TransactionClient | typeof prisma = prisma,
|
||||
): Promise<Assignment> => {
|
||||
const { personId, ...rest } = data
|
||||
return db.assignment.create({
|
||||
const {
|
||||
personId,
|
||||
itemId,
|
||||
assetId,
|
||||
quantity,
|
||||
assignmentDate,
|
||||
createdBy,
|
||||
createdById,
|
||||
notes,
|
||||
} = data
|
||||
|
||||
const assignment = await db.assignment.create({
|
||||
data: {
|
||||
...rest,
|
||||
personId: personId,
|
||||
personId,
|
||||
notes,
|
||||
assignedAt: assignmentDate,
|
||||
createdById: createdById ?? createdBy ?? "",
|
||||
stockLines:
|
||||
!assetId && itemId
|
||||
? {
|
||||
create: {
|
||||
itemId,
|
||||
quantity,
|
||||
notes,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
assetLines: assetId
|
||||
? {
|
||||
create: {
|
||||
assetId,
|
||||
assignedAt: assignmentDate,
|
||||
notes,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
})
|
||||
|
||||
return toLegacyAssignmentRecord(assignment, { itemId, assetId, quantity })
|
||||
},
|
||||
delete: async (id: string): Promise<Assignment> => {
|
||||
return prisma.assignment.update({
|
||||
where: { id },
|
||||
data: {
|
||||
returnDate: new Date(),
|
||||
personId: null,
|
||||
quantity: null,
|
||||
assetId: null,
|
||||
itemId: null,
|
||||
},
|
||||
const closedAt = new Date()
|
||||
|
||||
return prisma.$transaction(async (tx) => {
|
||||
const assignmentWithAssetLines = await tx.assignment.findUniqueOrThrow({
|
||||
where: { id },
|
||||
include: { assetLines: { include: { asset: true } } },
|
||||
})
|
||||
|
||||
await Promise.all(
|
||||
assignmentWithAssetLines.assetLines
|
||||
.filter((line) => !line.returnedAt)
|
||||
.map((line) =>
|
||||
tx.assignmentAssetLine.update({
|
||||
where: { id: line.id },
|
||||
data: {
|
||||
returnedAt: closedAt,
|
||||
returnedById: assignmentWithAssetLines.createdById,
|
||||
returnStatus: line.asset.status,
|
||||
},
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
const assignment = await tx.assignment.update({
|
||||
where: { id },
|
||||
data: {
|
||||
status: "RETURNED",
|
||||
closedAt,
|
||||
},
|
||||
})
|
||||
|
||||
return toLegacyAssignmentRecord(assignment, {})
|
||||
})
|
||||
},
|
||||
markReturnedIfActive: async (
|
||||
id: string,
|
||||
actorId?: string,
|
||||
db: Prisma.TransactionClient | typeof prisma = prisma,
|
||||
): Promise<boolean> => {
|
||||
const result = await db.assignment.updateMany({
|
||||
const closedAt = new Date()
|
||||
const assignment = await db.assignment.findFirst({
|
||||
where: {
|
||||
id,
|
||||
returnDate: null,
|
||||
status: { in: ["OPEN", "PARTIALLY_RETURNED"] },
|
||||
},
|
||||
include: { stockLines: true, assetLines: { include: { asset: true } } },
|
||||
})
|
||||
|
||||
if (!assignment) return false
|
||||
|
||||
await Promise.all(
|
||||
assignment.stockLines.map((line) =>
|
||||
db.assignmentStockLine.update({
|
||||
where: { id: line.id },
|
||||
data: { returnedQuantity: line.quantity },
|
||||
}),
|
||||
),
|
||||
)
|
||||
await Promise.all(
|
||||
assignment.assetLines
|
||||
.filter((line) => !line.returnedAt)
|
||||
.map((line) =>
|
||||
db.assignmentAssetLine.update({
|
||||
where: { id: line.id },
|
||||
data: {
|
||||
returnedAt: closedAt,
|
||||
returnedById: actorId ?? assignment.createdById,
|
||||
returnStatus: line.asset.status,
|
||||
},
|
||||
}),
|
||||
),
|
||||
)
|
||||
await db.assignment.update({
|
||||
where: { id },
|
||||
data: {
|
||||
returnDate: new Date(),
|
||||
personId: null,
|
||||
quantity: null,
|
||||
assetId: null,
|
||||
itemId: null,
|
||||
status: "RETURNED",
|
||||
closedAt,
|
||||
closedById: actorId,
|
||||
},
|
||||
})
|
||||
|
||||
return result.count === 1
|
||||
return true
|
||||
},
|
||||
update: async (
|
||||
id: string,
|
||||
data: Prisma.AssignmentUpdateInput | Prisma.AssignmentUncheckedUpdateInput,
|
||||
data: LegacyAssignmentUpdateData,
|
||||
db: Prisma.TransactionClient | typeof prisma = prisma,
|
||||
): Promise<Assignment> => {
|
||||
return db.assignment.update({
|
||||
const {
|
||||
itemId,
|
||||
assetId,
|
||||
quantity,
|
||||
assignmentDate,
|
||||
returnDate,
|
||||
createdBy,
|
||||
createdById,
|
||||
...assignmentData
|
||||
} = data
|
||||
const assignment = await db.assignment.update({
|
||||
where: { id },
|
||||
data,
|
||||
data: {
|
||||
...assignmentData,
|
||||
assignedAt: assignmentDate,
|
||||
closedAt: returnDate,
|
||||
status: returnDate ? "RETURNED" : "OPEN",
|
||||
createdById: createdById ?? createdBy,
|
||||
},
|
||||
})
|
||||
|
||||
if (itemId || quantity) {
|
||||
await db.assignmentStockLine.updateMany({
|
||||
where: { assignmentId: id },
|
||||
data: {
|
||||
itemId,
|
||||
quantity,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (assetId || returnDate) {
|
||||
await db.assignmentAssetLine.updateMany({
|
||||
where: { assignmentId: id },
|
||||
data: {
|
||||
assetId,
|
||||
returnedAt: returnDate,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return toLegacyAssignmentRecord(assignment, { itemId, assetId, quantity })
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user