This commit is contained in:
hero920103 committed 2026-10-19 01:13:17 +08:00
1 parent 5718a00e31
commit 1850ac640e
226 files changed
+37772 -12071

No files matched your search

+784
View File
@@ -0,0 +1,784 @@
import { request } from '@/api/request.js'
/**
* 进销存移动端接口封装,对应后端 /api/erp/*(路由表 go/routers/api/erp.go)。
* 与 Web 后台 /backend/erp/* 共用同一份统计口径:报表只认「已生效」单据与库存流水,
* 所以手机端看到的金额、毛利、预警数与 Web 端永远一致。
* 商品/仓库/客商在本端只读,建档一律回 Web 端完成。
*/
/** 单据状态机:草稿 →(提交审批) 审批中 →(引擎通过) 已生效;驳回/作废为终态 */
export const DOC_STATUS = {
DRAFT: 0,
APPROVING: 1,
APPROVED: 2,
REJECTED: 3,
CANCELLED: 4
}
export const DOC_STATUS_OPTIONS = [
{ value: DOC_STATUS.DRAFT, label: '草稿', type: 'info' },
{ value: DOC_STATUS.APPROVING, label: '审批中', type: 'primary' },
{ value: DOC_STATUS.APPROVED, label: '已生效', type: 'success' },
{ value: DOC_STATUS.REJECTED, label: '已驳回', type: 'error' },
{ value: DOC_STATUS.CANCELLED, label: '已作废', type: 'warning' }
]
/** 往来账核销状态 */
export const BILL_STATUS_OPTIONS = [
{ value: 0, label: '未核销', type: 'warning' },
{ value: 1, label: '部分核销', type: 'primary' },
{ value: 2, label: '已结清', type: 'success' }
]
/** 收付款方式:1收款 2付款 3其他收入 4费用支出 */
export const FUND_TYPES = [
{ value: 1, label: '收款', color: 'success' },
{ value: 2, label: '付款', color: 'error' },
{ value: 3, label: '其他收入', color: 'primary' },
{ value: 4, label: '费用支出', color: 'warning' }
]
/** 收款/付款单的结算方式(与 Web 端 payment.vue methodOptions 同一套值) */
export const FUND_METHODS = [
{ value: 'cash', label: '现金' },
{ value: 'bank', label: '银行转账' },
{ value: 'wechat', label: '微信' },
{ value: 'alipay', label: '支付宝' }
]
/** 收付款单状态:仅草稿可改,确认后驱动核销,作废会回滚已核销余额 */
export const FUND_STATUS_OPTIONS = [
{ value: 0, label: '草稿', type: 'info' },
{ value: 1, label: '已确认', type: 'success' },
{ value: 4, label: '已作废', type: 'warning' }
]
/** 往来账核销状态(列表筛选值,与后端 settle_state 参数一致) */
export const SETTLE_STATES = [
{ value: 'open', label: '未结清' },
{ value: 'part', label: '部分核销' },
{ value: 'done', label: '已结清' }
]
/** 单据类型 → 中文名(列表/流水里只有 doc_type 字符串时兜底用) */
export const DOC_TYPE_LABELS = {
purchase_order: '采购订单',
purchase_in: '采购入库',
purchase_return: '采购退货',
sales_order: '销售订单',
sales_out: '销售出库',
sales_return: '销售退货',
other_in: '其他入库',
other_out: '其他出库',
transfer: '库存调拨',
stock_take: '库存盘点'
}
export function docTypeLabel(type) {
return DOC_TYPE_LABELS[type] || type
}
/** 往来账类型 */
export const BILL_TYPES = [
{ value: 1, label: '应收', color: 'error' },
{ value: 2, label: '应付', color: 'success' }
]
export const PARTNER_TYPES = [
{ value: 'supplier', label: '供应商' },
{ value: 'customer', label: '客户' }
]
/** 库存预警状态(后端 safety_state 三值) */
export const SAFETY_STATE_OPTIONS = [
{ value: '', label: '全部' },
{ value: 'low', label: '低于安全库存', type: 'warning' },
{ value: 'over', label: '库存积压', type: 'primary' },
{ value: 'normal', label: '正常', type: 'success' }
]
/** 批次效期状态 */
export const EXPIRE_STATE_OPTIONS = [
{ value: '', label: '全部' },
{ value: 'expired', label: '已过期', type: 'error' },
{ value: 'expiring', label: '30天内到期', type: 'warning' },
{ value: 'normal', label: '正常', type: 'success' }
]
/** 购销汇总维度 */
export const REPORT_DIMENSIONS = [
{ value: 'product', label: '按商品' },
{ value: 'supplier', label: '按供应商' },
{ value: 'customer', label: '按客户' },
{ value: 'month', label: '按月' }
]
const LABEL_FALLBACK = { value: '未知' }
export function findOption(options, value) {
return options.find(item => String(item.value) === String(value ?? '')) || LABEL_FALLBACK
}
export function docStatusLabel(status) {
return findOption(DOC_STATUS_OPTIONS, status).label || status
}
export function docStatusType(status) {
return findOption(DOC_STATUS_OPTIONS, status).type || 'info'
}
export function billStatusLabel(status) {
return findOption(BILL_STATUS_OPTIONS, status).label
}
export function billStatusType(status) {
return findOption(BILL_STATUS_OPTIONS, status).type || 'info'
}
export function fundMethodLabel(method) {
return findOption(FUND_METHODS, method).label || method
}
export function fundStatusLabel(status) {
return findOption(FUND_STATUS_OPTIONS, status).label
}
export function fundStatusType(status) {
return findOption(FUND_STATUS_OPTIONS, status).type || 'info'
}
export function partnerTypeLabel(type) {
return findOption(PARTNER_TYPES, type).label
}
export function safetyStateLabel(state) {
return findOption(SAFETY_STATE_OPTIONS, state).label || state
}
/** 金额展示:千分位可选,统一两位小数 */
export function moneyText(value) {
const num = Number(value || 0)
return num.toFixed(2)
}
/** 数量展示:去掉浮点尾巴,最多四位小数 */
export function qtyText(value) {
const num = Number(value || 0)
return String(Math.round(num * 10000) / 10000)
}
/** ISO 时间串取日期部分 */
export function dayText(value) {
return String(value || '').slice(0, 10)
}
export function dateTimeText(value) {
const s = String(value || '')
return s ? `${s.slice(0, 10)} ${s.slice(11, 16)}` : ''
}
/** 分页参数归一:后端 pageSize 上限 100,越界回落 20 */
function pageArgs(params) {
return {
page: params.page || 1,
pageSize: params.pageSize || 20
}
}
/** 日期区间参数:空值不传,避免覆盖后端默认口径 */
function rangeArgs(params) {
const out = {}
if (params.dateFrom) out.date_from = params.dateFrom
if (params.dateTo) out.date_to = params.dateTo
return out
}
// ----------------------------------------------------------------- 元数据 / 看板
/**
* 开单字典:单据类型(含往来口径与是否动库存)、状态档、默认仓库、价格体系、负库存开关
*/
export function fetchErpMeta() {
return request({ url: '/api/erp/meta' })
}
/** 单据类型字典(列表筛选、开单选择都用它,避免端上硬编码十种类型) */
export async function fetchDocTypes() {
const meta = await fetchErpMeta()
return meta.doc_types || []
}
/**
* 经营看板:本月购销/毛利、今日销售、库存价值、往来余额、待办、近 6 月趋势、预警计数
*/
export async function fetchErpDashboard() {
const data = await request({ url: '/api/erp/dashboard' })
return {
month: data.month || {},
today: data.today || {},
stock: data.stock || {},
partnerBill: data.partner_bill || {},
todo: data.todo || {},
trend: data.trend || [],
warning: data.warning || {}
}
}
// ----------------------------------------------------------------- 单据
/**
* 单据列表
* params: { docType | docTypes[], status, keyword, partnerId, warehouseId, sourceDocId,
* dateFrom, dateTo, page, pageSize }
*/
export async function fetchDocs(params = {}) {
const data = await request({
url: '/api/erp/document/list',
data: {
...pageArgs(params),
doc_type: params.docType || '',
doc_types: Array.isArray(params.docTypes) ? params.docTypes.join(',') : (params.docTypes || ''),
status: params.status ?? '',
keyword: params.keyword || '',
partner_id: params.partnerId || '',
warehouse_id: params.warehouseId || '',
source_doc_id: params.sourceDocId || '',
...rangeArgs(params)
}
})
return { list: (data.list || []).map(normalizeDoc), total: data.total || 0 }
}
/** 单据详情(含明细行、实时库存与审批实例摘要) */
export async function getDoc(id) {
const data = await request({ url: `/api/erp/document/detail/${id}` })
if (!data) return null
return normalizeDoc(data, true)
}
/**
* 新建/覆盖保存单据(草稿),返回 { id, docNo }
* doc: { id?, docType, bizDate, warehouseId, toWarehouseId, partnerId, sourceDocId,
* discountAmount, remark, items: [{ productId, quantity, unitPrice, ... }] }
*/
export async function saveDoc(doc) {
const res = await request({
url: '/api/erp/document/save',
method: 'POST',
data: buildDocPayload(doc)
})
return { id: res?.id, docNo: res?.doc_no || '' }
}
/** 直接审核生效(跳过审批引擎,用于未绑定审批流的租户) */
export function approveDoc(id) {
return request({ url: `/api/erp/document/approve/${id}`, method: 'POST', data: {} })
}
/** 作废单据(草稿/审批中/已驳回可作废) */
export function cancelDoc(id) {
return request({ url: `/api/erp/document/cancel/${id}`, method: 'POST', data: {} })
}
/** 删除单据(仅草稿/已驳回) */
export async function deleteDoc(id) {
await request({ url: `/api/erp/document/delete/${id}`, method: 'DELETE' })
return true
}
/** 提交审批:flowId 为空时后端按单据类型自动选流(flow_id 走 query,不进 body) */
export async function submitDoc(id, flowId) {
const query = flowId ? `?flow_id=${flowId}` : ''
const res = await request({ url: `/api/erp/document/submit/${id}${query}`, method: 'POST', data: {} })
return res?.instance_id
}
/** 单据类型可用的审批流 */
export async function fetchDocFlows(docType) {
const data = await request({ url: '/api/erp/document/flows', data: { doc_type: docType } })
return { list: data.list || [], bizType: data.biz_type || '' }
}
/**
* 来源订单的可执行明细行(下单量 - 已执行量),用于「订单转入库/出库」
*/
export async function fetchExecutableDocs(docId) {
const data = await request({ url: `/api/erp/document/executable/${docId}` })
return {
doc: data.doc || null,
items: (data.items || []).map(item => ({
...item,
remain_qty: Number(item.remain_qty || 0),
quantity: Number(item.quantity || 0),
executed_qty: Number(item.executed_qty || 0)
}))
}
}
/** 盘点表:带出仓库账面库存,供盘点录入对比差异 */
export async function fetchTakeSheet(warehouseId) {
const data = await request({ url: '/api/erp/document/takeSheet', data: { warehouse_id: warehouseId } })
return (data.list || []).map(row => ({ ...row, book_qty: Number(row.book_qty || 0) }))
}
// ----------------------------------------------------------------- 库存
/**
* 库存查询
* params: { warehouseId, productId, categoryId, batchNo, keyword, groupBy('product'|其它=分仓批次),
* safetyState, onlyStock, page, pageSize } → { list, total, sumAmount }
*/
export async function fetchStocks(params = {}) {
const data = await request({
url: '/api/erp/stock/list',
data: {
...pageArgs(params),
warehouse_id: params.warehouseId || '',
product_id: params.productId || '',
category_id: params.categoryId || '',
batch_no: params.batchNo || '',
keyword: params.keyword || '',
group_by: params.groupBy || '',
safety_state: params.safetyState || '',
only_stock: params.onlyStock ? 1 : ''
}
})
return { list: data.list || [], total: data.total || 0, sumAmount: Number(data.sum_amount || 0) }
}
/**
* 库存流水(收发存台账)
* params: { productId, warehouseId, docType, batchNo, docNo, dateFrom, dateTo, page, pageSize }
*/
export async function fetchStockLedger(params = {}) {
const data = await request({
url: '/api/erp/stock/ledger',
data: {
...pageArgs(params),
product_id: params.productId || '',
warehouse_id: params.warehouseId || '',
doc_type: params.docType || '',
batch_no: params.batchNo || '',
doc_no: params.docNo || '',
...rangeArgs(params)
}
})
return {
list: (data.list || []).map(row => ({
...row,
create_time_text: dateTimeText(row.create_time),
biz_date: dayText(row.create_time)
})),
total: data.total || 0
}
}
/** 库存预警四类分组 + 计数 */
export async function fetchStockWarning() {
const data = await request({ url: '/api/erp/stock/warning' })
return {
low: data.low || [],
over: data.over || [],
out: data.out || [],
expiring: data.expiring || [],
counts: data.counts || { low: 0, over: 0, out: 0, expiring: 0 }
}
}
/** 智能补货建议(含在途、近 30 天销量、建议量与金额) */
export async function fetchReplenish() {
const data = await request({ url: '/api/erp/stock/replenish' })
return { list: data.list || [], total: data.total || 0 }
}
/**
* 批次台账
* params: { warehouseId, productId, keyword, expireState, onlyPositive, page, pageSize }
*/
export async function fetchBatches(params = {}) {
const data = await request({
url: '/api/erp/stock/batch',
data: {
...pageArgs(params),
warehouse_id: params.warehouseId || '',
product_id: params.productId || '',
keyword: params.keyword || '',
expire_state: params.expireState || '',
only_positive: params.onlyPositive ? 1 : ''
}
})
return { list: data.list || [], total: data.total || 0 }
}
// ----------------------------------------------------------------- 档案(只读)
/**
* 商品列表
* params: { keyword, categoryId, brand, onlyEnabled, page, pageSize }
*/
export async function fetchProducts(params = {}) {
const data = await request({
url: '/api/erp/product/list',
data: {
...pageArgs(params),
keyword: params.keyword || '',
category_id: params.categoryId || '',
brand: params.brand || '',
only_enabled: params.onlyEnabled ? 1 : ''
}
})
return { list: data.list || [], total: data.total || 0 }
}
/** 商品详情(含分仓库存 stocks[]) */
export function getProduct(id) {
return request({ url: `/api/erp/product/detail/${id}` })
}
/** 商品分类:list 平铺 + tree 递归树 */
export async function fetchCategories() {
const data = await request({ url: '/api/erp/product/category/list' })
return { list: data.list || [], tree: data.tree || [] }
}
/** 仓库列表(带在库品种数与货值,无分页) */
export async function fetchWarehouses(params = {}) {
const data = await request({
url: '/api/erp/warehouse/list',
data: { keyword: params.keyword || '', only_enabled: params.onlyEnabled ? 1 : '' }
})
return (data.list || []).map(row => ({
...(row.warehouse || {}),
stock_kinds: row.stock_kinds || 0,
stock_value: Number(row.stock_value || 0)
}))
}
/**
* 客商列表(统一出参)
* params: { partnerType('supplier'|其它=客户), keyword, onlyEnabled, page, pageSize }
*/
export async function fetchPartners(params = {}) {
const data = await request({
url: '/api/erp/partner/list',
data: {
...pageArgs(params),
partner_type: params.partnerType || 'customer',
keyword: params.keyword || '',
only_enabled: params.onlyEnabled ? 1 : ''
}
})
return { list: data.list || [], total: data.total || 0 }
}
/**
* 客商详情:必须显式带 partner_type,缺省后端按客户查
*/
export function getPartner(id, partnerType) {
return request({ url: `/api/erp/partner/detail/${id}`, data: { partner_type: partnerType || 'customer' } })
}
// ----------------------------------------------------------------- 往来账 / 收付款
/**
* 应收应付列表
* params: { billType(1|2), partnerId, sourceDocId, settleState('open'|'part'|'done'),
* keyword, dateFrom, dateTo, page, pageSize }
* → { list, total, summary: { openReceivable, openPayable } }
*/
export async function fetchBills(params = {}) {
const data = await request({
url: '/api/erp/finance/bill/list',
data: {
...pageArgs(params),
bill_type: params.billType ?? '',
partner_id: params.partnerId || '',
source_doc_id: params.sourceDocId || '',
partner_type: params.partnerType || '',
settle_state: params.settleState || '',
keyword: params.keyword || '',
...rangeArgs(params)
}
})
return {
list: (data.list || []).map(normalizeBillRow),
total: data.total || 0,
summary: {
openReceivable: Number((data.summary || {}).open_receivable || 0),
openPayable: Number((data.summary || {}).open_payable || 0)
}
}
}
/** 往来账详情:核销记录、关联收付款单、来源单据明细 */
export async function getBill(id) {
const data = await request({ url: `/api/erp/finance/bill/detail/${id}` })
if (!data) return null
return {
bill: normalizeBillRow({ bill: data.bill, balance: Number(data.bill?.amount || 0) - Number(data.bill?.settled_amount || 0) }),
settles: data.settles || [],
funds: Object.keys(data.funds || {}).map(key => data.funds[key]),
docItems: data.doc_items || []
}
}
/** 账龄分析:按客商分桶 + 应收/应付合计 */
export async function fetchAging() {
const data = await request({ url: '/api/erp/finance/aging' })
return {
list: data.list || [],
summary: data.summary || {},
buckets: data.buckets || []
}
}
/** 资金流水(按日汇总,默认近 30 天) */
export async function fetchCashFlow(params = {}) {
const data = await request({
url: '/api/erp/finance/cashflow',
data: rangeArgs(params)
})
return {
list: data.list || [],
totalIn: Number(data.total_in || 0),
totalOut: Number(data.total_out || 0),
net: Number(data.net || 0)
}
}
/** 客商往来汇总(累计/未结,收付双向) */
export async function fetchPartnerSummary(partnerType) {
const data = await request({
url: '/api/erp/finance/partner',
data: { partner_type: partnerType || '' }
})
return data.list || []
}
/**
* 收付款单列表
* params: { fundType(1..4), status, partnerId, keyword, dateFrom, dateTo, page, pageSize }
*/
export async function fetchFunds(params = {}) {
const data = await request({
url: '/api/erp/finance/fund/list',
data: {
...pageArgs(params),
fund_type: params.fundType ?? '',
status: params.status ?? '',
partner_id: params.partnerId || '',
keyword: params.keyword || '',
...rangeArgs(params)
}
})
return { list: (data.list || []).map(normalizeFund), total: data.total || 0 }
}
/** 收付款单详情(含核销行与被核销的往来账) */
export async function getFund(id) {
const data = await request({ url: `/api/erp/finance/fund/detail/${id}` })
if (!data) return null
return { fund: normalizeFund(data.fund || {}), settles: data.fund?.settles || [], bills: data.bills || [] }
}
/** 待核销的往来账(confirm 页面选账用):fundType=2 查应付,其余查应收 */
export async function fetchOpenBills(fundType, partnerId) {
const data = await request({
url: '/api/erp/finance/fund/openBills',
data: { fund_type: fundType, partner_id: partnerId || '' }
})
return (data.list || []).map(row => ({ ...row, balance: Number(row.balance || 0) }))
}
/** 新建/修改收付款草稿,返回 { id, fundNo } */
export async function saveFund(fund) {
const res = await request({
url: '/api/erp/finance/fund/save',
method: 'POST',
data: {
id: fund.id || 0,
fund_type: Number(fund.fundType),
partner_id: fund.partnerId || 0,
amount: Number(fund.amount) || 0,
pay_method: fund.payMethod || 'cash',
pay_account: fund.payAccount || '',
biz_date: fund.bizDate || '',
remark: fund.remark || ''
}
})
return { id: res?.id, fundNo: res?.fund_no || '' }
}
/**
* 确认收付款并核销
* settles 为空 → 后端按到期日 FIFO 自动核销;
* settles = [{ partnerBillId, amount }] → 按单核销
*/
export function confirmFund(id, settles) {
return request({
url: `/api/erp/finance/fund/confirm/${id}`,
method: 'POST',
data: { settles: (settles || []).map(s => ({ partner_bill_id: s.partnerBillId, amount: Number(s.amount) })) }
})
}
/** 作废收付款单(已发生核销的不可作废,需做反向冲销) */
export function voidFund(id) {
return request({ url: `/api/erp/finance/fund/void/${id}`, method: 'POST', data: {} })
}
/** 删除收付款草稿(仅草稿) */
export async function deleteFund(id) {
await request({ url: `/api/erp/finance/fund/delete/${id}`, method: 'DELETE' })
return true
}
// ----------------------------------------------------------------- 报表
/** 采购汇总:dimension = product|supplier|month */
export async function fetchPurchaseSummary(params = {}) {
const data = await request({
url: '/api/erp/report/purchase',
data: { dimension: params.dimension || 'product', partner_id: params.partnerId || '', ...rangeArgs(params) }
})
return normalizeSummary(data)
}
/** 销售汇总:dimension = product|customer|month */
export async function fetchSalesSummary(params = {}) {
const data = await request({
url: '/api/erp/report/sales',
data: { dimension: params.dimension || 'product', partner_id: params.partnerId || '', ...rangeArgs(params) }
})
return normalizeSummary(data)
}
/** 热销(order=desc)/滞销(order=asc)排行 */
export async function fetchSalesRank(params = {}) {
const data = await request({
url: '/api/erp/report/rank',
data: { order: params.order || 'desc', top: params.top || 10, ...rangeArgs(params) }
})
return { list: data.list || [], order: data.order || 'desc', range: data.range || [] }
}
/** 收发存汇总表:期初 = 期末 -(本期入 - 本期出),与库存结存账实一致 */
export async function fetchStockMove(params = {}) {
const data = await request({
url: '/api/erp/report/stockmove',
data: {
...pageArgs(params),
warehouse_id: params.warehouseId || '',
keyword: params.keyword || '',
...rangeArgs(params)
}
})
return { list: data.list || [], total: data.total || 0, summary: data.summary || {} }
}
/** 毛利分析:收入=销售出库-销售退货,成本取库存流水实际出库成本 */
export async function fetchProfit(params = {}) {
const data = await request({
url: '/api/erp/report/profit',
data: rangeArgs(params)
})
return { list: data.list || [], summary: data.summary || {} }
}
// ----------------------------------------------------------------- 归一化
/** 单据主表归一;withItems=true 时明细行日期/金额也归一 */
function normalizeDoc(row, withItems) {
const out = {
...row,
biz_date: dayText(row.biz_date),
create_time_text: dateTimeText(row.create_time),
audit_time_text: dateTimeText(row.audit_time),
total_qty: Number(row.total_qty || 0),
total_amount: Number(row.total_amount || 0),
discount_amount: Number(row.discount_amount || 0),
actual_amount: Number(row.actual_amount || 0)
}
if (withItems) {
out.items = (row.items || []).map(item => ({
...item,
quantity: Number(item.quantity || 0),
unit_price: Number(item.unit_price || 0),
amount: Number(item.amount || 0),
stock_qty: Number(item.stock_qty || 0),
production_date: dayText(item.production_date),
expire_date: dayText(item.expire_date)
}))
out.instance = row.instance || null
}
return out
}
/** 组装后端要求的单据请求体(明细只传可编辑字段,单价缺省由后端按价格体系取) */
function buildDocPayload(doc) {
return {
id: doc.id || 0,
doc_type: doc.docType,
biz_date: doc.bizDate || '',
warehouse_id: Number(doc.warehouseId) || 0,
to_warehouse_id: Number(doc.toWarehouseId) || 0,
partner_id: Number(doc.partnerId) || 0,
source_doc_id: Number(doc.sourceDocId) || 0,
discount_amount: Number(doc.discountAmount) || 0,
remark: doc.remark || '',
items: (doc.items || []).map(item => ({
id: item.id || 0,
product_id: Number(item.productId || item.product_id) || 0,
warehouse_id: Number(item.warehouseId || item.warehouse_id) || 0,
batch_no: item.batchNo || item.batch_no || '',
quantity: Number(item.quantity) || 0,
book_qty: Number(item.bookQty ?? item.book_qty) || 0,
ref_item_id: Number(item.refItemId || item.ref_item_id) || 0,
unit_price: Number(item.unitPrice ?? item.unit_price) || 0,
amount: Number(item.amount) || 0,
production_date: item.productionDate || item.production_date || '',
expire_date: item.expireDate || item.expire_date || '',
remark: item.remark || '',
sort_order: Number(item.sortOrder || item.sort_order) || 0
}))
}
}
/** 往来账行:后端返回 { bill, balance, overdue_days, status_label },摊平给列表用 */
function normalizeBillRow(row) {
const bill = row.bill || {}
return {
...bill,
balance: Number(row.balance ?? (Number(bill.amount || 0) - Number(bill.settled_amount || 0))),
overdue_days: Number(row.overdue_days || 0),
status_label: row.status_label || billStatusLabel(bill.status),
bill_date: dayText(bill.bill_date),
due_date: dayText(bill.due_date)
}
}
function normalizeFund(row) {
return {
...row,
amount: Number(row.amount || 0),
settled_amount: Number(row.settled_amount || 0),
biz_date: dayText(row.biz_date),
create_time_text: dateTimeText(row.create_time)
}
}
function normalizeSummary(data) {
return {
list: (data.list || []).map(row => ({
...row,
qty: Number(row.qty || 0),
amount: Number(row.amount || 0)
})),
dimension: data.dimension || 'product',
summary: {
qty: Number((data.summary || {}).qty || 0),
amount: Number((data.summary || {}).amount || 0)
}
}
}