334 lines
9.1 KiB
JavaScript
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')
|
|
}
|