feat(finance): 完成财务控制、L1 钱包硬化与迁移收敛

- P0/P1:影子总账追加调整、关账/退款/资金/积分幂等、成本版本追溯、对账订单项维度
- 迁移收敛:原 103–114 合并为 103_finance_foundation、104_finance_controls、105_work_order_consignment
- 数据:L1 钱包 BIGINT/CHECK/奖励幂等;重复奖励审计口径修正与人工核对清单
- 报表:销售毛利缺失成本收入提示,旧收支页明确现金流口径
- 文档:同步迁移基线、合并评估、代码复核与上线说明
This commit is contained in:
yml2213
2026-10-27 16:08:16 +08:00
parent a53321ef30
commit 9d5af359fa
47 changed files with 4527 additions and 1410 deletions
+4 -2
View File
@@ -144,11 +144,13 @@ function AdminApplication() {
<Route index element={<Navigate to="/admin/dashboard" replace />} />
<Route element={<RequireRole roles={['admin', 'operator', 'support']} />}>
<Route path="dashboard" element={<AdminDashboardPage />} />
<Route path="orders" element={<AdminOrdersPage />} />
<Route path="orders/:orderId" element={<AdminOrderDetailPage />} />
<Route path="tasks" element={<AdminTasksPage />} />
<Route path="tasks/:taskId" element={<AdminTaskDetailPage />} />
</Route>
<Route element={<RequireRole roles={['admin', 'operator', 'support', 'finance']} />}>
<Route path="orders" element={<AdminOrdersPage />} />
<Route path="orders/:orderId" element={<AdminOrderDetailPage />} />
</Route>
<Route element={<RequireRole roles={['admin', 'operator', 'finance']} />}>
<Route path="finance" element={<AdminFinancePage />} />
<Route path="finance/ledger" element={<AdminFinanceLedgerPage />} />
@@ -214,6 +214,7 @@ export default function AdminLayout() {
const operationItems: MenuProps['items'] =
role === 'finance'
? [
{ key: '/admin/orders', icon: <OrderedListOutlined />, label: '订单' },
financeSubMenu,
{ key: '/admin/worker-platform', icon: <TrophyOutlined />, label: '资金审核' },
]
@@ -11,6 +11,7 @@ import {
Input,
InputNumber,
Modal,
Popconfirm,
Row,
Select,
Space,
@@ -78,7 +79,10 @@ const MATCH_STATUS_META: Record<string, { label: string; color: string }> = {
export default function AdminFinanceLedgerPage() {
const { message } = App.useApp()
const queryClient = useQueryClient()
const canConfig = hasAdminPermission('finance.config')
const canConfig =
hasAdminPermission('finance.post') ||
hasAdminPermission('finance.close') ||
hasAdminPermission('finance.reconcile')
const [range, setRange] = useState<[Dayjs, Dayjs]>([dayjs().startOf('month'), dayjs()])
const [entryPage, setEntryPage] = useState(1)
const [entryPageSize, setEntryPageSize] = useState(20)
@@ -297,13 +301,27 @@ export default function AdminFinanceLedgerPage() {
render: (_, row) =>
canConfig ? (
row.status === 'closed' ? (
<Button size="small" onClick={() => reopenMutation.mutate(row.periodKey)}>
反关账
</Button>
<Popconfirm
title="确认反关账?"
description="反关账会重新开放该期间,之后可继续写入凭证。"
okText="反关账"
cancelText="取消"
onConfirm={() => reopenMutation.mutate(row.periodKey)}
>
<Button size="small">反关账</Button>
</Popconfirm>
) : (
<Button size="small" type="primary" onClick={() => closeMutation.mutate(row.periodKey)}>
关账
</Button>
<Popconfirm
title="确认关账?"
description="关账后该期间不可再新增凭证,且会校验人工成本与资金流水门槛。"
okText="关账"
cancelText="取消"
onConfirm={() => closeMutation.mutate(row.periodKey)}
>
<Button size="small" type="primary">
关账
</Button>
</Popconfirm>
)
) : null,
},
@@ -162,7 +162,17 @@ export default function AdminFinancePage() {
return (
<section className="page-stack">
<FinanceSubNav current="/admin/finance" />
<PageHeader title="收支明细" description="按日期查看订单收款、退款、打手结算和平台现金流。" />
<PageHeader
title="收支明细"
description="按日期查看订单收款、退款、打手结算与平台现金收付;打手报酬是成本口径,不参与净现金流。"
/>
<Alert
type="info"
showIcon
message="本页是现金收付口径,不是损益表"
description="打手报酬不进入净现金流;毛利、成本和缺失成本请到「销售毛利」查看,避免把奖励成本与提现现金重复相加。"
/>
<Card>
<Form<FinanceFilterForm>
@@ -289,7 +289,7 @@ export default function AdminOrderDetailPage() {
title="基础信息"
extra={
<Space>
{hasAdminPermission('finance.config') ? (
{hasAdminPermission('finance.post') ? (
<Button
danger
icon={<RollbackOutlined />}
@@ -562,7 +562,7 @@ export default function AdminOrderDetailPage() {
title: '操作',
width: 90,
render: (_, row) =>
hasAdminPermission('finance.config') && row.status === 'confirmed' ? (
hasAdminPermission('finance.post') && row.status === 'confirmed' ? (
<Button size="small" danger onClick={() => setVoidTarget(row)}>
撤销
</Button>
@@ -60,7 +60,7 @@ const COST_STAGE_LABEL: Record<string, { label: string; color: string }> = {
export default function AdminSalesProfitPage() {
const { message } = App.useApp()
const [form] = Form.useForm<FilterForm>()
const canConfig = hasAdminPermission('finance.config')
const canConfig = hasAdminPermission('finance.post')
const defaultDateFrom = dayjs().subtract(29, 'day').format('YYYY-MM-DD')
const defaultDateTo = dayjs().format('YYYY-MM-DD')
const [filters, setFilters] = useState<{
@@ -315,7 +315,7 @@ export default function AdminSalesProfitPage() {
<Alert
type="warning"
showIcon
message={`有 ${summary.costMissingCount} 个订单项没有成本快照,当前毛利偏高`}
message={`有 ${summary.costMissingCount} 个订单项没有成本快照,涉及收入 ${formatMoney(summary.costMissingRevenueFen)},当前毛利偏高`}
description="请先在「商品与成本」中补齐成本项/版本并回填快照,再使用毛利数据做经营决策。"
/>
) : null}
+1
View File
@@ -78,6 +78,7 @@ export type AdminSalesProfitOverview = {
netRevenueFen: number
costAmountFen: number
costMissingCount: number
costMissingRevenueFen: number
grossProfitFen: number
unmappedRevenueFen: number
unmappedRevenuePct: number