feat(settlement): v4.0.6 酒厂 T+3 每 3 天出一期并与订单完全比对
含现场提货入账、零应付仍出账、核对补生成,以及线上历史账单修复 SQL。
This commit is contained in:
@@ -19,6 +19,7 @@ import {
|
||||
} from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import type { Dayjs } from 'dayjs';
|
||||
import dayjs from 'dayjs';
|
||||
import {
|
||||
WINERY_SETTLEMENT_RATE,
|
||||
type SystemConfigFormResponse,
|
||||
@@ -95,6 +96,7 @@ function canConfirmWineryPay(row: { status: string; wineryAmount: number | strin
|
||||
const DELIVERY_LABELS: Record<string, string> = {
|
||||
LOCAL: '同城',
|
||||
CROSS_CITY: '跨城',
|
||||
ON_SITE_PICKUP: '现场提货',
|
||||
};
|
||||
|
||||
const WINERY_BANK_KEYS = [
|
||||
@@ -107,6 +109,7 @@ const WINERY_BANK_KEYS = [
|
||||
export default function WineryBillsPage() {
|
||||
const [form] = Form.useForm();
|
||||
const [bankForm] = Form.useForm<Record<string, string>>();
|
||||
const [reconcileForm] = Form.useForm<{ range?: [Dayjs, Dayjs] }>();
|
||||
const [filters, setFilters] = useState<Record<string, string>>({});
|
||||
const { data, loading, page, pageSize, setPage, setPageSize, reload } = useAdminList<Row>(
|
||||
'/admin/winery-bills',
|
||||
@@ -122,6 +125,8 @@ export default function WineryBillsPage() {
|
||||
[filters],
|
||||
);
|
||||
const [exporting, setExporting] = useState(false);
|
||||
const [reconciling, setReconciling] = useState(false);
|
||||
const [reconcileOpen, setReconcileOpen] = useState(false);
|
||||
const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]);
|
||||
const [detail, setDetail] = useState<(Row & { items?: BillItem[] }) | null>(null);
|
||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||
@@ -200,6 +205,33 @@ export default function WineryBillsPage() {
|
||||
}
|
||||
}
|
||||
|
||||
async function runReconcile(values: { range?: [Dayjs, Dayjs] }) {
|
||||
setReconciling(true);
|
||||
try {
|
||||
const body: { dateFrom?: string; dateTo?: string } = {};
|
||||
if (values.range?.[0]) body.dateFrom = values.range[0].format('YYYY-MM-DD');
|
||||
if (values.range?.[1]) body.dateTo = values.range[1].format('YYYY-MM-DD');
|
||||
const result = await request<{
|
||||
generated: number;
|
||||
skipped: number;
|
||||
orphanOrders: number;
|
||||
days: number;
|
||||
}>('/admin/winery-bills/reconcile', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
message.success(
|
||||
`核对完成:重算 ${result.generated} 天,跳过 ${result.skipped} 天,补入孤儿订单 ${result.orphanOrders} 笔`,
|
||||
);
|
||||
setReconcileOpen(false);
|
||||
reload();
|
||||
} catch (e) {
|
||||
message.error(e instanceof Error ? e.message : '核对失败');
|
||||
} finally {
|
||||
setReconciling(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function openBankModal() {
|
||||
setBankOpen(true);
|
||||
setBankLoading(true);
|
||||
@@ -314,13 +346,18 @@ export default function WineryBillsPage() {
|
||||
<AdminListHeader
|
||||
title="酒厂对账单"
|
||||
settings={settingsButton}
|
||||
description={`T+3:每日 8:00 汇总 3 天前(自然日)已完成的同城/跨城订单(实付 × ${ratePct}%);未打款红色、已打款绿色、应付为 0 无需打款(灰),可展开订单明细`}
|
||||
description={`T+3:每 3 天出一期(相对 2026-08-01),纳入上期 3 天同城/跨城/现场提货已付已完成订单(实付 × ${ratePct}%);应付为 0 仍出账「无需打款」`}
|
||||
actions={
|
||||
canEditWineryBank ? (
|
||||
<Button type="default" onClick={() => void openBankModal()}>
|
||||
酒厂银行账户信息配置
|
||||
<Space>
|
||||
<Button type="primary" onClick={() => setReconcileOpen(true)}>
|
||||
核对补生成
|
||||
</Button>
|
||||
) : null
|
||||
{canEditWineryBank ? (
|
||||
<Button type="default" onClick={() => void openBankModal()}>
|
||||
酒厂银行账户信息配置
|
||||
</Button>
|
||||
) : null}
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -530,6 +567,32 @@ export default function WineryBillsPage() {
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
title="核对补生成酒厂账单"
|
||||
open={reconcileOpen}
|
||||
onCancel={() => setReconcileOpen(false)}
|
||||
footer={null}
|
||||
destroyOnClose
|
||||
>
|
||||
<Typography.Paragraph type="secondary" style={{ marginBottom: 12 }}>
|
||||
核对时会补齐同城配送、跨城配送、现场提货的已付已完成订单;并修复历史现场单缺完成时间的问题。
|
||||
实付或应付为 0 也会出账,状态显示为「无需打款」。已打款账单不回刷。
|
||||
</Typography.Paragraph>
|
||||
<Form
|
||||
form={reconcileForm}
|
||||
layout="vertical"
|
||||
initialValues={{ range: [dayjs().subtract(60, 'day'), dayjs()] }}
|
||||
onFinish={(v) => void runReconcile(v)}
|
||||
>
|
||||
<Form.Item name="range" label="出账日区间(可留空则默认近 60 天)">
|
||||
<DatePicker.RangePicker style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
<Button type="primary" htmlType="submit" block loading={reconciling}>
|
||||
开始核对
|
||||
</Button>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user