Skip to content

Commit

Permalink
feat: allow negative inventory (#195)
Browse files Browse the repository at this point in the history
Remove the NegativeBookQuantityError and allow for book quantity updates to be negative, thus allowing for negative inventory. If this is the case, the admin should use the InventoryAdjustment to correct the inventory after the sale (but in this way, we did not "lose" the sale).
  • Loading branch information
dylants authored Apr 29, 2024
1 parent 1503ef2 commit 80e9476
Show file tree
Hide file tree
Showing 6 changed files with 21 additions and 70 deletions.
12 changes: 1 addition & 11 deletions src/lib/actions/book.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import BookCreateInput from '@/types/BookCreateInput';
import BookHydrated from '@/types/BookHydrated';
import { Book, Prisma, ProductType } from '@prisma/client';
import { serializeBookSource } from '@/lib/serializers/book-source';
import NegativeBookQuantityError from '@/lib/errors/NegativeBookQuantityError';

export async function buildAuthorsInput(
tx: Prisma.TransactionClient,
Expand Down Expand Up @@ -172,9 +171,7 @@ export async function reduceBookUpdates(
}

/**
* Attempts to update the Book by the quantityChange.
*
* @throws NegativeBookQuantityError if quantity change results in negative quantity
* Updates the quantity for a Book by the quantityChange.
*/
export async function updateBookQuantity({
bookId,
Expand All @@ -198,13 +195,6 @@ export async function updateBookQuantity({
updatedQuantity,
);

if (updatedQuantity < 0) {
logger.error(
'Unable to process update, attempting to set a negative quantity',
);
throw new NegativeBookQuantityError(book);
}

await tx.book.update({
data: { quantity: updatedQuantity },
where: { id: bookId },
Expand Down
34 changes: 17 additions & 17 deletions src/lib/actions/order.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { Order, OrderState, ProductType } from '@prisma/client';
import { fakeOrder } from '@/lib/fakes/order';
import { fakeOrderItem, fakeOrderItemHydrated } from '@/lib/fakes/order-item';
import { fakeBook } from '@/lib/fakes/book';
import NegativeBookQuantityError from '@/lib/errors/NegativeBookQuantityError';
import BadRequestError from '@/lib/errors/BadRequestError';
import { buildPaginationRequest } from '@/lib/pagination';
import OrderWithItemsHydrated from '@/types/OrderWithItemsHydrated';
Expand Down Expand Up @@ -164,7 +163,7 @@ describe('order action', () => {
expect(prismaMock.book.update).toHaveBeenCalledTimes(1);
});

it('should throw NegativeBookQuantityError when attempting to move order with insufficient inventory', async () => {
it('should allow for negative inventory quantity', async () => {
prismaMock.$transaction.mockImplementation((cb) => cb(prismaMock));

const book1 = fakeBook();
Expand All @@ -182,21 +181,22 @@ describe('order action', () => {
prismaMock.order.findFirstOrThrow.mockResolvedValue(order);
prismaMock.order.update.mockResolvedValue(order);

expect.assertions(3);
try {
await moveOrderToPendingTransactionOrThrow({
orderUID: order.orderUID,
tx: prismaMock,
});
} catch (err) {
expect(err instanceof NegativeBookQuantityError).toBeTruthy();
const error: NegativeBookQuantityError =
err as NegativeBookQuantityError;
expect(error.book).toEqual(book1);
expect(error.message).toEqual(
'Attempting to set a negative quantity for Book',
);
}
await moveOrderToPendingTransactionOrThrow({
orderUID: order.orderUID,
tx: prismaMock,
});

expect(prismaMock.book.update).toHaveBeenCalledWith({
data: { quantity: -1 },
where: { id: item1.bookId },
});

expect(prismaMock.order.update).toHaveBeenCalledWith({
data: {
orderState: OrderState.PENDING_TRANSACTION,
},
where: { orderUID: order.orderUID },
});
});

it('should throw BadRequestError when attempting to move order not in OPEN state', async () => {
Expand Down
19 changes: 0 additions & 19 deletions src/lib/actions/transaction-safe.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import {
syncTransactionStatusSafe,
} from '@/lib/actions/transaction-safe';
import BadRequestError from '@/lib/errors/BadRequestError';
import NegativeBookQuantityError from '@/lib/errors/NegativeBookQuantityError';
import { fakeBook } from '@/lib/fakes/book';
import { fakeTransaction } from '@/lib/fakes/transaction';

const mockCancelTransaction = jest.fn();
Expand Down Expand Up @@ -50,23 +48,6 @@ describe('transaction safe actions', () => {
});
});

it('should return error when createTransaction throws NegativeBookQuantityError', async () => {
const book = fakeBook();
mockCreateTransaction.mockRejectedValue(
new NegativeBookQuantityError(book),
);

expect(await createTransactionSafe('1')).toEqual({
data: null,
error: {
book,
message: 'Attempting to set a negative quantity for Book',
name: NegativeBookQuantityError.name,
},
status: 400,
});
});

it('should return error when createTransaction throws Error', async () => {
mockCreateTransaction.mockRejectedValue(new Error('unrecognized error'));

Expand Down
5 changes: 1 addition & 4 deletions src/lib/actions/transaction-safe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,13 @@ import {
syncTransactionStatus,
} from '@/lib/actions/transaction';
import BadRequestError from '@/lib/errors/BadRequestError';
import NegativeBookQuantityError from '@/lib/errors/NegativeBookQuantityError';
import { safeActionWrapper } from '@/lib/safe-action-wrapper';
import { HttpResponse } from '@/types/HttpResponse';
import { Order, Transaction } from '@prisma/client';

export async function createTransactionSafe(
orderUID: Order['orderUID'],
): Promise<
HttpResponse<Transaction | null, BadRequestError | NegativeBookQuantityError>
> {
): Promise<HttpResponse<Transaction | null, BadRequestError>> {
return safeActionWrapper(createTransaction, orderUID);
}

Expand Down
11 changes: 0 additions & 11 deletions src/lib/errors/NegativeBookQuantityError.ts

This file was deleted.

10 changes: 2 additions & 8 deletions src/lib/safe-action-wrapper.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
'use server';

import BadRequestError from '@/lib/errors/BadRequestError';
import NegativeBookQuantityError from '@/lib/errors/NegativeBookQuantityError';
import logger from '@/lib/logger';
import { HttpResponse } from '@/types/HttpResponse';

export async function safeActionWrapper<Return>(
wrappedFunction: (...args: never[]) => Promise<Return>,
...args: unknown[]
): Promise<
HttpResponse<Return | null, BadRequestError | NegativeBookQuantityError>
> {
): Promise<HttpResponse<Return | null, BadRequestError>> {
try {
const data = await wrappedFunction(...(args as never[]));

Expand All @@ -19,10 +16,7 @@ export async function safeActionWrapper<Return>(
status: 200,
};
} catch (err: unknown) {
if (
err instanceof BadRequestError ||
err instanceof NegativeBookQuantityError
) {
if (err instanceof BadRequestError) {
return {
data: null,
error: {
Expand Down

0 comments on commit 80e9476

Please sign in to comment.