Implement AbortError class and enhance transaction handling with stock and existence checks
This commit is contained in:
parent
bf561f8c7f
commit
ef16f045f7
@ -58,3 +58,10 @@ export function handlePrismaError(errorObj: any, res: Response, source: string)
|
|||||||
export default prisma;
|
export default prisma;
|
||||||
|
|
||||||
//FIXME: https://www.prisma.io/docs/orm/prisma-client/special-fields-and-types/null-and-undefined
|
//FIXME: https://www.prisma.io/docs/orm/prisma-client/special-fields-and-types/null-and-undefined
|
||||||
|
|
||||||
|
|
||||||
|
// // Simulate a Prisma error for testing purposes
|
||||||
|
// throw new Prisma.PrismaClientKnownRequestError(
|
||||||
|
// 'Simulated Prisma error for testing',
|
||||||
|
// { code: 'P2000', clientVersion: 'unknown' } // Example error parameters
|
||||||
|
// );
|
||||||
|
@ -5,6 +5,13 @@ import { parseDynamicSortBy } from '../../../../helpers/prisma_helpers.js';
|
|||||||
import { schema_get, schema_post, schema_patch, schema_del } from './transaction_schema.js';
|
import { schema_get, schema_post, schema_patch, schema_del } from './transaction_schema.js';
|
||||||
import { Prisma } from '@prisma/client';
|
import { Prisma } from '@prisma/client';
|
||||||
|
|
||||||
|
class AbortError extends Error {
|
||||||
|
constructor(public http_status: number, public status: string, public errorcode: string, public message: string, public details?: any) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'AbortError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: GET transaction
|
// MARK: GET transaction
|
||||||
async function get(req: Request, res: Response) {
|
async function get(req: Request, res: Response) {
|
||||||
const { error, value } = schema_get.validate(req.query);
|
const { error, value } = schema_get.validate(req.query);
|
||||||
@ -94,38 +101,81 @@ async function post(req: Request, res: Response) {
|
|||||||
log.api?.debug('POST transaction Success:', req.body, value);
|
log.api?.debug('POST transaction Success:', req.body, value);
|
||||||
|
|
||||||
const products: Array<number> = value.products;
|
const products: Array<number> = value.products;
|
||||||
let total = new Prisma.Decimal(0);
|
const outOfStockProducts: { id: number; name: string }[] = [];
|
||||||
|
const notFoundProducts: { id: number; name: string }[] = [];
|
||||||
const salesData: { productId: number; price: number }[] = [];
|
const salesData: { productId: number; price: number }[] = [];
|
||||||
|
let total = new Prisma.Decimal(0);
|
||||||
|
|
||||||
try {
|
// Start Prisma transaction
|
||||||
// Start Prisma transaction
|
await db
|
||||||
await db.$transaction(async (prisma) => {
|
.$transaction(async (prisma) => {
|
||||||
|
// Iterate over all products for this transaction(not prisma)
|
||||||
for (let i = 0; i < products.length; i++) {
|
for (let i = 0; i < products.length; i++) {
|
||||||
log.api?.debug('Product:', products[i]);
|
log.api?.debug('Product:', i + 1, 'of', products.length, '(Loop)');
|
||||||
|
|
||||||
|
// Get product (price, stock, name)
|
||||||
const product = await prisma.products.findUnique({
|
const product = await prisma.products.findUnique({
|
||||||
where: { id: products[i] },
|
where: { id: products[i] },
|
||||||
select: { price: true }
|
select: { price: true, stock: true, name: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Check if product exists
|
||||||
if (product) {
|
if (product) {
|
||||||
log.api?.debug('Price:', product.price, Number(product.price));
|
log.api?.debug('Price:', product.price, '[Name:' + product.name + ']', '[ID:' + products[i] + ']');
|
||||||
//total += Number(product.price);
|
|
||||||
total = total.add(product.price);
|
|
||||||
|
|
||||||
salesData.push({
|
if (product.stock > 0) {
|
||||||
productId: products[i],
|
// Add price of current product to total
|
||||||
price: Number(product.price)
|
total = total.add(product.price);
|
||||||
});
|
|
||||||
|
// Add product to salesData -> Later generate sales entry for each product
|
||||||
|
salesData.push({
|
||||||
|
productId: products[i],
|
||||||
|
price: Number(product.price)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reduce stock by 1
|
||||||
|
await prisma.products.update({
|
||||||
|
where: { id: products[i] },
|
||||||
|
data: { stock: { decrement: 1 } }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Product is out of stock
|
||||||
|
outOfStockProducts.push({ id: products[i], name: product.name });
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.api?.debug('Product not found:', products[i]);
|
// Product not found
|
||||||
|
notFoundProducts.push({ id: products[i], name: 'unknown' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.api?.debug('Total:', total.toFixed(2));
|
log.api?.debug('Total:', total.toFixed(2));
|
||||||
|
|
||||||
// TODO: Check if user exists
|
// Abort the Prisma transaction if there are not existing products
|
||||||
|
if (notFoundProducts.length > 0) {
|
||||||
|
log.api?.debug('Aborting. missing products:', notFoundProducts);
|
||||||
|
|
||||||
// Create transaction with sales
|
throw new AbortError(
|
||||||
|
400, // http_status
|
||||||
|
'ERROR', // status
|
||||||
|
'NOT_FOUND', // errorcode
|
||||||
|
'Some of the products included in the transaction do not exist, therefore the transaction has not been processed.', // message
|
||||||
|
notFoundProducts // details
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abort the Prisma transaction if there are products with insufficient stock
|
||||||
|
if (outOfStockProducts.length > 0) {
|
||||||
|
log.api?.debug('Aborting. out of stock products:', outOfStockProducts);
|
||||||
|
throw new AbortError(
|
||||||
|
400, // http_status
|
||||||
|
'ERROR', // status
|
||||||
|
'OUT_OF_STOCK', // errorcode
|
||||||
|
'Some of the products included in the transaction are out of stock, therefore the transaction has not been processed.', // message
|
||||||
|
outOfStockProducts // details
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create transaction with salesData
|
||||||
const transaction = await prisma.transactions.create({
|
const transaction = await prisma.transactions.create({
|
||||||
data: {
|
data: {
|
||||||
userId: value.user_id,
|
userId: value.user_id,
|
||||||
@ -140,11 +190,21 @@ async function post(req: Request, res: Response) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Everything went well
|
||||||
res.status(201).json({ status: 'CREATED', message: 'Successfully created transaction', id: transaction.id });
|
res.status(201).json({ status: 'CREATED', message: 'Successfully created transaction', id: transaction.id });
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
if (err instanceof AbortError) {
|
||||||
|
res.status(err.http_status).json({
|
||||||
|
status: err.status,
|
||||||
|
errorcode: err.errorcode,
|
||||||
|
message: err.message,
|
||||||
|
details: err.details
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
handlePrismaError(err, res, 'POST transaction');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} catch (err) {
|
|
||||||
handlePrismaError(err, res, 'POST transaction');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user