Files
2026-10-19 01:13:17 +08:00

785 lines
25 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
}
}
}