Files
Sathira Sri Sathara abb760cc19
CI / test (push) Successful in 10m23s
feat: Implement loyalty and wholesale services with models, routes, and validation
- Added models for loyalty points allocation, redemption, rewards, tiers, tier history, and referrals.
- Created business credit ledger entries, settlements, and settlement items models.
- Developed services for loyalty operations including account management, point allocation, redemption, and referral handling.
- Implemented wholesale credit management services for transactions and settlements.
- Established routes for loyalty and wholesale admin and customer operations with appropriate middleware for authentication and permission checks.
- Introduced validation schemas for loyalty and wholesale operations.
- Set up cron jobs for loyalty reconciliation tasks such as birthday rewards and point expirations.
- Created migration scripts to set up the database schema for loyalty and wholesale features.
- Added unit tests for validation and policy enforcement in loyalty and wholesale services.
2026-09-09 13:56:37 +05:30

2 lines
1.8 KiB
JavaScript

const crypto=require("crypto"),db=require("../../models"),credit=require("./credit.service"),inventory=require("../inventory/inventory.service"),payments=require("../commerce/payment.service");const fault=(m,c,s=400)=>Object.assign(new Error(m),{code:c,status:s});async function purchase({orderId,userId,eventId}){return db.sequelize.transaction(async transaction=>{const business=await db.BusinessCustomer.findOne({where:{user_id:userId,status:"ACTIVE"},transaction}),order=business&&await db.Order.findOne({where:{id:orderId,user_id:userId,business_customer_id:business.business_customer_id,status:"PENDING_PAYMENT"},include:[{model:db.OrderItem,as:"items"}],transaction,lock:transaction.LOCK.UPDATE});if(!order)throw fault("Eligible wholesale order not found","ORDER_NOT_CREDIT_ELIGIBLE",409);const result=await credit.post({businessCustomerId:business.business_customer_id,eventId,type:"CAPTURE",amount:String(order.grand_total),currency:order.currency,orderId:order.id,reference:order.order_number},transaction);let payment=await db.Payment.findOne({where:{order_id:order.id,provider:"INTERNAL_CREDIT"},transaction});if(!payment)payment=await db.Payment.create({id:crypto.randomUUID(),order_id:order.id,payment_reference:`CREDIT-${order.order_number}`,provider:"INTERNAL_CREDIT",method:"BUSINESS_CREDIT",status:"PAID",currency:order.currency,amount:order.grand_total,paid_at:new Date()},{transaction});if(!result.idempotent){for(const item of [...order.items].sort((a,b)=>a.variant_id.localeCompare(b.variant_id)))await inventory.consumeReservation({reservationKey:item.reservation_key,requestId:eventId,transaction});await order.update({status:"PAID",payment_status:"PAID"},{transaction});await payments.issueInvoice(order,transaction);}return{order,payment,credit:result,idempotent:result.idempotent};});}module.exports={purchase};