feat(cart): update CartItem model to use composite key and adjust related logic

fix(cart): ensure cart item updates and deletions are based on cartId and itemId

feat(buy): implement balance checks and updates during purchase process

fix(sellable): ensure cart items are deleted when a sellable is removed

feat(user): add conflict checks for shop creation to prevent ID collisions
This commit is contained in:
2026-03-26 20:33:11 +01:00
parent f588672894
commit 8b4eaee510
11 changed files with 119 additions and 31 deletions
+43 -2
View File
@@ -24,7 +24,10 @@ export async function POST(request: Request) {
// Fetch all cart items with related sellable and item to get shopId
const cartItemsWithShop = await db.cartItem.findMany({
where: { itemId: { in: cart.map((c) => c.id) } },
where: {
cartId: userId,
itemId: { in: cart.map((c) => c.id) },
},
include: {
sellable: {
include: {
@@ -49,10 +52,27 @@ export async function POST(request: Request) {
itemsByShop[shopId] ??= []; // initialize if undefined or null
itemsByShop[shopId].push({
id: ci.sellable.item.item_name, // API expects item_name as id
quantity: ci.quantity,
quantity: ci.quantity * ci.sellable.amount, // total quantity based on cart item quantity and sellable amount
});
}
// Check if user has enough balance for all items
const totalCost = cartItemsWithShop.reduce((sum, ci) => {
return sum + ci.quantity * ci.sellable.price;
}, 0);
const user = await db.user.findUnique({
where: { id: userId },
select: { balance: true },
});
if (!user || user.balance < totalCost) {
return NextResponse.json(
{ error: "Insufficient balance" },
{ status: 400 },
);
}
console.log(itemsByShop);
// Send requests per shop
@@ -79,6 +99,27 @@ export async function POST(request: Request) {
);
}
// Deduct the total cost from the user's balance
await db.user.update({
where: { id: userId },
data: { balance: { decrement: totalCost } },
});
// Add balance to shop owner
console.log("Updating shop owner balance for shop:", shopId);
const shop = await db.shop.findUnique({
where: { id: Number(shopId) },
select: { userId: true },
});
console.log("Shop owner userId:", shop?.userId);
console.log("Total cost to add to shop owner:", totalCost);
if (shop) {
await db.user.update({
where: { id: shop.userId },
data: { balance: { increment: totalCost } },
});
}
// Delete successfully sent items from cart
const itemIds = items.map((i) => i.id);
await db.cartItem.deleteMany({
+27 -10
View File
@@ -39,29 +39,46 @@ export async function PATCH(request: Request) {
});
// Check if the item already exists in the cart
const existingCartItem = await db.cartItem.findUnique({
where: { itemId },
const existingCartItem = await db.cartItem.findFirst({
where: {
cartId: userId,
itemId,
},
});
if (existingCartItem) {
if (quantity <= 0 && quantity * -1 >= existingCartItem.quantity) {
await db.cartItem.delete({
where: { itemId },
const nextQuantity = existingCartItem.quantity + quantity;
if (nextQuantity <= 0) {
await db.cartItem.deleteMany({
where: {
cartId: userId,
itemId,
},
});
} else {
// Update quantity
await db.cartItem.update({
where: { itemId },
data: { quantity: existingCartItem.quantity + quantity },
await db.cartItem.updateMany({
where: {
cartId: userId,
itemId,
},
data: { quantity: nextQuantity },
});
}
} else {
if (quantity < 0) {
return NextResponse.json(
{ error: "Cannot remove an item that is not in the cart" },
{ status: 400 },
);
}
// Add new item
await db.cartItem.create({
data: {
itemId,
quantity,
cartId: userId,
cartId: cart.userId,
},
});
}
+4
View File
@@ -97,6 +97,10 @@ export async function DELETE(request: Request) {
return NextResponse.json({ error: "Item not found" }, { status: 404 });
}
await db.cartItem.deleteMany({
where: { itemId: item.id },
});
await db.sellable.delete({
where: {
id: item.id,
+16
View File
@@ -141,6 +141,22 @@ export async function POST(request: Request) {
const shopsToCreate = body.shops.filter((s) => !currentShopIds.has(s.id));
if (shopsToCreate.length > 0) {
const conflictingShops = await db.shop.findMany({
where: {
id: { in: shopsToCreate.map((s) => s.id) },
NOT: { userId: session.user.id },
},
select: { id: true },
});
if (conflictingShops.length > 0) {
const conflictIds = conflictingShops.map((s) => s.id).join(", ");
return NextResponse.json(
{ error: `Shop ID(s) already claimed by another user: ${conflictIds}` },
{ status: 409 },
);
}
await db.shop.createMany({
data: shopsToCreate.map((s) => ({
id: s.id,