Files
dukang/server/dukang-api/prisma/sql/fix-winery-bills-v406.sql
T
jacy f35e1a3ba5 feat(settlement): v4.0.6 酒厂 T+3 每 3 天出一期并与订单完全比对
含现场提货入账、零应付仍出账、核对补生成,以及线上历史账单修复 SQL。
2026-08-31 17:27:38 +08:00

196 lines
6.9 KiB
SQL

-- =============================================================================
-- 酒厂账单一次性修复(v4.0.6)
-- 库:生产 dukang_prod(也可在已同步的本地 dukang_haoke 先演练)
--
-- 规则(与代码一致):
-- 起点 WINERY_BILL_EPOCH_YMD = 2026-08-01
-- 周期 WINERY_SETTLEMENT_PERIOD_DAYS = 3
-- 出账日 8/4、8/7、8/10 … ;纳入窗口 [出账日-3, 出账日)
-- 订单:status=COMPLETED AND pay_status=PAID
-- delivery_type IN ('LOCAL','CROSS_CITY','ON_SITE_PICKUP')
-- 账期归属:completed_at(缺则 paid_at)的北京日历日
-- 应付 = ROUND(实付合计 × 0.3, 2);出账日无单仍留账单(无需打款)
-- 已打款账单不改明细
-- 起点之前已入账的订单(如 7/28 同城挂在 7/31)保持原账单,不迁到 8/4
--
-- 本脚本会:
-- 1) 补现场提货等缺 completed_at 的已完成单
-- 2) 按周期补齐出账日账单(含中间应付为 0 的空账)
-- 3) 把未入账 / 挂错期(如 8/29)的同城、跨城、现场提货订单迁入对应出账日
-- 4) 按明细重算未打款表头
-- 5) 删除未打款、无明细、且非周期日的空账单(7/29、8/1、8/29 等)
--
-- 建议:先部署含「每 3 天出一期」的 API,再跑本脚本。
-- 若必须先修数据:跑完后尽快发版,避免旧「每日只收 D-3 当天」任务回刷 8/28。
--
-- 用法(生产):
-- mysql -u... -p dukang_prod < fix-winery-bills-v406.sql
-- =============================================================================
START TRANSACTION;
-- 1) 现场提货等:支付即完成但未写 completed_at
UPDATE user_order
SET completed_at = paid_at
WHERE status = 'COMPLETED'
AND pay_status = 'PAID'
AND completed_at IS NULL
AND paid_at IS NOT NULL;
-- 2) 补齐周期出账日(8/4 起至「今天北京日」能落到的最后一张)
INSERT INTO winery_bill (
bill_no, bill_date, order_count, order_amount, winery_rate, winery_amount, status, created_at
)
WITH RECURSIVE issue_days AS (
SELECT DATE('2026-08-04') AS issue_ymd
UNION ALL
SELECT DATE_ADD(issue_ymd, INTERVAL 3 DAY)
FROM issue_days
WHERE DATE_ADD(issue_ymd, INTERVAL 3 DAY) <= DATE(DATE_ADD(UTC_TIMESTAMP(), INTERVAL 8 HOUR))
)
SELECT
CONCAT('WBFIX', DATE_FORMAT(d.issue_ymd, '%Y%m%d')) AS bill_no,
TIMESTAMP(DATE_ADD(d.issue_ymd, INTERVAL -8 HOUR)) AS bill_date,
0,
0.00,
0.3000,
0.00,
'UNPAID',
TIMESTAMP(DATE_ADD(d.issue_ymd, INTERVAL -8 HOUR))
FROM issue_days d
WHERE NOT EXISTS (
SELECT 1
FROM winery_bill b
WHERE b.bill_date = TIMESTAMP(DATE_ADD(d.issue_ymd, INTERVAL -8 HOUR))
);
-- 3) 起点日及之后:从未打款账单上拿掉挂错期的明细(已打款账单因 status 过滤不会动)
DELETE i
FROM winery_bill_item i
INNER JOIN winery_bill b ON b.id = i.winery_bill_id
INNER JOIN user_order o ON o.id = i.order_id
INNER JOIN winery_bill target
ON target.bill_date = TIMESTAMP(DATE_ADD(
DATE_ADD(
DATE('2026-08-01'),
INTERVAL (FLOOR(DATEDIFF(
DATE(CONVERT_TZ(COALESCE(o.completed_at, o.paid_at), '+00:00', '+08:00')),
DATE('2026-08-01')
) / 3) + 1) * 3 DAY
),
INTERVAL -8 HOUR
))
WHERE b.status = 'UNPAID'
AND b.id <> target.id
AND o.status = 'COMPLETED'
AND o.pay_status = 'PAID'
AND o.delivery_type IN ('LOCAL', 'CROSS_CITY', 'ON_SITE_PICKUP')
AND COALESCE(o.completed_at, o.paid_at) IS NOT NULL
AND DATE(CONVERT_TZ(COALESCE(o.completed_at, o.paid_at), '+00:00', '+08:00')) >= '2026-08-01';
-- 4) 把未入账的同城/跨城/现场提货订单写入对应周期账单
INSERT INTO winery_bill_item (
winery_bill_id, order_id, order_no, delivery_type, pay_amount, winery_amount, paid_at, created_at
)
SELECT
target.id,
o.id,
o.order_no,
o.delivery_type,
o.pay_amount,
ROUND(o.pay_amount * 0.3, 2),
COALESCE(o.paid_at, o.completed_at),
UTC_TIMESTAMP(3)
FROM user_order o
INNER JOIN winery_bill target
ON target.bill_date = TIMESTAMP(DATE_ADD(
DATE_ADD(
DATE('2026-08-01'),
INTERVAL (FLOOR(DATEDIFF(
DATE(CONVERT_TZ(COALESCE(o.completed_at, o.paid_at), '+00:00', '+08:00')),
DATE('2026-08-01')
) / 3) + 1) * 3 DAY
),
INTERVAL -8 HOUR
))
LEFT JOIN winery_bill_item existing ON existing.order_id = o.id
WHERE o.status = 'COMPLETED'
AND o.pay_status = 'PAID'
AND o.delivery_type IN ('LOCAL', 'CROSS_CITY', 'ON_SITE_PICKUP')
AND COALESCE(o.completed_at, o.paid_at) IS NOT NULL
AND DATE(CONVERT_TZ(COALESCE(o.completed_at, o.paid_at), '+00:00', '+08:00')) >= '2026-08-01'
AND existing.id IS NULL;
-- 5) 按明细重算所有未打款表头(应付为 0 的周期账单也保留)
UPDATE winery_bill b
LEFT JOIN (
SELECT
winery_bill_id,
COUNT(*) AS order_count,
ROUND(SUM(pay_amount), 2) AS order_amount
FROM winery_bill_item
GROUP BY winery_bill_id
) x ON x.winery_bill_id = b.id
SET
b.order_count = IFNULL(x.order_count, 0),
b.order_amount = IFNULL(x.order_amount, 0.00),
b.winery_rate = 0.3000,
b.winery_amount = ROUND(IFNULL(x.order_amount, 0) * 0.3, 2)
WHERE b.status = 'UNPAID';
-- 6) 清掉误生成的非周期日空账单(有明细的如 7/31 保留;8/1 虽 MOD=0 但未满 3 天,仍删)
DELETE b
FROM winery_bill b
LEFT JOIN winery_bill_item i ON i.winery_bill_id = b.id
WHERE b.status = 'UNPAID'
AND i.id IS NULL
AND NOT (
DATEDIFF(DATE(CONVERT_TZ(b.bill_date, '+00:00', '+08:00')), DATE('2026-08-01')) >= 3
AND MOD(DATEDIFF(DATE(CONVERT_TZ(b.bill_date, '+00:00', '+08:00')), DATE('2026-08-01')), 3) = 0
);
COMMIT;
-- -----------------------------------------------------------------------------
-- 核对(不改数据)
-- -----------------------------------------------------------------------------
SELECT '=== 账单列表 ===' AS s;
SELECT
DATE(CONVERT_TZ(bill_date, '+00:00', '+08:00')) AS bill_ymd,
bill_no,
order_count,
order_amount,
winery_amount,
status
FROM winery_bill
ORDER BY bill_date;
SELECT '=== 明细按配送类型 ===' AS s;
SELECT delivery_type, COUNT(*) AS cnt, ROUND(SUM(pay_amount), 2) AS pay_sum
FROM winery_bill_item
GROUP BY delivery_type;
SELECT '=== 应入账但未入账(应为 0 行)===' AS s;
SELECT o.id, o.order_no, o.delivery_type, o.pay_amount,
DATE(CONVERT_TZ(COALESCE(o.completed_at, o.paid_at), '+00:00', '+08:00')) AS attr_ymd
FROM user_order o
LEFT JOIN winery_bill_item i ON i.order_id = o.id
WHERE o.status = 'COMPLETED'
AND o.pay_status = 'PAID'
AND o.delivery_type IN ('LOCAL', 'CROSS_CITY', 'ON_SITE_PICKUP')
AND i.id IS NULL;
SELECT '=== 非周期日仍在的账单(仅允许起点前已入账的,如 7/31)===' AS s;
SELECT
DATE(CONVERT_TZ(bill_date, '+00:00', '+08:00')) AS bill_ymd,
bill_no,
order_count,
order_amount,
status
FROM winery_bill
WHERE NOT (
DATEDIFF(DATE(CONVERT_TZ(bill_date, '+00:00', '+08:00')), DATE('2026-08-01')) >= 3
AND MOD(DATEDIFF(DATE(CONVERT_TZ(bill_date, '+00:00', '+08:00')), DATE('2026-08-01')), 3) = 0
)
ORDER BY bill_date;