chore(settlement): add finance bills mock seed script
CI / verify (pull_request) Has been cancelled

EOF

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-26 10:08:01 +08:00
parent da21cd3daa
commit 94d2a88581
2 changed files with 448 additions and 0 deletions
+1
View File
@@ -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",
@@ -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();
});