P2-2部署尝试+compose通配符修复 / D2收尾(唯一单号+前后断言+H5回归17过) / P2-1 StorefrontAdmin真分页 / 后端511+PG4
This commit is contained in:
@@ -230,6 +230,13 @@ import {
|
||||
isValidPrice as validatePrice,
|
||||
lineAmount,
|
||||
} from '../utils/transaction'
|
||||
import {
|
||||
linesTotal as calcLinesTotal,
|
||||
taxTotal as calcTaxTotal,
|
||||
roundOffAmount as calcRoundOff,
|
||||
netTotal as calcNetTotal,
|
||||
debtTotal as calcDebtTotal,
|
||||
} from '../utils/salesman'
|
||||
|
||||
const token = ref(localStorage.getItem('token') || '')
|
||||
const username = ref(localStorage.getItem('username') || '')
|
||||
@@ -394,21 +401,11 @@ function lineTax(p) {
|
||||
function rowAmount(r) {
|
||||
return lineAmount({ quantity: r.qty, unit_price: r.price })
|
||||
}
|
||||
const linesTotal = computed(() => order.value.reduce((s, r) => s + rowAmount(r), 0))
|
||||
const taxTotal = computed(() =>
|
||||
order.value.reduce((s, r) => {
|
||||
const amt = rowAmount(r)
|
||||
const rate = Number(r.taxRate || 0)
|
||||
return s + (rate > 0 && amt > 0 ? amt - amt / (1 + rate) : 0)
|
||||
}, 0),
|
||||
)
|
||||
const roundOffAmount = computed(() => {
|
||||
const rt = Number(form.round_to || 0)
|
||||
if (!rt) return 0
|
||||
const total = linesTotal.value
|
||||
return Number((total - Math.floor(total / rt) * rt).toFixed(4))
|
||||
})
|
||||
const netTotal = computed(() => linesTotal.value - roundOffAmount.value)
|
||||
// D2 收尾:合计/抹零/税额走 utils/salesman 纯函数(与 npm test 同一份实现)
|
||||
const linesTotal = computed(() => calcLinesTotal(order.value))
|
||||
const taxTotal = computed(() => calcTaxTotal(order.value))
|
||||
const roundOffAmount = computed(() => calcRoundOff(linesTotal.value, form.round_to))
|
||||
const netTotal = computed(() => calcNetTotal(order.value, form.round_to))
|
||||
|
||||
const creditPercent = computed(() => {
|
||||
const limit = Number(credit.value.limit || 0)
|
||||
@@ -476,10 +473,8 @@ const debtsLoading = ref(false)
|
||||
const debtCustomer = ref(null)
|
||||
const debtStatusMap = { open: '未收', partial: '部分已收', paid: '已结清', cancelled: '已取消' }
|
||||
const debtStatusText = (s) => debtStatusMap[s] || s
|
||||
const debtTotal = computed(() =>
|
||||
debts.value.filter((d) => d.status !== 'paid')
|
||||
.reduce((s, d) => s + Number(d.balance || 0), 0),
|
||||
)
|
||||
// D2 收尾:欠款合计走 utils/salesman 纯函数(与 npm test 同一份实现)
|
||||
const debtTotal = computed(() => calcDebtTotal(debts.value))
|
||||
|
||||
async function loadDebts() {
|
||||
debtsLoading.value = true
|
||||
|
||||
@@ -64,6 +64,14 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
style="margin-top: 14px"
|
||||
layout="prev, pager, next, total"
|
||||
:total="ordersCount"
|
||||
:page-size="ordersPageSize"
|
||||
v-model:current-page="ordersPage"
|
||||
@current-change="loadOrders"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 客户账号 -->
|
||||
@@ -149,6 +157,10 @@ const tab = ref('orders')
|
||||
const orders = ref([])
|
||||
const ordersLoading = ref(false)
|
||||
const statusFilter = ref('submitted')
|
||||
// P2-1:商城订单分页(后端 StandardAsyncPagination 默认 20 条,无分页会吞数据)
|
||||
const ordersPage = ref(1)
|
||||
const ordersPageSize = 20
|
||||
const ordersCount = ref(0)
|
||||
|
||||
const accounts = ref([])
|
||||
const accountsLoading = ref(false)
|
||||
@@ -171,9 +183,14 @@ async function loadOrders() {
|
||||
ordersLoading.value = true
|
||||
try {
|
||||
const data = await api.get('/storefront/admin/orders/', {
|
||||
params: { status: statusFilter.value },
|
||||
params: {
|
||||
status: statusFilter.value,
|
||||
page: ordersPage.value,
|
||||
page_size: ordersPageSize,
|
||||
},
|
||||
})
|
||||
orders.value = data.results || []
|
||||
ordersCount.value = data.count ?? data.results?.length ?? 0
|
||||
} finally {
|
||||
ordersLoading.value = false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// D2 · 业务员 H5 纯计算(与 Salesman.vue 同口径,无框架依赖 + node --test 单测)。
|
||||
//
|
||||
// 抽取原因:抹零/价内税拆分/欠款合计逻辑锁在 Salesman.vue 的 computed 里,
|
||||
// 无法进 `npm test`。这里沉淀为纯函数,组件与测试共用同一份实现。
|
||||
|
||||
export function rowAmount(row) {
|
||||
return Number(row?.qty || 0) * Number(row?.price || 0)
|
||||
}
|
||||
|
||||
export function linesTotal(rows) {
|
||||
return (rows || []).reduce((s, r) => s + rowAmount(r), 0)
|
||||
}
|
||||
|
||||
export function lineTaxAmount(amount, rate) {
|
||||
const amt = Number(amount || 0)
|
||||
const r = Number(rate || 0)
|
||||
if (!r || amt <= 0) return 0
|
||||
return amt - amt / (1 + r)
|
||||
}
|
||||
|
||||
export function taxTotal(rows) {
|
||||
return (rows || []).reduce((s, r) => s + lineTaxAmount(rowAmount(r), r.taxRate), 0)
|
||||
}
|
||||
|
||||
export function roundOffAmount(total, roundTo) {
|
||||
const rt = Number(roundTo || 0)
|
||||
if (!rt) return 0
|
||||
const t = Number(total || 0)
|
||||
return Number((t - Math.floor(t / rt) * rt).toFixed(4))
|
||||
}
|
||||
|
||||
export function netTotal(rows, roundTo) {
|
||||
const total = linesTotal(rows)
|
||||
return total - roundOffAmount(total, roundTo)
|
||||
}
|
||||
|
||||
export function openDebts(rows) {
|
||||
return (rows || []).filter((r) => r?.status === 'open' || r?.status === 'partial')
|
||||
}
|
||||
|
||||
export function debtTotal(rows) {
|
||||
return openDebts(rows)
|
||||
.filter((d) => d.status !== 'paid')
|
||||
.reduce((s, d) => s + Number(d.balance || 0), 0)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import {
|
||||
rowAmount,
|
||||
linesTotal,
|
||||
lineTaxAmount,
|
||||
taxTotal,
|
||||
roundOffAmount,
|
||||
netTotal,
|
||||
openDebts,
|
||||
debtTotal,
|
||||
} from '../src/utils/salesman.js'
|
||||
|
||||
test('rowAmount / linesTotal', () => {
|
||||
assert.equal(rowAmount({ qty: 2, price: 12.5 }), 25)
|
||||
assert.equal(linesTotal([{ qty: 2, price: 12.5 }, { qty: 1, price: 6 }]), 31)
|
||||
assert.equal(linesTotal([]), 0)
|
||||
})
|
||||
|
||||
test('lineTaxAmount uses 价内拆分 (D4 同公式)', () => {
|
||||
// 113 @13% → 税 13
|
||||
assert.ok(Math.abs(lineTaxAmount(113, 0.13) - 13) < 1e-9)
|
||||
assert.equal(lineTaxAmount(100, 0), 0)
|
||||
assert.equal(lineTaxAmount(0, 0.13), 0)
|
||||
})
|
||||
|
||||
test('taxTotal sums per-row 价内税', () => {
|
||||
const rows = [
|
||||
{ qty: 1, price: 113, taxRate: 0.13 },
|
||||
{ qty: 1, price: 100, taxRate: 0 },
|
||||
]
|
||||
assert.ok(Math.abs(taxTotal(rows) - 13) < 1e-9)
|
||||
})
|
||||
|
||||
test('roundOffAmount 抹零口径与 Salesman.vue 一致', () => {
|
||||
assert.equal(roundOffAmount(31, ''), 0)
|
||||
assert.equal(roundOffAmount(31, '1'), 0)
|
||||
// 31.37 抹角 → 0.07
|
||||
assert.ok(Math.abs(roundOffAmount(31.37, '0.1') - 0.07) < 1e-9)
|
||||
// 31.37 抹分 → 0
|
||||
assert.equal(roundOffAmount(31.37, '0.01'), 0)
|
||||
})
|
||||
|
||||
test('netTotal = 行合计 - 抹零', () => {
|
||||
const rows = [{ qty: 2, price: 12.5 }, { qty: 1, price: 6 }]
|
||||
assert.ok(Math.abs(netTotal(rows, '0.1') - 31) < 1e-9)
|
||||
assert.ok(Math.abs(netTotal([{ qty: 1, price: 31.37 }], '0.1') - 31.3) < 1e-9)
|
||||
})
|
||||
|
||||
test('openDebts 只留 open/partial,debtTotal 求和 balance', () => {
|
||||
const rows = [
|
||||
{ id: 1, status: 'open', balance: '31.00' },
|
||||
{ id: 2, status: 'partial', balance: '10.50' },
|
||||
{ id: 3, status: 'paid', balance: '0' },
|
||||
]
|
||||
assert.equal(openDebts(rows).length, 2)
|
||||
assert.ok(Math.abs(debtTotal(rows) - 41.5) < 1e-9)
|
||||
assert.equal(debtTotal([]), 0)
|
||||
})
|
||||
Reference in New Issue
Block a user