import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; const root = dirname(dirname(fileURLToPath(import.meta.url))); const read = (path) => readFileSync(join(root, path), 'utf8'); const service = read('backend/src/operations/business-report-service.ts'); const routes = read('backend/src/routes/business-statistics.ts'); const worker = read('backend/src/tasks/worker.ts'); const migration = read('database/migrations/2026081111_m10b_business_reports.up.sql'); const panel = read('admin/src/components/BusinessReportsPanel.vue'); const navigation = read('admin/src/navigation.mjs'); for (const token of [ 'roomGrossCents', 'roomRefundCents', 'productGrossCents', 'productRefundCents', 'cleaningCostCents', 'usedMinutes', 'capacityMinutes', 'utilizationBasisPoints', 'zonedDateStart', 'sourceChecksum', 'reconciliation', 'enqueueDueAggregations' ]) assert.ok(service.includes(token), `Missing report service contract: ${token}`); assert.ok(service.includes("'PARTIALLY_REFUNDED', 'REFUNDED'")); for (const channel of ['WECHAT', 'BALANCE', 'PACKAGE', 'GROUP_BUY', 'OTHER']) { assert.ok(service.includes(`'${channel}'`), `Missing payment channel: ${channel}`); } for (const path of [ '/admin-api/reports/business', '/admin-api/reports/business/export', '/admin-api/reports/business/rebuild' ]) assert.ok(routes.includes(path), `Missing report route: ${path}`); assert.ok(routes.includes('text/csv; charset=utf-8')); assert.ok(worker.includes("['statistics.aggregate'")); assert.ok(worker.includes('reports.enqueueDueAggregations()')); assert.ok(migration.includes('qipai_business_daily_summaries')); for (const permission of ['report.read', 'report.export', 'report.manage']) assert.ok(migration.includes(permission)); for (const label of ['今日', '近 7 日', '全部授权门店', '重算日汇总', '经营明细']) assert.ok(panel.includes(label)); assert.ok(navigation.includes("menuKey: 'reports'")); console.log('PASS: M10-B timezone reporting, refunds, usage, channels, daily reconciliation and export gates are present.');