更新
This commit is contained in:
1 parent
5718a00e31
commit
1850ac640e
226 files changed
+37772
-12071
No files matched your search
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user