266 lines
8.2 KiB
TypeScript
266 lines
8.2 KiB
TypeScript
import { Request, Response } from 'express';
|
|
import db, { handlePrismaError } from '../../../../handlers/db.js'; // Database
|
|
import log from '../../../../handlers/log.js';
|
|
import { parseDynamicSortBy } from '../../../../helpers/prisma_helpers.js';
|
|
import { schema_get, schema_post, schema_patch, schema_del } from './transaction_schema.js';
|
|
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
|
|
async function get(req: Request, res: Response) {
|
|
const { error, value } = schema_get.validate(req.query);
|
|
if (error) {
|
|
log.api?.debug('GET transaction Error:', req.query, value, error.details[0].message);
|
|
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: error.details[0].message });
|
|
} else {
|
|
log.api?.debug('GET transaction Success:', req.query, value);
|
|
|
|
if (value.id !== undefined || value.user_id !== undefined) {
|
|
// get by id or user_id
|
|
await db
|
|
.$transaction([
|
|
// Same query for count and findMany
|
|
db.transactions.count({
|
|
where: {
|
|
OR: [{ id: value.id }, { userId: value.user_id }],
|
|
paid: value.paid
|
|
},
|
|
orderBy: parseDynamicSortBy(value.sort.toString(), value.order.toString()),
|
|
skip: value.skip,
|
|
take: value.take
|
|
}),
|
|
db.transactions.findMany({
|
|
where: {
|
|
OR: [{ id: value.id }, { userId: value.user_id }],
|
|
paid: value.paid
|
|
},
|
|
orderBy: parseDynamicSortBy(value.sort.toString(), value.order.toString()),
|
|
skip: value.skip,
|
|
take: value.take
|
|
})
|
|
])
|
|
.then(([count, result]) => {
|
|
if (result.length !== 0) {
|
|
res.status(200).json({ count, result });
|
|
} else {
|
|
res.status(404).json({ status: 'ERROR', errorcode: 'NOT_FOUND', message: 'Could not find specified transaction' });
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
handlePrismaError(err, res, 'GET transaction');
|
|
});
|
|
} else {
|
|
// get all
|
|
await db
|
|
.$transaction([
|
|
// Same query for count and findMany
|
|
db.transactions.count({
|
|
where: {
|
|
paid: value.paid
|
|
},
|
|
orderBy: parseDynamicSortBy(value.sort.toString(), value.order.toString()),
|
|
skip: value.skip,
|
|
take: value.take
|
|
}),
|
|
db.transactions.findMany({
|
|
where: {
|
|
paid: value.paid
|
|
},
|
|
orderBy: parseDynamicSortBy(value.sort.toString(), value.order.toString()),
|
|
skip: value.skip,
|
|
take: value.take
|
|
})
|
|
])
|
|
.then(([count, result]) => {
|
|
if (result.length !== 0) {
|
|
res.status(200).json({ count, result });
|
|
} else {
|
|
res.status(404).json({ status: 'ERROR', errorcode: 'NOT_FOUND', message: 'Could not find specified transaction' });
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
handlePrismaError(err, res, 'GET transaction');
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: CREATE transaction
|
|
async function post(req: Request, res: Response) {
|
|
const { error, value } = schema_post.validate(req.body);
|
|
if (error) {
|
|
log.api?.debug('POST transaction Error:', req.body, value, error.details[0].message);
|
|
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: error.details[0].message });
|
|
} else {
|
|
log.api?.debug('POST transaction Success:', req.body, value);
|
|
|
|
const products: Array<number> = value.products;
|
|
const outOfStockProducts: { id: number; name: string }[] = [];
|
|
const notFoundProducts: { id: number; name: string }[] = [];
|
|
const salesData: { productId: number; price: number }[] = [];
|
|
let total = new Prisma.Decimal(0);
|
|
|
|
// Start Prisma transaction
|
|
await db
|
|
.$transaction(async (prisma) => {
|
|
// Iterate over all products for this transaction(not prisma)
|
|
for (let i = 0; i < products.length; i++) {
|
|
log.api?.debug('Product:', i + 1, 'of', products.length, '(Loop)');
|
|
|
|
// Get product (price, stock, name)
|
|
const product = await prisma.products.findUnique({
|
|
where: { id: products[i] },
|
|
select: { price: true, stock: true, name: true }
|
|
});
|
|
|
|
// Check if product exists
|
|
if (product) {
|
|
log.api?.debug('Price:', product.price, '[Name:' + product.name + ']', '[ID:' + products[i] + ']');
|
|
|
|
if (product.stock > 0) {
|
|
// Add price of current product to total
|
|
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 {
|
|
// Product not found
|
|
notFoundProducts.push({ id: products[i], name: 'unknown' });
|
|
}
|
|
}
|
|
|
|
log.api?.debug('Total:', total.toFixed(2));
|
|
|
|
// Abort the Prisma transaction if there are not existing products
|
|
if (notFoundProducts.length > 0) {
|
|
log.api?.debug('Aborting. missing products:', notFoundProducts);
|
|
|
|
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({
|
|
data: {
|
|
userId: value.user_id,
|
|
total: total,
|
|
paid: false,
|
|
sales: {
|
|
create: salesData
|
|
}
|
|
},
|
|
select: {
|
|
id: true
|
|
}
|
|
});
|
|
|
|
// Everything went well
|
|
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');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// MARK: UPDATE transaction
|
|
async function patch(req: Request, res: Response) {
|
|
const { error, value } = schema_patch.validate(req.body);
|
|
if (error) {
|
|
log.api?.debug('PATCH transaction Error:', req.body, value, error.details[0].message);
|
|
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: error.details[0].message });
|
|
} else {
|
|
log.api?.debug('PATCH transaction Success:', req.body, value);
|
|
await db.transactions
|
|
.update({
|
|
where: {
|
|
id: value.id
|
|
},
|
|
data: {
|
|
userId: value.user_id,
|
|
paid: value.paid,
|
|
paidAt: value.paid ? new Date() : undefined
|
|
},
|
|
select: {
|
|
id: true
|
|
}
|
|
})
|
|
.then((result) => {
|
|
res.status(200).json({ status: 'UPDATED', message: 'Successfully updated transaction', id: result.id });
|
|
})
|
|
.catch((err) => {
|
|
handlePrismaError(err, res, 'PATCH transaction');
|
|
});
|
|
}
|
|
}
|
|
|
|
// MARK: DELETE transaction
|
|
async function del(req: Request, res: Response) {
|
|
const { error, value } = schema_del.validate(req.body);
|
|
if (error) {
|
|
log.api?.debug('DEL transaction Error:', req.body, value, error.details[0].message);
|
|
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: error.details[0].message });
|
|
} else {
|
|
log.api?.debug('DEL transaction Success:', req.body, value);
|
|
await db.transactions
|
|
.delete({
|
|
where: {
|
|
id: value.id
|
|
}
|
|
})
|
|
.then((result) => {
|
|
res.status(200).json({ status: 'DELETED', message: 'Successfully deleted transaction', id: result.id });
|
|
})
|
|
.catch((err) => {
|
|
handlePrismaError(err, res, 'DEL transaction');
|
|
});
|
|
}
|
|
}
|
|
|
|
export default { get, post, patch, del };
|