785 lines
25 KiB
JavaScript
785 lines
25 KiB
JavaScript
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)
|
||
}
|
||
}
|
||
}
|