refactor(movements): normalize snapshot convention to mutate-then-write

This commit is contained in:
2026-06-25 03:11:15 +02:00
parent 575cd2d9a0
commit 95c52579d1
3 changed files with 52 additions and 40 deletions
+5 -5
View File
@@ -61,15 +61,15 @@ function toLegacyMovementType(type: InventoryMovementType) {
return type
}
function getStockSnapshot(currentStock: number, stockDelta: number) {
function getStockSnapshotFromAfter(afterStock: number, stockDelta: number) {
if (stockDelta < 0) {
return {
previousStock: currentStock + Math.abs(stockDelta),
newStock: currentStock,
previousStock: afterStock + Math.abs(stockDelta),
newStock: afterStock,
}
}
const previousStock = Math.max(currentStock - stockDelta, 0)
const previousStock = Math.max(afterStock - stockDelta, 0)
return {
previousStock,
@@ -219,7 +219,7 @@ export const MovementService = {
create: {
itemId,
stockDelta,
...getStockSnapshot(item.stock, stockDelta),
...getStockSnapshotFromAfter(item.stock, stockDelta),
},
}
: undefined,
+35 -35
View File
@@ -173,6 +173,10 @@ export async function createAssetUseCase(
)
: null
if (status === "AVAILABLE") {
await ItemService.updateStock(itemId, 1, tx)
}
await MovementService.create(
{
itemId,
@@ -186,10 +190,6 @@ export async function createAssetUseCase(
tx,
)
if (status === "AVAILABLE") {
await ItemService.updateStock(itemId, 1, tx)
}
return {
success: true,
assetId: newAsset.id,
@@ -290,6 +290,37 @@ export async function updateAssetUseCase(
tx,
)
const shouldIncrementNextItemStock =
transition.willBeAvailable &&
(!transition.wasAvailable || transition.itemChanged)
const shouldDecrementPreviousItemStock =
transition.wasAvailable &&
(!transition.willBeAvailable || transition.itemChanged)
if (shouldIncrementNextItemStock) {
await ItemService.updateStock(transition.nextItemId, 1, tx)
}
if (shouldDecrementPreviousItemStock) {
if (!transition.previousItemId) {
throw new AssetTransitionError({
itemId: ["Previous item not found for available asset"],
})
}
const stockWasDecremented = await ItemService.decrementStockIfAvailable(
transition.previousItemId,
1,
tx,
)
if (!stockWasDecremented) {
throw new AssetTransitionError({
stock: ["Item does not have enough stock"],
})
}
}
let closedActiveAssignment = false
if (transition.activeAssignment && !transition.willBeAssigned) {
@@ -325,13 +356,6 @@ export async function updateAssetUseCase(
closedActiveAssignment = true
}
const shouldIncrementNextItemStock =
transition.willBeAvailable &&
(!transition.wasAvailable || transition.itemChanged)
const shouldDecrementPreviousItemStock =
transition.wasAvailable &&
(!transition.willBeAvailable || transition.itemChanged)
if (
transition.statusChanged &&
!transition.hasPerson &&
@@ -422,30 +446,6 @@ export async function updateAssetUseCase(
)
}
if (shouldIncrementNextItemStock) {
await ItemService.updateStock(transition.nextItemId, 1, tx)
}
if (shouldDecrementPreviousItemStock) {
if (!transition.previousItemId) {
throw new AssetTransitionError({
itemId: ["Previous item not found for available asset"],
})
}
const stockWasDecremented = await ItemService.decrementStockIfAvailable(
transition.previousItemId,
1,
tx,
)
if (!stockWasDecremented) {
throw new AssetTransitionError({
stock: ["Item does not have enough stock"],
})
}
}
if (transition.willBeAssigned && transition.nextPersonId) {
const activeAssignment = transition.activeAssignment
@@ -80,6 +80,8 @@ describe("asset use-cases", () => {
expect(movements[0].stockLines[0]).toMatchObject({
itemId: item.id,
stockDelta: 1,
previousStock: 0,
newStock: 1,
})
expect(movements[0].assetLines[0]).toMatchObject({
assetId: result.assetId,
@@ -343,6 +345,12 @@ describe("asset use-cases", () => {
"ASSIGNMENT",
"RETURN",
])
expect(movements[0].stockLines[0]).toMatchObject({
itemId: item.id,
stockDelta: 1,
previousStock: 0,
newStock: 1,
})
expect(movements[1]).toMatchObject({
assignmentId: activeAssignment.id,
performedById: actor.id,
@@ -350,6 +358,8 @@ describe("asset use-cases", () => {
expect(movements[1].stockLines[0]).toMatchObject({
itemId: item.id,
stockDelta: -1,
previousStock: 1,
newStock: 0,
})
expect(movements[1].assetLines[0]).toMatchObject({
assetId: created.assetId,
@@ -361,6 +371,8 @@ describe("asset use-cases", () => {
expect(movements[2].stockLines[0]).toMatchObject({
itemId: item.id,
stockDelta: 1,
previousStock: 0,
newStock: 1,
})
expect(movements[2].assetLines[0]).toMatchObject({
assetId: created.assetId,