feat(backend): implement pricing engine for loyalty points and 3-level promotions
This commit is contained in:
1 parent
36ea126993
commit
77c52e0534
4 files changed
+109
-7
No files matched your search
@@ -22,7 +22,25 @@ export const ordersRoute = new Elysia({ prefix: "/orders" })
|
||||
.post("/", async ({ body, set }) => {
|
||||
try {
|
||||
const data = body as any;
|
||||
|
||||
const settings = await prisma.storeSettings.findFirst()
|
||||
const earnRate = settings?.pointsEarnRate || 1.0
|
||||
const netAmount = data.totalAmount - data.shippingFee
|
||||
const pointsEarned = Math.floor(Math.max(0, netAmount) * earnRate)
|
||||
|
||||
if (data.customerId && data.pointsUsed && data.pointsUsed > 0) {
|
||||
// Verify customer has enough points
|
||||
const customer = await prisma.customer.findUnique({ where: { id: data.customerId } })
|
||||
if (!customer || customer.points < data.pointsUsed) {
|
||||
set.status = 400
|
||||
return { error: "Insufficient points" }
|
||||
}
|
||||
// Deduct points immediately
|
||||
await prisma.customer.update({
|
||||
where: { id: data.customerId },
|
||||
data: { points: { decrement: data.pointsUsed } }
|
||||
})
|
||||
}
|
||||
|
||||
const order = await prisma.order.create({
|
||||
data: {
|
||||
customerId: data.customerId,
|
||||
@@ -30,10 +48,18 @@ export const ordersRoute = new Elysia({ prefix: "/orders" })
|
||||
customerEmail: data.customerEmail,
|
||||
customerPhone: data.customerPhone,
|
||||
shippingAddress: data.shippingAddress,
|
||||
totalAmount: data.totalAmount,
|
||||
shippingFee: data.shippingFee,
|
||||
subtotal: data.subtotal || 0,
|
||||
promoLevel1Name: data.promoLevel1Name,
|
||||
promoLevel1Disc: data.promoLevel1Disc || 0,
|
||||
promoLevel2Name: data.promoLevel2Name,
|
||||
promoLevel2Disc: data.promoLevel2Disc || 0,
|
||||
couponCode: data.couponCode || null,
|
||||
discountAmount: data.discountAmount || 0,
|
||||
pointsUsed: data.pointsUsed || 0,
|
||||
pointsDiscount: data.pointsDiscount || 0,
|
||||
pointsEarned: pointsEarned,
|
||||
shippingFee: data.shippingFee,
|
||||
totalAmount: data.totalAmount,
|
||||
items: {
|
||||
create: data.items.map((item: any) => ({
|
||||
productId: item.productId,
|
||||
@@ -76,8 +102,15 @@ export const ordersRoute = new Elysia({ prefix: "/orders" })
|
||||
shippingAddress: t.String(),
|
||||
totalAmount: t.Number(),
|
||||
shippingFee: t.Number(),
|
||||
subtotal: t.Optional(t.Number()),
|
||||
promoLevel1Name: t.Optional(t.Nullable(t.String())),
|
||||
promoLevel1Disc: t.Optional(t.Number()),
|
||||
promoLevel2Name: t.Optional(t.Nullable(t.String())),
|
||||
promoLevel2Disc: t.Optional(t.Number()),
|
||||
couponCode: t.Optional(t.Nullable(t.String())),
|
||||
discountAmount: t.Optional(t.Number()),
|
||||
pointsUsed: t.Optional(t.Number()),
|
||||
pointsDiscount: t.Optional(t.Number()),
|
||||
items: t.Array(t.Object({
|
||||
productId: t.Number(),
|
||||
variantId: t.Optional(t.Number()),
|
||||
@@ -92,11 +125,31 @@ export const ordersRoute = new Elysia({ prefix: "/orders" })
|
||||
.patch("/:id/status", async ({ params: { id }, body, set }) => {
|
||||
try {
|
||||
const { status } = body as any;
|
||||
return await prisma.order.update({
|
||||
const order = await prisma.order.findUnique({ where: { id: Number(id) } });
|
||||
|
||||
const updatedOrder = await prisma.order.update({
|
||||
where: { id: Number(id) },
|
||||
data: { status },
|
||||
include: { items: true }
|
||||
});
|
||||
|
||||
// Grant points if status becomes PAID and it wasn't PAID before
|
||||
if (status === 'PAID' && order?.status !== 'PAID' && order?.customerId && order?.pointsEarned > 0) {
|
||||
await prisma.customer.update({
|
||||
where: { id: order.customerId },
|
||||
data: { points: { increment: order.pointsEarned } }
|
||||
});
|
||||
}
|
||||
|
||||
// If status becomes CANCELLED, refund the points used
|
||||
if (status === 'CANCELLED' && order?.status !== 'CANCELLED' && order?.customerId && order?.pointsUsed > 0) {
|
||||
await prisma.customer.update({
|
||||
where: { id: order.customerId },
|
||||
data: { points: { increment: order.pointsUsed } }
|
||||
});
|
||||
}
|
||||
|
||||
return updatedOrder;
|
||||
} catch (err) {
|
||||
set.status = 500;
|
||||
return { error: "Failed to update order status" };
|
||||
|
||||
Reference in new issue
Block a user