import { ICommerceService, PriceList, Payment, Shipment, ShipmentItem, Return, ReturnItem, } from '../contracts/commerce'; import { IRepository } from '../contracts/repository'; import { Order } from '../../entities/commerce/order'; import { Product, ProductCategory } from '../../entities/commerce/product'; import { MoneyAmount } from '../../entities/types'; import { BaseMockRepository } from './baseRepository'; import { ulid } from '../../shared/lib/ulid'; import { getServices } from './index'; export class MockCommerceService implements ICommerceService { products: IRepository; categories: IRepository; priceLists: IRepository; orders: IRepository; payments: IRepository; shipments: IRepository; returns: IRepository; constructor() { this.products = new BaseMockRepository('product'); this.categories = new BaseMockRepository('product_category'); this.priceLists = new BaseMockRepository('price_list'); this.orders = new BaseMockRepository('order'); this.payments = new BaseMockRepository('payment'); this.shipments = new BaseMockRepository('shipment'); this.returns = new BaseMockRepository('return'); } async updateOrderStatus(orderId: string, status: Order['status']): Promise { return this.orders.update(orderId, { status }); } async confirmOrder(orderId: string, warehouseId = 'wh_main'): Promise { const order = await this.orders.getById(orderId); if (!order) { throw new Error(`Заказ с ID ${orderId} не найден.`); } // Reserve stock in inventory const inventory = getServices().inventory; if (inventory && order.items) { for (const item of order.items) { await inventory.reserveStock(orderId, warehouseId, item.productId, item.quantity); } } return this.orders.update(orderId, { status: 'confirmed' }); } async cancelOrder(orderId: string): Promise { const order = await this.orders.getById(orderId); if (!order) { throw new Error(`Заказ с ID ${orderId} не найден.`); } // Release any active warehouse reservations const inventory = getServices().inventory; if (inventory) { await inventory.cancelOrderReservations(orderId); } return this.orders.update(orderId, { status: 'cancelled' }); } async createPayment( orderId: string, amount: MoneyAmount, method: Payment['method'], autoRecordFinance = false ): Promise { const order = await this.orders.getById(orderId); if (!order) { throw new Error(`Заказ с ID ${orderId} не найден.`); } const now = new Date().toISOString(); const payment = await this.payments.create({ orderId, orderNumber: order.orderNumber, amount, method, status: 'completed', paidAt: now, transactionId: `TXN-${ulid().slice(0, 10)}`, }); // Update order payment status await this.orders.update(orderId, { paymentStatus: 'paid' }); // Automatically record finance transaction if requested if (autoRecordFinance) { const finance = getServices().finance; if (finance) { await finance.createTransactionFromPayment(payment.id, orderId, amount); } } return payment; } async createShipment( orderId: string, warehouseId: string, items?: ShipmentItem[] ): Promise { const order = await this.orders.getById(orderId); if (!order) { throw new Error(`Заказ с ID ${orderId} не найден.`); } const shipmentItems: ShipmentItem[] = items || order.items.map((i) => ({ productId: i.productId, sku: i.sku, name: i.name, quantity: i.quantity, })); const shipment = await this.shipments.create({ orderId, orderNumber: order.orderNumber, warehouseId, status: 'ready', trackingNumber: `TRK-${ulid().slice(0, 8)}`, carrier: 'Krona Экспресс', dispatchedAt: new Date().toISOString(), items: shipmentItems, }); await this.orders.update(orderId, { fulfillmentStatus: 'fulfilled', status: 'shipped' }); // Deduct reserved stock and physical stock in inventory const inventory = getServices().inventory; if (inventory) { await inventory.fulfillOrderReservations(orderId, warehouseId, shipmentItems); } return shipment; } async processReturn( orderId: string, reason: string, items: ReturnItem[], warehouseId = 'wh_main', autoRecordFinance = false ): Promise { const order = await this.orders.getById(orderId); if (!order) { throw new Error(`Заказ с ID ${orderId} не найден.`); } const totalMinor = items.reduce((sum, item) => sum + item.refundAmount.minor, 0); const ret = await this.returns.create({ orderId, orderNumber: order.orderNumber, status: 'refunded', reason, totalRefund: { minor: totalMinor, currency: order.total.currency }, items, }); await this.orders.update(orderId, { fulfillmentStatus: 'returned', paymentStatus: 'refunded', }); // Restore physical stock in inventory const inventory = getServices().inventory; if (inventory) { for (const item of items) { await inventory.adjustStock( warehouseId, item.productId, item.quantity, `Возврат по заказу #${order.orderNumber}: ${reason}` ); } } // Automatically record refund transaction in finance if requested if (autoRecordFinance) { const finance = getServices().finance; if (finance) { await finance.createTransactionFromReturn(ret.id, orderId, ret.totalRefund); } } return ret; } }