Files
BMS/bms-front/src/api/price/billing.js
T
2026-10-15 16:09:05 +08:00

334 lines
9.1 KiB
JavaScript

import request from '@/utils/request'
import { saveAs } from 'file-saver'
import { blobValidate } from '@/utils/bms'
import { ElLoading } from 'element-plus'
import errorCode from '@/utils/errorCode'
/** Quote trial for billing center */
export function quoteBilling(data) {
return request({
url: '/price/billing/quote',
method: 'post',
data
})
}
/** Products from customer's bound effective package (trial dropdown) */
export function listQuoteBoundProducts(customerCode, bizDate) {
return request({
url: '/price/billing/quote/products',
method: 'get',
params: { customerCode, bizDate }
})
}
/** Historical quote list */
export function listQuote(query) {
return request({
url: '/price/billing/quote/list',
method: 'get',
params: query
})
}
/** Historical quote detail */
export function getQuote(quoteId) {
return request({
url: '/price/billing/quote/' + quoteId,
method: 'get'
})
}
/** Sync order receivable bill */
export function billOrder(data) {
return request({
url: '/price/billing/bill',
method: 'post',
data
})
}
/** Receivable bill list */
export function listBill(query) {
return request({
url: '/price/billing/bill/list',
method: 'get',
params: query
})
}
/** Receivable bill detail */
export function getBill(billId) {
return request({
url: '/price/billing/bill/' + billId,
method: 'get'
})
}
/** Refund receivable bill (billType=REFUND) */
export function refundBill(billId) {
return request({
url: '/price/billing/bill/' + encodeURIComponent(billId) + '/refund',
method: 'post',
timeout: 120000
})
}
/** Order no options for remote search (bill list multi search) */
export function listOrderNoOptions(keyword) {
return request({
url: '/price/billing/bill/orderNos',
method: 'get',
params: { keyword }
})
}
/** 运单号下拉远程搜索选项 */
export function listTrackingNoOptions(keyword) {
return request({
url: '/price/billing/bill/trackingNos',
method: 'get',
params: { keyword }
})
}
/** 转单号下拉远程搜索选项 */
export function listTransferNoOptions(keyword) {
return request({
url: '/price/billing/bill/transferNos',
method: 'get',
params: { keyword }
})
}
/** 计费详情:增加应收费用明细 */
export function addIncomeBillFee(data) {
return request({
url: '/price/billing/bill/fee',
method: 'post',
data
})
}
/** 批量增加费用:按筛选预览匹配订单 */
export function previewBatchAddIncomeFee(data) {
return request({
url: '/price/billing/bill/fee/batch/preview',
method: 'post',
data
})
}
/** 批量增加费用:提交异步任务 */
export function submitBatchAddIncomeFee(data) {
return request({
url: '/price/billing/bill/fee/batch',
method: 'post',
data
})
}
export function getBatchAddIncomeFeeTask(taskId) {
return request({
url: '/price/billing/bill/fee/batch/task/' + taskId,
method: 'get'
})
}
async function pollBatchAddIncomeFeeTask(taskId, onProgress) {
const started = Date.now()
const maxMs = 30 * 60 * 1000
while (Date.now() - started < maxMs) {
const res = await getBatchAddIncomeFeeTask(taskId)
const task = res.data || {}
if (typeof onProgress === 'function') onProgress(task)
if (task.status === 'SUCCESS') return { task }
if (task.status === 'FAIL') {
const err = new Error(task.error || task.message || 'batch add fee failed')
err.task = task
throw err
}
await new Promise(resolve => setTimeout(resolve, 800))
}
throw new Error('batch add fee timeout')
}
/**
* Async batch add fee; poll until done.
* @param {object} data BatchAddIncomeBillFeeRequest
* @param {(task: object) => void} [onProgress]
*/
export async function batchAddIncomeFeeAsync(data, onProgress) {
const submitRes = await submitBatchAddIncomeFee(data)
const d = submitRes && submitRes.data
const taskId = d && (d.taskId || d)
if (!taskId) throw new Error('missing batch fee taskId')
return pollBatchAddIncomeFeeTask(String(taskId), onProgress)
}
/** 计费详情:编辑手动应收费用 */
export function updateIncomeBillFee(data) {
return request({
url: '/price/billing/bill/fee',
method: 'put',
data
})
}
/** 计费详情:删除手动应收费用 */
export function deleteIncomeBillFee(feeId) {
return request({
url: '/price/billing/bill/fee/' + feeId,
method: 'delete'
})
}
/** 计费列表:批量更新订单额度欠费状态(校验可用额度) */
export function updateCreditArrears(billIds) {
return request({
url: '/price/billing/bill/creditArrears',
method: 'put',
data: billIds
})
}
/** 计费列表:单票修改计费重(写 charge_weight,保留 weight)并重新计费 */
export function updateChargeWeight(data) {
return request({
url: '/price/billing/bill/chargeWeight',
method: 'put',
data
})
}
export function getChargeWeightImportTask(taskId) {
return request({
url: '/price/billing/bill/chargeWeight/import/task/' + taskId,
method: 'get'
})
}
function pickChargeWeightImportTaskId(res) {
const d = res && res.data
if (!d) return ''
if (typeof d === 'string' || typeof d === 'number') return String(d)
return String(d.taskId || d.id || '')
}
async function pollChargeWeightImportTask(taskId, onProgress) {
const started = Date.now()
const maxMs = 30 * 60 * 1000
while (Date.now() - started < maxMs) {
const res = await getChargeWeightImportTask(taskId)
const task = res.data || {}
if (typeof onProgress === 'function') onProgress(task)
if (task.status === 'SUCCESS') return { task }
if (task.status === 'FAIL') {
const err = new Error(task.error || task.message || 'charge weight import failed')
err.task = task
throw err
}
await new Promise(resolve => setTimeout(resolve, 800))
}
throw new Error('charge weight import timeout')
}
/**
* Async import charge weight + rebill; poll until done.
* @param {File|Blob} file
* @param {(task: object) => void} [onProgress]
*/
export async function importChargeWeightAsync(file, onProgress) {
const formData = new FormData()
formData.append('file', file)
const submitRes = await request({
url: '/price/billing/bill/chargeWeight/importData',
method: 'post',
data: formData,
headers: { 'Content-Type': 'multipart/form-data', repeatSubmit: false },
// Backend parses Excel + validates rows before returning taskId
timeout: 10 * 60 * 1000
})
const taskId = pickChargeWeightImportTaskId(submitRes)
if (!taskId) throw new Error('missing charge weight import taskId')
return pollChargeWeightImportTask(taskId, onProgress)
}
/** Download charge-weight import template (optionally fill by list filters). */
export async function downloadChargeWeightTemplate(data, filename) {
const loading = ElLoading.service({ text: '正在下载模板,请稍候', background: 'rgba(0, 0, 0, 0.7)' })
try {
const blobData = await request({
url: '/price/billing/bill/chargeWeight/importTemplate',
method: 'post',
data: data || {},
responseType: 'blob',
timeout: 120000
})
if (blobValidate(blobData)) {
saveAs(new Blob([blobData]), filename || ('批量修改计费重导入模板_' + Date.now() + '.xlsx'))
return
}
const resText = await blobData.text()
const rspObj = JSON.parse(resText)
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] || '下载模板失败'
throw new Error(errMsg)
} finally {
loading.close()
}
}
/** 批量修改计费重导入附件记录 */
export function listChargeWeightImportAttachments(query) {
return request({
url: '/price/billing/bill/chargeWeight/import/attachments',
method: 'get',
params: query
})
}
/** Submit async recalc (PRODUCT / REDLINE) */
export function submitRecalcBillPrice(mode, data) {
const path = mode === 'REDLINE' ? 'recalcRedline' : 'recalcProduct'
return request({
url: '/price/billing/bill/' + path,
method: 'post',
data
})
}
export function getRecalcBillPriceTask(taskId) {
return request({
url: '/price/billing/bill/recalcPrice/task/' + taskId,
method: 'get'
})
}
/**
* Async recalc product/redline amount; poll until done.
* @param {'PRODUCT'|'REDLINE'} mode
* @param {object} data
* @param {(task: object) => void} [onProgress]
*/
export async function recalcBillPriceAsync(mode, data, onProgress) {
const submitRes = await submitRecalcBillPrice(mode, data)
const d = submitRes && submitRes.data
const taskId = d && (d.taskId || d)
if (!taskId) throw new Error('missing recalc taskId')
const started = Date.now()
const maxMs = 30 * 60 * 1000
while (Date.now() - started < maxMs) {
const res = await getRecalcBillPriceTask(String(taskId))
const task = res.data || {}
if (typeof onProgress === 'function') onProgress(task)
if (task.status === 'SUCCESS') return { task }
if (task.status === 'FAIL') {
const err = new Error(task.error || task.message || 'recalc price failed')
err.task = task
throw err
}
await new Promise(resolve => setTimeout(resolve, 800))
}
throw new Error('recalc price timeout')
}