import env from '#start/env' import logger from '@adonisjs/core/services/logger' import type Product from '#models/product' import type { SupplierPricePoint } from '#models/history' import axios from 'axios' interface ErpProductListParams { limit?: number skip?: number order?: string where?: { sku?: string warehouse?: string condition?: string } } export interface ErpProductItem { sku: string condition: string qty: number price: number costs?: Array<{ currency: string; price: number }> | null packageContain?: string | null type?: string | null erpId?: string | null warehouse?: string | null } export interface ErpProductPage { items: ErpProductItem[] /** Tổng số bản ghi ở ERP (dùng để phân trang vòng lặp sync) */ total: number skip: number limit: number } /** * Gọi ERP để lấy lịch sử giá supplier theo SKU/condition. */ export default class ErpService { static async getSupplierPricing(product: Product): Promise { const url = env.get('ERP_API_URL') if (!url) throw new Error('ERP_API_URL chưa cấu hình') const body = { urlAPI: '/api/wtbquote-result/get-result-list', filter: { limit: 50, skip: 0, order: 'updatedAt desc', where: { _q: product.sku, condition: product.condition }, }, } const resp = await axios.post(url + "/api/transferGetData", body, { headers: { Authorization: `Bearer ${env.get('ERP_API_KEY')}`, 'Content-Type': 'application/json', }, }) if (!resp.data) throw new Error(`ERP API lỗi: ${resp.status}`) const data: any = await resp.data logger.debug({ count: data?.data?.length }, 'ERP supplier pricing fetched') return (data?.data ?? []).map((r: any) => ({ price: Number(r.price), date: r.createdAt, source: 'erp', })) } /** * Gọi ERP để lấy danh sách sản phẩm tồn kho cho sync. */ static async getProductsForSync(params: ErpProductListParams = {}): Promise { const url = env.get('ERP_API_URL') if (!url) throw new Error('ERP_API_URL chưa cấu hình') const limit = params.limit ?? 20 const skip = params.skip ?? 0 try { const body = { urlAPI: '/api/products/instock', filter: { limit, skip, order: params.order ?? '', where: { sku: params.where?.sku ?? '', warehouse: params.where?.warehouse ?? '', condition: params.where?.condition ?? '', }, }, } const resp = await axios.post(url + "/api/transferGetData", body, { headers: { Authorization: `Bearer ${env.get('ERP_API_KEY')}`, }, }) if (!resp.data) throw new Error(`ERP API lỗi: ${resp.status}`) const data: any = await resp.data const raw = Array.isArray(data?.data) ? data.data : Array.isArray(data) ? data : [] const total = Number(data?.total ?? raw.length) logger.debug({ count: raw.length, total, skip }, 'ERP products fetched for sync') const items: ErpProductItem[] = raw.map((item: any) => { const sku = String(item?.sku ?? item?.SKU ?? item?.productSku ?? item?.product?.sku ?? '').trim() const condition = String( item?.condition ?? item?.Condition ?? item?.conditionName ?? item?.productCondition ?? '' ).trim() const qty = Number(item?.qty ?? item?.quantity ?? item?.stock ?? item?.availableQty ?? item?.inStockQty ?? 0) const price = Number(item?.price ?? item?.salePrice ?? item?.listPrice ?? item?.unitPrice ?? 0) const costs = Array.isArray(item?.costs) ? item.costs.map((entry: any) => ({ currency: String(entry?.currency ?? 'USD').trim(), price: Number(entry?.price ?? 0), })) : item?.cost != null || item?.purchasePrice != null || item?.costPrice != null ? [ { currency: 'USD', price: Number(item?.cost ?? item?.purchasePrice ?? item?.costPrice ?? 0), }, ] : null const packageContain = item?.packageContain ?? item?.package_contain ?? item?.packageQty ?? null const erpId = item?.productId ?? null const warehouse = item?.warehouse ?? 'AU' return { sku, condition, qty: Number.isFinite(qty) ? qty : 0, price: Number.isFinite(price) ? price : 0, costs, packageContain: packageContain == null ? null : String(packageContain), type: 'ERP', erpId: erpId ? String(erpId) : null, warehouse, } }) return { items, total: Number.isFinite(total) ? total : items.length, skip, limit } } catch (error) { logger.error({ err: error }, 'ERP getProductsForSync lỗi') // throw error return { items: [], total: 0, skip, limit } } } }