From 94d2a88581df4cc5883fded01238022da0406e2e Mon Sep 17 00:00:00 2001 From: jacy <18049821889@163.com> Date: Sun, 26 Jul 2026 10:08:01 +0800 Subject: [PATCH] chore(settlement): add finance bills mock seed script EOF Co-authored-by: Cursor --- server/dukang-api/package.json | 1 + server/dukang-api/prisma/seed-finance-mock.ts | 447 ++++++++++++++++++ 2 files changed, 448 insertions(+) create mode 100644 server/dukang-api/prisma/seed-finance-mock.ts diff --git a/server/dukang-api/package.json b/server/dukang-api/package.json index f3a5238..2221c65 100644 --- a/server/dukang-api/package.json +++ b/server/dukang-api/package.json @@ -12,6 +12,7 @@ "prisma:migrate": "prisma migrate dev", "prisma:validate": "prisma validate", "prisma:seed": "ts-node --transpile-only prisma/seed-v31.ts", + "prisma:seed-finance": "ts-node --transpile-only prisma/seed-finance-mock.ts", "prisma:migrate-city-partner": "ts-node --transpile-only prisma/migrate-city-partner.ts", "prisma:seed-legacy": "ts-node --transpile-only prisma/seed-prev1.ts", "prisma:upsert-super-admin": "ts-node --transpile-only scripts/upsert-super-admin.ts", diff --git a/server/dukang-api/prisma/seed-finance-mock.ts b/server/dukang-api/prisma/seed-finance-mock.ts new file mode 100644 index 0000000..83e0f70 --- /dev/null +++ b/server/dukang-api/prisma/seed-finance-mock.ts @@ -0,0 +1,447 @@ +/** + * 财务四账单 Mock 数据(不重跑全量 seed) + * 用法:cd server/dukang-api && npx ts-node --transpile-only prisma/seed-finance-mock.ts + */ +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +function startOfDay(d: Date) { + return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0); +} + +function billNo(prefix: string, suffix: string) { + return `${prefix}MOCK${suffix}`.slice(0, 32); +} + +async function main() { + console.log('Seeding finance mock bills...'); + + const store = await prisma.store.findFirst({ + where: { auditStatus: 'APPROVED' }, + orderBy: { id: 'asc' }, + }); + if (!store) throw new Error('没有门店数据,请先 pnpm db:seed'); + + const partner = await prisma.partnerAccount.findFirst({ + where: { isPrimary: 1, status: 'ACTIVE' }, + orderBy: { id: 'asc' }, + }); + if (!partner) throw new Error('没有合伙人主账号,请先 pnpm db:seed'); + + let provider = await prisma.fulfillmentProvider.findFirst({ + where: { code: { in: ['XFX', 'XIAOFEIXIA'] } }, + }); + if (!provider) { + provider = await prisma.fulfillmentProvider.findFirst({ orderBy: { id: 'asc' } }); + } + if (!provider) throw new Error('没有仓配承运商,请先 pnpm db:seed 或在仓配管理注册'); + + // 确保小飞侠有对账配置,便于物流汇总页展示 + if (!provider.pricingRulesJson || !provider.bankAccountName) { + provider = await prisma.fulfillmentProvider.update({ + where: { id: provider.id }, + data: { + settlementMethod: provider.settlementMethod ?? 'PREPAID', + prepaidBalance: Number(provider.prepaidBalance) > 0 ? provider.prepaidBalance : 5000, + bankAccountName: provider.bankAccountName || '郑州小飞侠物流有限公司', + bankName: provider.bankName || '招商银行', + bankBranch: provider.bankBranch || '郑州金水支行', + bankAccountNo: provider.bankAccountNo || '6225880000001234', + pricingRulesJson: + provider.pricingRulesJson || + JSON.stringify({ + baseBottles: 2, + baseFee: 6, + extraBottleFee: 2, + boxBottles: 6, + boxFee: 14, + }), + }, + }); + } + + const today = startOfDay(new Date()); + const yesterday = new Date(today); + yesterday.setDate(yesterday.getDate() - 1); + const dayBefore = new Date(today); + dayBefore.setDate(dayBefore.getDate() - 2); + + const thisMonthStart = new Date(today.getFullYear(), today.getMonth(), 1); + const thisMonthEnd = new Date(today.getFullYear(), today.getMonth() + 1, 0, 23, 59, 59, 999); + const prevMonthStart = new Date(today.getFullYear(), today.getMonth() - 1, 1); + const prevMonthEnd = new Date(today.getFullYear(), today.getMonth(), 0, 23, 59, 59, 999); + + // 清理旧 mock(按账单号前缀) + await prisma.logisticsBillItem.deleteMany({ + where: { logisticsBill: { billNo: { startsWith: 'LBMOCK' } } }, + }); + await prisma.logisticsPrepaidLedger.deleteMany({ + where: { logisticsBill: { billNo: { startsWith: 'LBMOCK' } } }, + }); + await prisma.logisticsBill.deleteMany({ where: { billNo: { startsWith: 'LBMOCK' } } }); + await prisma.wineryBillItem.deleteMany({ + where: { wineryBill: { billNo: { startsWith: 'WBMOCK' } } }, + }); + await prisma.wineryBill.deleteMany({ where: { billNo: { startsWith: 'WBMOCK' } } }); + await prisma.storePayout.updateMany({ + where: { storeBill: { billNo: { startsWith: 'SBMOCK' } } }, + data: { storeBillId: null }, + }); + await prisma.storeBill.deleteMany({ where: { billNo: { startsWith: 'SBMOCK' } } }); + await prisma.partnerBill.deleteMany({ where: { billNo: { startsWith: 'PBMOCK' } } }); + + // ─── 1. 门店账单(昨日未打款 + 前日已打款)─── + const storeBillUnpaid = await prisma.storeBill.create({ + data: { + billNo: billNo('SB', `${yesterday.getTime()}U`), + storeId: store.id, + billDate: yesterday, + redeemCount: 5, + redeemAmount: 1500, + settlementRate: 0.6, + payoutAmount: 900, + status: 'UNPAID', + }, + }); + const storeBillPaid = await prisma.storeBill.create({ + data: { + billNo: billNo('SB', `${dayBefore.getTime()}P`), + storeId: store.id, + billDate: dayBefore, + redeemCount: 3, + redeemAmount: 800, + settlementRate: 0.6, + payoutAmount: 480, + status: 'PAID', + paidAt: yesterday, + }, + }); + + // ─── 2. 合伙人月账单(上月待确认 + 本月已打款样例)─── + const partnerBillAwait = await prisma.partnerBill.upsert({ + where: { + partnerAccountId_periodStart: { + partnerAccountId: partner.id, + periodStart: prevMonthStart, + }, + }, + create: { + billNo: billNo('PB', `${prevMonthStart.getTime()}A`), + partnerAccountId: partner.id, + periodStart: prevMonthStart, + periodEnd: prevMonthEnd, + orderCommission: 860, + redeemCommission: 420, + totalAmount: 1280, + status: 'AWAITING_CONFIRM', + sentAt: yesterday, + }, + update: { + billNo: billNo('PB', `${prevMonthStart.getTime()}A`), + orderCommission: 860, + redeemCommission: 420, + totalAmount: 1280, + status: 'AWAITING_CONFIRM', + sentAt: yesterday, + paidAt: null, + rejectReason: null, + }, + }); + + // 本月另起一条若冲突则跳过(unique partner+period)— 用上上月做已打款 + const olderMonthStart = new Date(today.getFullYear(), today.getMonth() - 2, 1); + const olderMonthEnd = new Date(today.getFullYear(), today.getMonth() - 1, 0, 23, 59, 59, 999); + const partnerBillPaid = await prisma.partnerBill.upsert({ + where: { + partnerAccountId_periodStart: { + partnerAccountId: partner.id, + periodStart: olderMonthStart, + }, + }, + create: { + billNo: billNo('PB', `${olderMonthStart.getTime()}P`), + partnerAccountId: partner.id, + periodStart: olderMonthStart, + periodEnd: olderMonthEnd, + orderCommission: 500, + redeemCommission: 300, + totalAmount: 800, + status: 'PAID', + sentAt: olderMonthEnd, + confirmedAt: thisMonthStart, + paidAt: thisMonthStart, + }, + update: { + billNo: billNo('PB', `${olderMonthStart.getTime()}P`), + orderCommission: 500, + redeemCommission: 300, + totalAmount: 800, + status: 'PAID', + paidAt: thisMonthStart, + }, + }); + + // ─── 3. 酒厂日账单 ─── + const wineryUnpaid = await prisma.wineryBill.upsert({ + where: { billDate: yesterday }, + create: { + billNo: billNo('WB', `${yesterday.getTime()}U`), + billDate: yesterday, + orderCount: 4, + orderAmount: 4792, + wineryRate: 0.3, + wineryAmount: 1437.6, + status: 'UNPAID', + }, + update: { + billNo: billNo('WB', `${yesterday.getTime()}U`), + orderCount: 4, + orderAmount: 4792, + wineryRate: 0.3, + wineryAmount: 1437.6, + status: 'UNPAID', + paidAt: null, + }, + }); + await prisma.wineryBillItem.deleteMany({ where: { wineryBillId: wineryUnpaid.id } }); + // 用极大假 orderId,避免与真实订单冲突 + const fakeOrderBase = BigInt('900000000000000001'); + await prisma.wineryBillItem.createMany({ + data: [ + { + wineryBillId: wineryUnpaid.id, + orderId: fakeOrderBase, + orderNo: 'DKMOCK-WB-001', + deliveryType: 'LOCAL', + payAmount: 1198, + wineryAmount: 359.4, + paidAt: yesterday, + }, + { + wineryBillId: wineryUnpaid.id, + orderId: fakeOrderBase + 1n, + orderNo: 'DKMOCK-WB-002', + deliveryType: 'LOCAL', + payAmount: 1198, + wineryAmount: 359.4, + paidAt: yesterday, + }, + { + wineryBillId: wineryUnpaid.id, + orderId: fakeOrderBase + 2n, + orderNo: 'DKMOCK-WB-003', + deliveryType: 'CROSS_CITY', + payAmount: 1797, + wineryAmount: 539.1, + paidAt: yesterday, + }, + { + wineryBillId: wineryUnpaid.id, + orderId: fakeOrderBase + 3n, + orderNo: 'DKMOCK-WB-004', + deliveryType: 'LOCAL', + payAmount: 599, + wineryAmount: 179.7, + paidAt: yesterday, + }, + ], + }); + + const wineryPaid = await prisma.wineryBill.upsert({ + where: { billDate: dayBefore }, + create: { + billNo: billNo('WB', `${dayBefore.getTime()}P`), + billDate: dayBefore, + orderCount: 2, + orderAmount: 2396, + wineryRate: 0.3, + wineryAmount: 718.8, + status: 'PAID', + paidAt: yesterday, + }, + update: { + billNo: billNo('WB', `${dayBefore.getTime()}P`), + orderCount: 2, + orderAmount: 2396, + wineryRate: 0.3, + wineryAmount: 718.8, + status: 'PAID', + paidAt: yesterday, + }, + }); + + // ─── 4. 物流对账月账单(上月挂账未结 + 本月充值已扣)─── + const logisticsUnpaid = await prisma.logisticsBill.upsert({ + where: { + fulfillmentProviderId_periodStart: { + fulfillmentProviderId: provider.id, + periodStart: prevMonthStart, + }, + }, + create: { + billNo: billNo('LB', `${prevMonthStart.getTime()}U`), + fulfillmentProviderId: provider.id, + periodStart: prevMonthStart, + periodEnd: prevMonthEnd, + orderCount: 3, + bottleCount: 20, + logisticsAmount: 46, + settlementMethod: 'MONTHLY_CREDIT', + pricingSnapshotJson: JSON.stringify({ + baseBottles: 2, + baseFee: 6, + extraBottleFee: 2, + boxBottles: 6, + boxFee: 14, + }), + status: 'UNPAID', + }, + update: { + billNo: billNo('LB', `${prevMonthStart.getTime()}U`), + orderCount: 3, + bottleCount: 20, + logisticsAmount: 46, + settlementMethod: 'MONTHLY_CREDIT', + status: 'UNPAID', + paidAt: null, + }, + }); + await prisma.logisticsBillItem.deleteMany({ where: { logisticsBillId: logisticsUnpaid.id } }); + await prisma.logisticsBillItem.createMany({ + data: [ + { + logisticsBillId: logisticsUnpaid.id, + orderId: fakeOrderBase + 10n, + orderNo: 'DKMOCK-LB-001', + quantity: 6, + logisticsAmount: 14, + shippedAt: new Date(prevMonthStart.getTime() + 3 * 86400000), + }, + { + logisticsBillId: logisticsUnpaid.id, + orderId: fakeOrderBase + 11n, + orderNo: 'DKMOCK-LB-002', + quantity: 8, + logisticsAmount: 20, // 箱14 + 2瓶6 + shippedAt: new Date(prevMonthStart.getTime() + 8 * 86400000), + }, + { + logisticsBillId: logisticsUnpaid.id, + orderId: fakeOrderBase + 12n, + orderNo: 'DKMOCK-LB-003', + quantity: 6, + logisticsAmount: 14, + shippedAt: new Date(prevMonthStart.getTime() + 15 * 86400000), + }, + ], + }); + // 修正合计:14+20+14=48 + await prisma.logisticsBill.update({ + where: { id: logisticsUnpaid.id }, + data: { bottleCount: 20, logisticsAmount: 48 }, + }); + + const logisticsPaid = await prisma.logisticsBill.upsert({ + where: { + fulfillmentProviderId_periodStart: { + fulfillmentProviderId: provider.id, + periodStart: thisMonthStart, + }, + }, + create: { + billNo: billNo('LB', `${thisMonthStart.getTime()}P`), + fulfillmentProviderId: provider.id, + periodStart: thisMonthStart, + periodEnd: thisMonthEnd, + orderCount: 2, + bottleCount: 10, + logisticsAmount: 22, + settlementMethod: 'PREPAID', + pricingSnapshotJson: JSON.stringify({ + baseBottles: 2, + baseFee: 6, + extraBottleFee: 2, + boxBottles: 6, + boxFee: 14, + }), + status: 'PAID', + paidAt: yesterday, + }, + update: { + billNo: billNo('LB', `${thisMonthStart.getTime()}P`), + orderCount: 2, + bottleCount: 10, + logisticsAmount: 22, + settlementMethod: 'PREPAID', + status: 'PAID', + paidAt: yesterday, + }, + }); + await prisma.logisticsBillItem.deleteMany({ where: { logisticsBillId: logisticsPaid.id } }); + await prisma.logisticsBillItem.createMany({ + data: [ + { + logisticsBillId: logisticsPaid.id, + orderId: fakeOrderBase + 20n, + orderNo: 'DKMOCK-LB-101', + quantity: 4, + logisticsAmount: 10, + shippedAt: new Date(thisMonthStart.getTime() + 2 * 86400000), + }, + { + logisticsBillId: logisticsPaid.id, + orderId: fakeOrderBase + 21n, + orderNo: 'DKMOCK-LB-102', + quantity: 6, + logisticsAmount: 14, + shippedAt: new Date(thisMonthStart.getTime() + 5 * 86400000), + }, + ], + }); + + // 充值流水样例 + await prisma.logisticsPrepaidLedger.create({ + data: { + fulfillmentProviderId: provider.id, + type: 'RECHARGE', + amount: 5000, + balanceAfter: 5000, + remark: 'Mock 预充运费', + }, + }); + await prisma.logisticsPrepaidLedger.create({ + data: { + fulfillmentProviderId: provider.id, + type: 'DEDUCT', + amount: 22, + balanceAfter: 4978, + logisticsBillId: logisticsPaid.id, + remark: 'Mock 本月物流账单扣款', + }, + }); + await prisma.fulfillmentProvider.update({ + where: { id: provider.id }, + data: { prepaidBalance: 4978, settlementMethod: 'PREPAID' }, + }); + + console.log('Finance mock ready:', { + store: { id: store.id.toString(), name: store.name }, + partner: { id: partner.id.toString(), phone: partner.phone }, + provider: { id: provider.id.toString(), code: provider.code, name: provider.name }, + storeBills: [storeBillUnpaid.billNo, storeBillPaid.billNo], + partnerBills: [partnerBillAwait.billNo, partnerBillPaid.billNo], + wineryBills: [wineryUnpaid.billNo, wineryPaid.billNo], + logisticsBills: [logisticsUnpaid.billNo, logisticsPaid.billNo], + }); + console.log('打开 admin:财务 → 门店/合伙人/酒厂/物流对账'); +} + +main() + .catch((e) => { + console.error(e); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + });