feat(assignments): support line-based returns and authenticated updates
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
|
||||
let prisma: PrismaClient
|
||||
let createAssignmentUseCase: typeof import("@/use-cases/assignment.use-cases").createAssignmentUseCase
|
||||
let updateAssignmentUseCase: typeof import("@/use-cases/assignment.use-cases").updateAssignmentUseCase
|
||||
let returnAssignmentUseCase: typeof import("@/use-cases/assignment.use-cases").returnAssignmentUseCase
|
||||
|
||||
beforeAll(async () => {
|
||||
@@ -23,6 +24,7 @@ beforeAll(async () => {
|
||||
|
||||
prisma = prismaModule.prisma
|
||||
createAssignmentUseCase = assignmentUseCases.createAssignmentUseCase
|
||||
updateAssignmentUseCase = assignmentUseCases.updateAssignmentUseCase
|
||||
returnAssignmentUseCase = assignmentUseCases.returnAssignmentUseCase
|
||||
})
|
||||
|
||||
@@ -45,11 +47,10 @@ describe("assignment use-cases", () => {
|
||||
|
||||
const result = await createAssignmentUseCase({
|
||||
actorId: actor.id,
|
||||
itemId: item.id,
|
||||
personId: person.id,
|
||||
quantity: 2,
|
||||
assignmentDate,
|
||||
notes: "Initial assignment",
|
||||
lines: [{ itemId: item.id, quantity: 2 }],
|
||||
})
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
@@ -89,6 +90,44 @@ describe("assignment use-cases", () => {
|
||||
expect(movements[0].stockLines[0]).toMatchObject({
|
||||
itemId: item.id,
|
||||
stockDelta: -2,
|
||||
previousStock: 5,
|
||||
newStock: 3,
|
||||
})
|
||||
})
|
||||
|
||||
it("updates an assignment from a quantity line DTO and keeps the edited line quantity", async () => {
|
||||
const actor = await createTestUser(prisma)
|
||||
const person = await createTestPerson(prisma)
|
||||
const item = await createTestItem(prisma, { stock: 5 })
|
||||
|
||||
const created = await createAssignmentUseCase({
|
||||
actorId: actor.id,
|
||||
personId: person.id,
|
||||
lines: [{ itemId: item.id, quantity: 2 }],
|
||||
})
|
||||
|
||||
expect(created.success).toBe(true)
|
||||
if (!created.success)
|
||||
throw new Error("Expected assignment creation success")
|
||||
|
||||
const updated = await updateAssignmentUseCase({
|
||||
actorId: actor.id,
|
||||
id: created.assignmentId,
|
||||
personId: person.id,
|
||||
lines: [{ itemId: item.id, quantity: 1 }],
|
||||
})
|
||||
|
||||
expect(updated.success).toBe(true)
|
||||
|
||||
const assignment = await prisma.assignment.findUniqueOrThrow({
|
||||
where: { id: created.assignmentId },
|
||||
include: { stockLines: true },
|
||||
})
|
||||
|
||||
expect(assignment.stockLines).toHaveLength(1)
|
||||
expect(assignment.stockLines[0]).toMatchObject({
|
||||
itemId: item.id,
|
||||
quantity: 1,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -111,11 +150,13 @@ describe("assignment use-cases", () => {
|
||||
},
|
||||
})
|
||||
|
||||
await expect(
|
||||
prisma.item.findUniqueOrThrow({ where: { id: item.id } }),
|
||||
).resolves.toMatchObject({ stock: 1 })
|
||||
await expect(prisma.assignment.count()).resolves.toBe(0)
|
||||
await expect(prisma.inventoryMovement.count()).resolves.toBe(0)
|
||||
expect(
|
||||
await prisma.item.findUniqueOrThrow({ where: { id: item.id } }),
|
||||
).toMatchObject({
|
||||
stock: 1,
|
||||
})
|
||||
expect(await prisma.assignment.count()).toBe(0)
|
||||
expect(await prisma.inventoryMovement.count()).toBe(0)
|
||||
})
|
||||
|
||||
it("returns an assignment, restores stock, closes it, and records a RETURN movement", async () => {
|
||||
@@ -145,7 +186,7 @@ describe("assignment use-cases", () => {
|
||||
prisma.item.findUniqueOrThrow({ where: { id: item.id } }),
|
||||
prisma.assignment.findUniqueOrThrow({
|
||||
where: { id: created.assignmentId },
|
||||
include: { stockLines: true },
|
||||
include: { stockLines: { include: { returns: true } } },
|
||||
}),
|
||||
prisma.inventoryMovement.findMany({
|
||||
include: { stockLines: true },
|
||||
@@ -158,12 +199,18 @@ describe("assignment use-cases", () => {
|
||||
expect(assignment).toMatchObject({
|
||||
personId: person.id,
|
||||
status: "RETURNED",
|
||||
closedById: actor.id,
|
||||
})
|
||||
expect(assignment.stockLines[0]).toMatchObject({
|
||||
itemId: item.id,
|
||||
quantity: 3,
|
||||
returnedQuantity: 3,
|
||||
})
|
||||
expect(assignment.stockLines[0].returns).toHaveLength(1)
|
||||
expect(assignment.stockLines[0].returns[0]).toMatchObject({
|
||||
quantity: 3,
|
||||
receivedById: actor.id,
|
||||
})
|
||||
expect(movements).toHaveLength(2)
|
||||
expect(movements[0]).toMatchObject({
|
||||
type: "ASSIGNMENT",
|
||||
@@ -173,6 +220,8 @@ describe("assignment use-cases", () => {
|
||||
expect(movements[0].stockLines[0]).toMatchObject({
|
||||
itemId: item.id,
|
||||
stockDelta: -3,
|
||||
previousStock: 4,
|
||||
newStock: 1,
|
||||
})
|
||||
expect(movements[1]).toMatchObject({
|
||||
type: "RETURN",
|
||||
@@ -182,6 +231,100 @@ describe("assignment use-cases", () => {
|
||||
expect(movements[1].stockLines[0]).toMatchObject({
|
||||
itemId: item.id,
|
||||
stockDelta: 3,
|
||||
previousStock: 1,
|
||||
newStock: 4,
|
||||
})
|
||||
})
|
||||
|
||||
it("records partial quantity returns before fully closing the assignment", async () => {
|
||||
const actor = await createTestUser(prisma)
|
||||
const person = await createTestPerson(prisma)
|
||||
const item = await createTestItem(prisma, { stock: 5 })
|
||||
|
||||
const created = await createAssignmentUseCase({
|
||||
actorId: actor.id,
|
||||
itemId: item.id,
|
||||
personId: person.id,
|
||||
quantity: 5,
|
||||
})
|
||||
|
||||
expect(created.success).toBe(true)
|
||||
if (!created.success)
|
||||
throw new Error("Expected assignment creation success")
|
||||
|
||||
const assignment = await prisma.assignment.findUniqueOrThrow({
|
||||
where: { id: created.assignmentId },
|
||||
include: { stockLines: true },
|
||||
})
|
||||
|
||||
const firstReturn = await returnAssignmentUseCase({
|
||||
id: created.assignmentId,
|
||||
actorId: actor.id,
|
||||
returns: [
|
||||
{
|
||||
assignmentLineId: assignment.stockLines[0].id,
|
||||
quantity: 2,
|
||||
notes: "First return batch",
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(firstReturn).toEqual({ success: true })
|
||||
|
||||
const partiallyReturned = await prisma.assignment.findUniqueOrThrow({
|
||||
where: { id: created.assignmentId },
|
||||
include: { stockLines: { include: { returns: true } } },
|
||||
})
|
||||
|
||||
expect(partiallyReturned).toMatchObject({
|
||||
status: "PARTIALLY_RETURNED",
|
||||
closedAt: null,
|
||||
closedById: null,
|
||||
})
|
||||
expect(partiallyReturned.stockLines[0]).toMatchObject({
|
||||
quantity: 5,
|
||||
returnedQuantity: 2,
|
||||
})
|
||||
expect(partiallyReturned.stockLines[0].returns).toHaveLength(1)
|
||||
expect(partiallyReturned.stockLines[0].returns[0]).toMatchObject({
|
||||
quantity: 2,
|
||||
receivedById: actor.id,
|
||||
notes: "First return batch",
|
||||
})
|
||||
|
||||
const secondReturn = await returnAssignmentUseCase({
|
||||
id: created.assignmentId,
|
||||
actorId: actor.id,
|
||||
returns: [
|
||||
{
|
||||
assignmentLineId: assignment.stockLines[0].id,
|
||||
quantity: 3,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
expect(secondReturn).toEqual({ success: true })
|
||||
|
||||
const fullyReturned = await prisma.assignment.findUniqueOrThrow({
|
||||
where: { id: created.assignmentId },
|
||||
include: { stockLines: { include: { returns: true } } },
|
||||
})
|
||||
|
||||
expect(fullyReturned).toMatchObject({
|
||||
status: "RETURNED",
|
||||
closedById: actor.id,
|
||||
})
|
||||
expect(fullyReturned.closedAt).toBeInstanceOf(Date)
|
||||
expect(fullyReturned.stockLines[0]).toMatchObject({
|
||||
quantity: 5,
|
||||
returnedQuantity: 5,
|
||||
})
|
||||
expect(fullyReturned.stockLines[0].returns).toHaveLength(2)
|
||||
|
||||
expect(
|
||||
await prisma.item.findUniqueOrThrow({ where: { id: item.id } }),
|
||||
).toMatchObject({
|
||||
stock: 5,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -212,6 +355,6 @@ describe("assignment use-cases", () => {
|
||||
errors: { id: ["Assignment already returned"] },
|
||||
})
|
||||
|
||||
await expect(prisma.inventoryMovement.count()).resolves.toBe(2)
|
||||
expect(await prisma.inventoryMovement.count()).toBe(2)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user