コンテンツにスキップ

請求書 PDF の事前生成と初回 DL レイテンシ解消

  • 作成日: 2026-05-21
  • 目的: storefront 上の請求書ダウンロード初回レイテンシ(staging 実測 20 秒超)を、生成タイミングを注文確定時点へ前倒すことで構造的に解消する。

背景

  • ritsOrderInvoiceReportInvoiceService.getOrIssueInitialInvoice を経由し、保存済み PDF が無いと リクエストの内側で Playwright Chromium レンダリングを同期実行する仕組みになっている(packages/plugins/src/standard-extensions/report/report-invoice.service.ts)。
  • Chromium cold-start と networkidle 待ちで 20 秒の sync timeout に届く事例があり、staging では UI が status=GENERATING メッセージへフォールバックする挙動が常態化していた。
  • 元々は OrderPlacedEvent で invoice job を事前 enqueue する設計だったが、commit 9e30553a7(SMILE 統合 PR)で「初回 DL 時の同期発行」へ切り替えられ、事前生成が停止していた。

決定

  • 注文確定 (OrderPlacedEvent) で ReportQueueService.enqueue により invoice job を事前投入する。worker process が請求書 PDF を非同期に生成し、R2 へ保存する。
  • production: 専用 worker process group が処理する(apps/vendure-server/fly.toml)。
  • staging: VENDURE_RUN_JOB_QUEUE=true の app machine 内で in-process worker が処理する(apps/vendure-server/fly.staging.toml)。
  • 顧客の DL リクエストは保存済み PDF を返すだけの fast path になる。
  • 既存仕様 2026-04-invoice-availability-regardless-of-payment-method.md の「決済方法に依存せず請求書発行可能」要件はそのまま維持し、本決定はそのうちの 発行タイミング を前倒す変更に限定する。
  • 同期発行 fallback (ReportInvoiceService.getOrIssueInitialInvoice の 20s sync wait) は救済路として残し、queue 未処理/backfill 未済の注文でも UI が壊れないようにする。

受け入れ条件

  • OrderPlacedEvent が発火した注文について、worker が rits-report-generation queue で type: invoice job を完了させ、report_record に新規レコードを作成すること。
  • storefront の ritsOrderInvoice クエリが、通常の確定済み注文に対して 1 回目から status=READY と署名 URL を返却すること(worker 処理が完了済みであることが前提)。
  • backfill スクリプト apps/vendure-server/scripts/backfill-invoice-reports.ts で、対象状態(既定: PaymentSettled 以降)の既存注文について、report_record に未生成の invoice レコードを生成できること。
  • Sentry Metricsvendure.report.job.enqueuedtype=invoice, from_state=placed, to_state=placed が記録され、事前生成 lane が止まっていないかを継続観測できること。

非対象

  • Chromium 以外のレンダラへの既定切り替え(REPORT_RENDERER=cloudflare)。Cloudflare API token の Browser Rendering 権限付与が前提となるため、本仕様には含めず別 issue で扱う。
  • 配送帳票 (delivery / label) の発行タイミングと方式。
  • 帳票テンプレートの内容変更。
  • 再発行・訂正フロー。

運用

  • backfill 実行例(staging):
flyctl ssh console --app ritsubi-ecommerce-staging \
  -C "node --enable-source-maps dist/scripts/backfill-invoice-reports.js --limit 200"

ローカルから tsx 経由で実行する場合:

SECRETS_ENV=staging SECRETS_SERVICE=vendure ./scripts/ops/with-env.sh -- \
  pnpm -C apps/vendure-server exec tsx scripts/backfill-invoice-reports.ts --dry-run

関連

  • docs/specifications/2026-04-invoice-availability-regardless-of-payment-method.md
  • packages/plugins/src/standard-extensions/report/report-order.listener.ts
  • packages/plugins/src/standard-extensions/report/report-invoice.service.ts
  • packages/plugins/src/standard-extensions/report/report-queue.service.ts
  • apps/vendure-server/scripts/backfill-invoice-reports.ts