157 lines
5.0 KiB
TypeScript
157 lines
5.0 KiB
TypeScript
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'
|
|
import { convertCondition } from '#helpers/condition'
|
|
|
|
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<SupplierPricePoint[]> {
|
|
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: convertCondition(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<ErpProductPage> {
|
|
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 }
|
|
}
|
|
}
|
|
}
|