import { initContract } from '@ts-rest/core'; import { z } from 'zod'; import { shopSchema, updateShopSchema, shopProductSchema, createProductSchema, updateProductSchema, shopSaleSchema, shopCustomerSchema, createCustomerSchema, updateCustomerSchema, creditHistoryEventSchema, createCreditPaymentSchema, createSaleSchema, stockMovementSchema, storefrontOrderSchema, updateStorefrontOrderStatusBodySchema, updateStorefrontOrderStatusResponseSchema, sendCustomerReminderSchema, sendCustomerReminderResponseSchema, reEngageCustomerSchema, reEngageCustomerResponseSchema, } from '../schemas/shopkeeper.schemas'; import { errorResponseSchema, successResponseSchema, paginationQuerySchema, paginationResponseSchema, } from '../schemas/common.schemas'; const c = initContract(); export const shopkeeperContract = c.router({ getShop: { method: 'GET', path: '/shopkeeper/shop', responses: { 200: shopSchema, 401: errorResponseSchema }, summary: 'Get current shop details', }, updateShop: { method: 'PATCH', path: '/shopkeeper/shop', body: updateShopSchema, responses: { 200: shopSchema, 400: errorResponseSchema, 401: errorResponseSchema, 409: errorResponseSchema, }, summary: 'Update shop details', }, listProducts: { method: 'GET', path: '/shopkeeper/products', query: z .object({ active: z.coerce.boolean().optional(), category: z.string().optional(), search: z.string().optional(), }) .merge(paginationQuerySchema), responses: { 200: z.object({ products: z.array(shopProductSchema), pagination: paginationResponseSchema }), 401: errorResponseSchema, }, summary: 'List shop products', }, getProduct: { method: 'GET', path: '/shopkeeper/products/:id', pathParams: z.object({ id: z.string().uuid() }), responses: { 200: shopProductSchema, 404: errorResponseSchema, 401: errorResponseSchema }, summary: 'Get product by ID', }, createProduct: { method: 'POST', path: '/shopkeeper/products', body: createProductSchema, responses: { 201: shopProductSchema, 400: errorResponseSchema, 401: errorResponseSchema }, summary: 'Create a new product', }, updateProduct: { method: 'PATCH', path: '/shopkeeper/products/:id', pathParams: z.object({ id: z.string().uuid() }), body: updateProductSchema, responses: { 200: shopProductSchema, 404: errorResponseSchema, 401: errorResponseSchema }, summary: 'Update a product', }, deleteProduct: { method: 'DELETE', path: '/shopkeeper/products/:id', pathParams: z.object({ id: z.string().uuid() }), body: z.object({}), responses: { 200: successResponseSchema, 404: errorResponseSchema, 401: errorResponseSchema }, summary: 'Delete a product (soft delete)', }, uploadProductIcon: { method: 'POST', path: '/shopkeeper/products/:id/icon', pathParams: z.object({ id: z.string().uuid() }), contentType: 'multipart/form-data', body: z.object({ file: z.any() }), responses: { 200: z.object({ iconUrl: z.string() }), 400: errorResponseSchema, 401: errorResponseSchema, }, summary: 'Upload product icon photo', }, listSales: { method: 'GET', path: '/shopkeeper/sales', query: z .object({ from: z.coerce.date().optional(), to: z.coerce.date().optional(), productId: z.string().uuid().optional(), inputMethod: z.string().optional(), paymentMethod: z.string().optional(), staffProfileId: z.string().uuid().optional(), }) .merge(paginationQuerySchema), responses: { 200: z.object({ sales: z.array(shopSaleSchema), pagination: paginationResponseSchema, summary: z.object({ totalRevenue: z.number(), totalTransactions: z.number() }), }), 401: errorResponseSchema, }, summary: 'List sales with filters', }, createSale: { method: 'POST', path: '/shopkeeper/sales', body: createSaleSchema, responses: { 201: shopSaleSchema, 400: errorResponseSchema, 401: errorResponseSchema, 404: errorResponseSchema, }, summary: 'Record a manual sale and update stock', }, listCustomers: { method: 'GET', path: '/shopkeeper/customers', query: z .object({ search: z.string().optional(), hasCredit: z.coerce.boolean().optional(), }) .merge(paginationQuerySchema), responses: { 200: z.object({ customers: z.array(shopCustomerSchema), pagination: paginationResponseSchema }), 401: errorResponseSchema, }, summary: 'List shop customers', }, createCustomer: { method: 'POST', path: '/shopkeeper/customers', body: createCustomerSchema, responses: { 201: shopCustomerSchema, 400: errorResponseSchema, 401: errorResponseSchema }, summary: 'Create a new customer', }, getCustomer: { method: 'GET', path: '/shopkeeper/customers/:id', pathParams: z.object({ id: z.string().uuid() }), responses: { 200: shopCustomerSchema, 401: errorResponseSchema, 403: errorResponseSchema, 404: errorResponseSchema, }, summary: 'Get customer by ID', }, updateCustomer: { method: 'PATCH', path: '/shopkeeper/customers/:id', pathParams: z.object({ id: z.string().uuid() }), body: updateCustomerSchema, responses: { 200: shopCustomerSchema, 400: errorResponseSchema, 401: errorResponseSchema, 403: errorResponseSchema, 404: errorResponseSchema, }, summary: 'Update customer name or phone', }, getCustomerCreditHistory: { method: 'GET', path: '/shopkeeper/customers/:id/credit-history', pathParams: z.object({ id: z.string().uuid() }), query: paginationQuerySchema, responses: { 200: z.object({ events: z.array(creditHistoryEventSchema), pagination: paginationResponseSchema, }), 401: errorResponseSchema, 403: errorResponseSchema, 404: errorResponseSchema, }, summary: 'List credit sales and repayments for a customer', }, createCreditPayment: { method: 'POST', path: '/shopkeeper/customers/:id/credit-payments', pathParams: z.object({ id: z.string().uuid() }), body: createCreditPaymentSchema, responses: { 200: shopCustomerSchema, 400: errorResponseSchema, 401: errorResponseSchema, 403: errorResponseSchema, 404: errorResponseSchema, }, summary: 'Record a credit repayment and decrement balance', }, sendCustomerReminder: { method: 'POST', path: '/shopkeeper/customers/:id/send-reminder', pathParams: z.object({ id: z.string().uuid() }), body: sendCustomerReminderSchema, responses: { 200: sendCustomerReminderResponseSchema, 400: errorResponseSchema, 401: errorResponseSchema, 403: errorResponseSchema, 404: errorResponseSchema, 409: errorResponseSchema, 429: errorResponseSchema, }, summary: 'Send debt reminder to customer via WhatsApp or SMS', }, reEngageCustomer: { method: 'POST', path: '/shopkeeper/customers/:id/re-engage', pathParams: z.object({ id: z.string().uuid() }), body: reEngageCustomerSchema, responses: { 200: reEngageCustomerResponseSchema, 400: errorResponseSchema, 401: errorResponseSchema, 403: errorResponseSchema, 404: errorResponseSchema, 409: errorResponseSchema, 429: errorResponseSchema, }, summary: 'Send owner-approved WhatsApp re-engagement to at-risk customer', }, listStockMovements: { method: 'GET', path: '/shopkeeper/stock-movements', query: z .object({ productId: z.string().uuid().optional(), type: z.string().optional(), from: z.coerce.date().optional(), to: z.coerce.date().optional(), }) .merge(paginationQuerySchema), responses: { 200: z.object({ movements: z.array(stockMovementSchema), pagination: paginationResponseSchema, }), 401: errorResponseSchema, }, summary: 'List stock movements', }, listStorefrontOrders: { method: 'GET', path: '/shopkeeper/storefront/orders', query: z.object({ status: z .enum([ 'all', 'awaiting_payment', 'awaiting_approval', 'paid', 'reserved', 'ready', 'collected', 'cancelled', ]) .default('awaiting_approval'), limit: z.coerce.number().int().min(1).max(100).default(30), }), responses: { 200: z.object({ orders: z.array(storefrontOrderSchema) }), 401: errorResponseSchema, }, summary: 'List storefront online orders with status filters', }, updateStorefrontOrderStatus: { method: 'PATCH', path: '/shopkeeper/storefront/orders/:id/status', pathParams: z.object({ id: z.string().uuid() }), body: updateStorefrontOrderStatusBodySchema, responses: { 200: updateStorefrontOrderStatusResponseSchema, 400: errorResponseSchema, 401: errorResponseSchema, 404: errorResponseSchema, 409: errorResponseSchema, }, summary: 'Advance storefront order status (approve, ready, collected, cancel)', }, approveStorefrontManualPayment: { method: 'POST', path: '/shopkeeper/storefront/orders/:id/approve-manual-payment', pathParams: z.object({ id: z.string().uuid() }), body: z.object({}), responses: { 200: z.object({ id: z.string().uuid(), status: z.string(), }), 400: errorResponseSchema, 401: errorResponseSchema, 404: errorResponseSchema, 409: errorResponseSchema, }, summary: 'Approve storefront manual transfer order and record sale', }, });