diff --git a/src/docs/completed-routes.yaml b/src/docs/completed-routes.yaml index 7dbbfa9..07be920 100644 --- a/src/docs/completed-routes.yaml +++ b/src/docs/completed-routes.yaml @@ -154,7 +154,52 @@ components: id: { type: string, example: '1' } item_code: { type: string, example: 'ITM-00006', description: Auto-generated on create via ITEM document series; read-only } item_name: { type: string, example: 'LABSA' } - hsn_code_id: { type: integer, nullable: true, example: 1 } + item_category_id: { type: string, example: '38' } + item_subcategory_id: { type: string, nullable: true, example: '30' } + hsn_code_id: { type: string, nullable: true, example: '1' } + item_category: + type: object + nullable: true + properties: + id: { type: string, example: '38' } + code: { type: string, example: 'RAW' } + name: { type: string, example: 'Raw Material' } + item_subcategory: + type: object + nullable: true + properties: + id: { type: string, example: '30' } + code: { type: string, example: 'CHEM' } + name: { type: string, example: 'Chemicals' } + item_category_id: { type: string, example: '38' } + hsn_code: + type: object + nullable: true + properties: + id: { type: string, example: '1' } + code: { type: string, example: '34029099' } + description: { type: string, example: 'Organic surface-active agents' } + uom: + type: object + nullable: true + properties: + id: { type: string } + code: { type: string } + name: { type: string } + gst_rate: + type: object + nullable: true + properties: + id: { type: string } + rate_pct: { type: number } + description: { type: string, nullable: true } + brand: + type: object + nullable: true + properties: + id: { type: string } + code: { type: string } + name: { type: string } BrandsCreateBody: type: object required: [code, name, brand_type] diff --git a/src/modules/masters/items/items.service.js b/src/modules/masters/items/items.service.js index 91d73f0..e0bf70c 100644 --- a/src/modules/masters/items/items.service.js +++ b/src/modules/masters/items/items.service.js @@ -15,6 +15,50 @@ const SOFT_DELETE_TABLES = new Set([ 'brands', ]); +const itemInclude = { + item_categories: { select: { id: true, code: true, name: true } }, + item_subcategories: { select: { id: true, code: true, name: true, item_category_id: true } }, + hsn_codes: { select: { id: true, code: true, description: true } }, + uom: { select: { id: true, code: true, name: true } }, + gst_rates: { select: { id: true, rate_pct: true, description: true } }, + brands: { select: { id: true, code: true, name: true } }, +}; + +const toDecimal = (value) => + value !== null && value !== undefined ? Number(value) : value; + +const sanitizeItem = (row) => { + if (!row) return null; + const { + item_categories, + item_subcategories, + hsn_codes, + uom, + gst_rates, + brands, + ...rest + } = row; + + return { + ...rest, + min_order_qty: toDecimal(rest.min_order_qty), + reorder_level: toDecimal(rest.reorder_level), + item_category: item_categories || null, + item_subcategory: item_subcategories || null, + hsn_code: hsn_codes || null, + uom: uom || null, + gst_rate: gst_rates + ? { ...gst_rates, rate_pct: toDecimal(gst_rates.rate_pct) } + : null, + brand: brands || null, + item_categories: undefined, + item_subcategories: undefined, + hsn_codes: undefined, + gst_rates: undefined, + brands: undefined, + }; +}; + const normalizeString = (value) => { if (value === undefined || value === null) return value; const str = String(value).trim(); @@ -138,19 +182,22 @@ const createItems = async (payload, userId, requestId) => { if (data.is_asset_item === undefined) data.is_asset_item = false; if (data.is_active === undefined) data.is_active = true; - const created = await prisma.items.create({ data }); + const created = await prisma.items.create({ + data, + include: itemInclude, + }); await auditLog({ tableName: 'items', recordId: created.id, action: 'CREATE', oldValue: null, - newValue: created, + newValue: sanitizeItem(created), userId, requestId, }); - return created; + return sanitizeItem(created); }; const listItems = async (query) => { @@ -169,9 +216,10 @@ const listItems = async (query) => { : {}), }; - const [data, total] = await Promise.all([ + const [rows, total] = await Promise.all([ prisma.items.findMany({ where, + include: itemInclude, orderBy: { created_at: 'desc' }, skip, take: limit, @@ -179,15 +227,16 @@ const listItems = async (query) => { prisma.items.count({ where }), ]); - return { data, meta: { page, limit, total } }; + return { data: rows.map(sanitizeItem), meta: { page, limit, total } }; }; const getItemsById = async (id) => { const one = await prisma.items.findFirst({ where: { id: BigInt(id), deleted_at: null }, + include: itemInclude, }); if (!one) throw new ApiError(404, 'items not found'); - return one; + return sanitizeItem(one); }; const updateItems = async (id, payload, userId, requestId) => { @@ -204,6 +253,7 @@ const updateItems = async (id, payload, userId, requestId) => { const updated = await prisma.items.update({ where: { id: BigInt(id) }, data, + include: itemInclude, }); await auditLog({ @@ -211,12 +261,12 @@ const updateItems = async (id, payload, userId, requestId) => { recordId: id, action: 'UPDATE', oldValue: existing, - newValue: updated, + newValue: sanitizeItem(updated), userId, requestId, }); - return updated; + return sanitizeItem(updated); }; const deleteItems = async (id, userId, requestId) => {