Skip to content

レシートの差額を「調整行」でなく各品目へ金額比で配分する - #16

Merged
yamk12nfu merged 5 commits into
mainfrom
feature/receipt-tax-distribution
Aug 1, 2026
Merged

yamk12nfu merged 5 commits into
mainfrom
feature/receipt-tax-distribution

Conversation

@yamk12nfu

@yamk12nfu yamk12nfu commented Aug 1, 2026 •

Copy link
Copy Markdown
Owner

背景

税別レシート(品目が税抜表記、合計だけ税込)を実運用したところ、差額を「調整(税・割引等)」1行にまとめる方式に2つの問題が出た。

  1. 手直しが毎回発生する — その行の負担区分が既定の折半になるため、片方の個人品目(酒・化粧品など)に掛かった消費税まで折半されてしまう
  2. 正しい値を判断できない — 1行にまとまっているので「いくらを誰に寄せれば正しいのか」が分からず、直すには自分で按分を計算するしかない

要件定義に代替案として書いていた TBD-003「品目金額比での自動按分」 へ切り替える。

変更内容

差額を各品目の金額比で配分して価格に上乗せする。税はその品目の負担区分にそのまま従う(酒が自分100%なら酒の税も自分100%)。

牛肉600円(自分100%) + にんじん400円(折半)、税込1,100円のレシートの場合:

これまで これから
品目 牛肉600 / にんじん400 / 調整100 牛肉660 / にんじん440
税の行き先 100円が折半 牛肉の税60円は自分100%、にんじんの税40円は折半
手作業 毎回、調整行の負担区分を直す なし
  • withAdjustmentItem を distributeDifference に差し替え。端数は累積の目標値から逆算するので、配分後の品目合計は必ずレシートの支払額と一致する(品目ごとに割合を掛けて丸めると1円ずれる)
  • マイナスの差額(総額からの値引き)も配分できるようになった。調整行方式では「マイナスの品目」が作れず adjustmentSkipped で諦めていたケース
  • 配分しても品目が増えないので、AI由来の品目の上限を99件から100件(expenses.save の上限)に戻した
  • AIプロンプトの「税込金額を返す」指示をやめ、印字どおり返させる。税別レシートには税込が印字されておらず、AIが自分で税を計算しようとして不安定になるため
  • 配分したときは品目リストの上に「消費税などの差額を各品目に配分しました」とグレーで表示(金額がレシート表記と変わるので黙って変えない)
  • docs/ 3点を更新し、TBD-003 を経緯ごと「決着」として記録

既知の限界

金額比の按分は税率が一律である前提のため、軽減税率8%と標準税率10%が混在するレシートでは食品側に税が数円〜数十円多く乗る。従来の「税を全部折半」よりは不公平が小さいので、税率別按分(AIに品目ごとの税率を返させる)は残課題として docs/verification-checklist.md に確認項目を残した。

確認

  • npx tsc --noEmit
  • npx vitest run — 205件パス
  • npx eslint . — エラーなし
  • npx next build
  • 実機で実物の税別レシートを読み取る(要マージ後)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • 新機能

    • レシート品目と支払額の差額を、調整行ではなく各品目へ自動配分するようになりました。
    • 税抜表記の品目価格を、印字された金額のまま扱えるようになりました。
    • 最大100件の品目を処理できるようになりました。
  • 改善

    • 配分後の品目合計が支払額と一致するようになりました。
    • 配分できない場合は処理を中止し、確認を促します。
    • 配分結果や手入力への切り替えに応じて通知の内容と色を表示します。

税別レシート(品目が税抜表記、合計だけ税込)を実運用したところ、差額を
「調整(税・割引等)」1行にまとめる方式に2つの問題が出た。

1. その行の負担区分が既定の折半になるため、片方の個人品目(酒・化粧品など)
   に掛かった消費税まで折半されてしまい、毎回手で直す必要があった
2. 1行にまとまっているので「いくらを誰に寄せれば正しいのか」を判断できず、
   直すには自分で按分を計算するしかなかった

要件定義に代替案として書いていた TBD-003 の「品目金額比での自動按分」へ
切り替える。差額を各品目の金額比で配分して価格に上乗せするので、税はその
品目の負担区分にそのまま従う(酒が自分100%なら酒の税も自分100%)。

- withAdjustmentItem を distributeDifference に差し替え。端数は累積の目標値
  から逆算するので、配分後の品目合計は必ずレシートの金額と一致する
- マイナスの差額(総額からの値引き)も配分できるようになった。調整行では
  「マイナスの品目」が作れず諦めていたケース
- 品目が増えないので AI 由来の品目の上限を99件から100件に戻す
- AIプロンプトの「税込金額を返す」指示をやめ、印字どおり返させる。税別レシート
  には税込が印字されておらず、AIが自分で税を計算しようとして不安定になるため
- 配分したときは画面に「消費税などの差額を各品目に配分しました」と一言出す
  (品目の金額がレシート表記と変わるため)

既知の限界として、金額比の按分は税率が一律である前提のため、軽減税率8%と
標準税率10%が混在するレシートでは食品側に税が数円〜数十円多く乗る。従来の
「税を全部折半」よりは不公平が小さいので、税率別按分は残課題とした。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 1, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
warikapp Ignored Ignored Preview Aug 1, 2026 6:19pm

@coderabbitai

coderabbitai Bot commented Aug 1, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@yamk12nfu, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 24 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6377f887-cf4a-410a-a6bb-6d2692052c14

📥 Commits

Reviewing files that changed from the base of the PR and between 6def9b1 and 4362d8f.

📒 Files selected for processing (3)
  • app/(app)/expenses/new/receipt/receipt-client.tsx
  • lib/receipt.test.ts
  • lib/receipt.ts

Walkthrough

レシートの品目価格を印字金額として扱い、合計との差額を既存品目へ金額比で配分する処理へ変更しました。配分状態を正規化結果と画面通知に反映し、テストと仕様文書を更新しました。

Changes

レシート差額配分

Layer / File(s) Summary
AI価格抽出契約
convex/ai/schema.ts, convex/ai/types.ts
品目価格を印字金額として抽出する説明へ変更しました。税抜金額を税込金額へ再計算しない指示を追加しました。
差額配分と正規化
lib/receipt.ts, lib/receipt.test.ts
調整行の追加を削除し、差額を既存品目へ金額比で配分する distributeDifference を追加しました。配分不能条件、丸め、値引き、100件上限を検証します。
配分状態の画面通知
convex/receipts.ts, app/(app)/expenses/new/receipt/receipt-client.tsx
配分成功、配分省略、手入力切り替えの通知に text と tone を設定しました。通知のtoneに応じて表示色を変更しました。
仕様と検証文書
docs/implementation-plan.md, docs/requirements.md, docs/verification-checklist.md
印字価格、品目への金額比配分、合計一致、配分不能条件を文書化しました。税率混在時の配分誤差を既知の制限として記録しました。

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ReceiptAI
  participant normalizeParsedReceipt
  participant distributeDifference
  participant ReceiptClient
  ReceiptAI->>normalizeParsedReceipt: 抽出した品目と支払額
  normalizeParsedReceipt->>distributeDifference: 品目と合計金額
  distributeDifference-->>normalizeParsedReceipt: 配分済み品目と状態
  normalizeParsedReceipt-->>ReceiptClient: 正規化済みレシート
  ReceiptClient-->>ReceiptClient: toneに応じた通知表示
Loading

Possibly related PRs

Poem

うさぎが差額を品目へ配る
丸めた合計をぴたりと揃える
黄色は警告、青は情報
レシートの通知が跳ねる
月夜も検証を見守る

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed タイトルは、調整行から各品目への金額比配分への変更というプルリクエストの主要な変更を明確に示しています。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/receipt-tax-distribution

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/`(app)/expenses/new/receipt/receipt-client.tsx:
- Around line 438-441: Update the notice element rendered near NOTICE_TONE_CLASS
to expose dynamic messages to assistive technology: assign role="alert" for warn
notices and role="status" for info notices, preserving the existing styling and
notice.text rendering.

In `@docs/implementation-plan.md`:
- Around line 888-895: docs/implementation-plan.md
の実地確認記録で、現在の金額比配分方式と矛盾する「調整(税・割引等)」行の自動追加という記述を更新してください。該当する実物レシート確認記録は、品目への金額比配分結果を期待結果として記載するか、旧方式の履歴であることを明示し、現行仕様と誤認されないようにしてください。

In `@docs/requirements.md`:
- Around line 187-194:
要件定義でAI抽出値とreceipts.parseの正規化後レスポンスを明確に分離し、items[].priceがrawでは印字額、正規化後は配分結果の価格を示すことを記載してください。API(SR-001)のレスポンス契約にdistributed、distributionSkipped、sourceItemCountを追加し、配分後の価格の意味と品目合計がtotal_amountに一致する条件を明記してください。distributeDifferenceでV-403の上限超過により配分を省略した場合もdistributionSkipped等で記録する仕様に合わせ、normalizeParsedReceiptおよびreceipt-client.tsxのproducer-consumer契約と整合させてください。
- Line 526: docs/requirements.md の「TBD-003 の決着」見出しを、親見出し「8.
未確定事項一覧」の直下に適切な階層となるよう `###` レベルへ変更し、他の内容は変更しないでください。
- Around line 520-535: Synchronize the TBD-003 status and evidence across
docs/requirements.md lines 520-535, docs/implementation-plan.md lines 895-895,
and docs/verification-checklist.md lines 224-230: if the tax-exclusive receipt
was not actually verified, remove the 2026-08-02 completion date and operational
results from all three documents; otherwise, retain them only with the same
verification basis and date recorded consistently in each document.

In `@docs/verification-checklist.md`:
- Around line 224-230:
docs/verification-checklist.md内で精算端数処理に付与されているTBD-003を、未使用の別IDへ変更し、同項目を参照する関連文書のリンクや記載も新しいIDに更新してください。レシート差額配分の完了済みTBD-003は変更せず、各未解決事項が一意の識別子で追跡できる状態を保ってください。
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fb6d57da-b072-4fb6-9043-45761d69f786

📥 Commits

Reviewing files that changed from the base of the PR and between e3297c3 and 78714c0.

📒 Files selected for processing (9)
  • app/(app)/expenses/new/receipt/receipt-client.tsx
  • convex/ai/schema.ts
  • convex/ai/types.ts
  • convex/receipts.ts
  • docs/implementation-plan.md
  • docs/requirements.md
  • docs/verification-checklist.md
  • lib/receipt.test.ts
  • lib/receipt.ts

Comment on lines +438 to +441
<p
className={`rounded-lg border p-3 text-sm ${NOTICE_TONE_CLASS[notice.tone]}`}
>
{notice.text}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

差額通知を支援技術にも通知してください。

notice は編集画面への遷移後に挿入されますが、<p> にライブ領域がありません。スクリーンリーダー利用者が、金額の変更や確認要求を受け取れない可能性があります。warn は role="alert"、info は role="status" などの役割を設定してください。

修正例
             <p
+              role={notice.tone === "warn" ? "alert" : "status"}
               className={`rounded-lg border p-3 text-sm ${NOTICE_TONE_CLASS[notice.tone]}`}
             >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<p
className={`rounded-lg border p-3 text-sm ${NOTICE_TONE_CLASS[notice.tone]}`}
>
{notice.text}
<p
role={notice.tone === "warn" ? "alert" : "status"}
className={`rounded-lg border p-3 text-sm ${NOTICE_TONE_CLASS[notice.tone]}`}
>
{notice.text}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`(app)/expenses/new/receipt/receipt-client.tsx around lines 438 - 441,
Update the notice element rendered near NOTICE_TONE_CLASS to expose dynamic
messages to assistive technology: assign role="alert" for warn notices and
role="status" for info notices, preserving the existing styling and notice.text
rendering.

Comment on lines +888 to +895
6. **AI由来の品目数(`sourceItemCount`)が0なら**「レシートを読み取れませんでした」(レシート以外・不鮮明な画像)。成功ログもこの判定の後に出す
- [x] `lib/receipt.ts`(**純粋関数。AI呼び出しと切り分けてテストする**。`lib/settlement.ts` と同じ方針):
- AIの返り値を `expenses.save` の制約(V-402 / V-403)に均す(名前50字・金額1〜9,999,999の整数・数量1〜999、保存できない行は捨てる、品目は99件まで)
- AIの返り値を `expenses.save` の制約(V-402 / V-403)に均す(名前50字・金額1〜9,999,999の整数・数量1〜999、保存できない行は捨てる、品目は100件まで)
- **数量は品目名に畳み込んで `quantity` は常に1にする**(「牛乳 ×3」)。理由: (1) `price` は「その行の合計金額」なので `price × quantity` にすると数量ぶん二重に効く、(2) 仕分けUIに数量の入力欄が無く(Phase 5の設計)、残すと画面に出ない値が合計に効いてユーザーが読み取り誤りを確認・修正できない
- AIが `price` を単価で返してしまった場合の救済: 行合計として足すと `total_amount` に合わず、`price × quantity` なら一致するときは単価とみなして行合計に直す(どちらとも判定できなければ指示どおり行合計として扱い、ズレは調整行が吸収する)
- 品目合計 ≠ `total_amount` のとき、差額を品目 `調整(税・割引等)` として追加(負担区分の初期値は画面側の既定=折半)
- ⚠️ **差額がマイナス(品目合計 > 合計金額)のとき、または上限9,999,999円を超えるときは調整行を作らない**。金額は「1円以上9,999,999円以下の整数」(V-403)で、そのままでは `expenses.save` が必ず弾くため。代わりに `adjustmentSkipped: true` を返し、画面が「金額を確認してください」と促す(TBD-003で運用しながら見直す)
- AIが `price` を単価で返してしまった場合の救済: 行合計として足すと `total_amount` に合わず、`price × quantity` なら一致するときは単価とみなして行合計に直す(どちらとも判定できなければ指示どおり行合計として扱い、ズレは差額の配分が吸収する)
- 品目合計 ≠ `total_amount` のとき、差額を**各品目へ金額比で配分**する(`distributeDifference`)。端数は「累積の目標値との差」で決めるので、配分後の品目合計は必ず `total_amount` と一致する
- ⚠️ **配分すると1円未満に潰れる品目が出るときは配分しない**。金額は「1円以上9,999,999円以下の整数」(V-403)で、そのままでは `expenses.save` が必ず弾くため。代わりに `distributionSkipped: true` を返し、画面が「金額を確認してください」と促す
- 📌 **当初は差額を「調整(税・割引等)」1行にまとめる実装だった**(要件の初版)。税別レシートの実運用でこの行の負担区分が既定の折半になり、個人品目に掛かった税まで折半されてしまうことが分かったため、TBD-003 の代替案「金額比の自動按分」へ変更した(2026-08-02)。詳細と既知の限界(軽減税率の混在)は `docs/requirements.md > TBD-003 の決着` を参照

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

旧方式の実地確認記録を新しい配分方式に合わせて更新してください。

Line 888-895は調整行方式を削除したと記録しています。しかしLine 927は、実物レシートで「調整(税・割引等)」行が自動追加されたと記録しています。この文書は旧方式を現在の期待結果として扱う状態です。Line 927を金額比配分の結果へ更新するか、変更前の履歴として明記してください。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/implementation-plan.md` around lines 888 - 895,
docs/implementation-plan.md
の実地確認記録で、現在の金額比配分方式と矛盾する「調整(税・割引等)」行の自動追加という記述を更新してください。該当する実物レシート確認記録は、品目への金額比配分結果を期待結果として記載するか、旧方式の履歴であることを明示し、現行仕様と誤認されないようにしてください。

Comment thread docs/requirements.md
Comment on lines +187 to +194
| items[].price | int | 必須 | レシートに印字された金額(円)。税抜表記ならそのまま税抜。値引きはその品目に反映 |
| items[].quantity | int | 必須 | 数量(既定1) |

**処理フロー**:
1. ユーザーが画像を撮影/選択 → クライアントで縮小・圧縮
2. 画像を Convex File Storage へアップロード(アップロードURLはサーバー関数で発行)
3. API(SR-001)がAIプロバイダへ画像+抽出プロンプト(JSONスキーマ指定)を送信
4. 応答JSONをスキーマ検証。品目合計 ≠ total_amount の場合、差額を「調整(税・割引等)」品目として自動追加(初期負担区分: 折半)
4. 応答JSONをスキーマ検証。品目合計 ≠ total_amount の場合、差額(税別レシートの消費税、総額からの値引きなど)を**各品目へ金額比で配分**する。配分後の品目合計は total_amount と必ず一致する。配分すると1円未満に潰れる品目が出る場合のみ配分せず、画面で金額の確認を促す

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

AI抽出値とreceipts.parseのレスポンス契約を分離してください。

Line 187-188はitems[].priceをレシート印字額として定義しています。一方、convex/receipts.tsはnormalizeParsedReceiptの結果を返し、distributeDifferenceがitems[].priceを変更します。API仕様のLine 208はレスポンスを「上記出力項目のJSON」とだけ記載しています。

この記述では、正規化後の価格を印字価格として扱う実装が発生します。raw AIレスポンスと正規化後のレスポンスを分離し、distributed、distributionSkipped、sourceItemCount、配分後の価格意味を明記してください。V-403上限超過による配分省略も記録してください。

提供されたconvex/receipts.ts、lib/receipt.ts、receipt-client.tsxのproducer-consumer契約に基づきます。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/requirements.md` around lines 187 - 194,
要件定義でAI抽出値とreceipts.parseの正規化後レスポンスを明確に分離し、items[].priceがrawでは印字額、正規化後は配分結果の価格を示すことを記載してください。API(SR-001)のレスポンス契約にdistributed、distributionSkipped、sourceItemCountを追加し、配分後の価格の意味と品目合計がtotal_amountに一致する条件を明記してください。distributeDifferenceでV-403の上限超過により配分を省略した場合もdistributionSkipped等で記録する仕様に合わせ、normalizeParsedReceiptおよびreceipt-client.tsxのproducer-consumer契約と整合させてください。

Comment thread docs/requirements.md
Comment on lines +520 to +535
| ~~TBD-003~~ | F-003 | ~~税・割引の「調整行」方式(デフォルト折半)で実運用上問題ないか。代替案: 品目金額比での自動按分~~ **決着(2026-08-02)**: 税別レシートの運用で問題が出たため代替案(金額比の自動按分)へ変更。理由は下記 | 二人で運用確認 | 完了 |
| TBD-004 | F-010 | iOSアプリ化の方式(PWA止まり / Capacitor / ネイティブ)と時期 | 開発者 | 公開検討時 |
| TBD-005 | F-008 | 仕分けAI提案の実装時期(MVP後の最初の改善候補) | 開発者 | MVP完成後 |
| TBD-006 | 6.1 | Gemini切り替え時のプロンプト・スキーマ互換性検証(構造化出力の指定方法がプロバイダ間で異なる) | 開発者 | 切り替え時 |
| TBD-007 | F-002 | 世帯からの退出・世帯削除・アカウント削除のフロー詳細(MVPでは設定画面に最小限の導線のみ) | 開発者 | 実装時 |

#### TBD-003 の決着(2026-08-02)

税別レシート(品目が税抜表記、合計だけ税込)を実運用したところ、調整行方式に2つの問題が出た。

1. **手直しが毎回発生する** — 差額(消費税ぶん)が「調整(税・割引等)」1行になり、その負担区分が既定の折半になる。片方の個人品目(酒・化粧品など)が金額の大半を占めるレシートでは、その品目に掛かった税まで折半されてしまうため、毎回この行の負担区分を手で変える必要があった
2. **正しい値を判断できない** — 1行にまとまっているので「いくらを誰に寄せれば正しいのか」がユーザーには分からず、直すには自分で按分を計算するしかなかった

代替案の**金額比による自動按分**に切り替えた。差額を各品目の金額比で配分して品目価格に上乗せするので、税は**その品目の負担区分にそのまま従う**(酒が自分100%なら酒の税も自分100%)。手直しも判断も不要になる。

**既知の限界**: 金額比の按分は税率が一律であることを前提とするため、軽減税率(8%)と標準税率(10%)が混在するレシートでは、食品側に税が数円〜数十円多く乗る。「税を全部折半」だった従来より不公平は小さいが、完全に正確ではない。正確にするにはAIに品目ごとの税率も返させて税率グループごとに按分する必要があり、AIスキーマ・プロンプトの変更を伴うため見送った(必要になれば再検討する)。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repo files likely relevant =="
git ls-files | rg '(^|/)(package\.json|README\.md|README\.html|docs/(requirements|implementation-plan|verification-checklist)\.md)$' || true

echo
echo "== docs/requirements around TBD-003 =="
sed -n '505,550p' docs/requirements.md 2>/dev/null || true

echo
echo "== docs/implementation-plan around TBD-003 =="
sed -n '885,900p' docs/implementation-plan.md 2>/dev/null || true

echo
echo "== docs/verification-checklist around TBD-003 =="
sed -n '218,235p' docs/verification-checklist.md 2>/dev/null || true

echo
echo "== search for PR purpose and receipt verification text =="
rg -n "税別|税別レシート|実機確認|TBD-003|実運用|2026-08-02|検証|PR" -i README.md docs package.json 2>/dev/null | head -200 || true

Repository: yamk12nfu/warikapp

Length of output: 30409


🌐 Web query:

github.com yamk12nfu warikapp docs requirements TBD-003 2026-08-02

💡 Result:

There is no public evidence or authoritative technical documentation linking a repository named "warikapp" under the GitHub account "yamk12nfu" to a requirements document designated as "TBD-003" for August 2, 2026. An analysis of the provided terms indicates that "TBD-003" is a generic placeholder or identifier used in unrelated contexts, including: - Chinese group standard T/TBD 003-2024 for fertilizer product sampling specifications [1][2]. - An OEM part number for Honda Accord vehicle components [3]. - A medical product code for antibiotic sensitivity discs [4]. - A track identifier for record labels [5][6]. - A placeholder reference within NASA flight software documentation [7]. The GitHub user "yamk12nfu" exists and manages various repositories related to AI agents, automation, and development utilities [8], but none of these repositories are publicly titled "warikapp," nor is there any associated "TBD-003" documentation linked to this user's profile [8][9][10][11].

Citations:


TBD-003 の完了日と実機確認結果を同期させてください。

文書全体に 2026-08-02 の決着・実運用結果が書かれていますが、PRの説明では税別レシートの実機確認を未実施にしています。この差異は運用記録を信用できなくします。確認結果がないなら 2026-08-02 の完了日時・実運用経過を削除し、確認済みの場合は、その根拠や確認日時を3文書で揃えてください。

  • docs/requirements.md#L520-L535
  • docs/implementation-plan.md#L895-L895
  • docs/verification-checklist.md#L224-L230
🧰 Tools
🪛 markdownlint-cli2 (0.23.1)

[warning] 526-526: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4

(MD001, heading-increment)

📍 Affects 3 files
  • docs/requirements.md#L520-L535 (this comment)
  • docs/implementation-plan.md#L895-L895
  • docs/verification-checklist.md#L224-L230
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/requirements.md` around lines 520 - 535, Synchronize the TBD-003 status
and evidence across docs/requirements.md lines 520-535,
docs/implementation-plan.md lines 895-895, and docs/verification-checklist.md
lines 224-230: if the tax-exclusive receipt was not actually verified, remove
the 2026-08-02 completion date and operational results from all three documents;
otherwise, retain them only with the same verification basis and date recorded
consistently in each document.

Comment thread docs/requirements.md
| TBD-006 | 6.1 | Gemini切り替え時のプロンプト・スキーマ互換性検証(構造化出力の指定方法がプロバイダ間で異なる) | 開発者 | 切り替え時 |
| TBD-007 | F-002 | 世帯からの退出・世帯削除・アカウント削除のフロー詳細(MVPでは設定画面に最小限の導線のみ) | 開発者 | 実装時 |

#### TBD-003 の決着(2026-08-02)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

見出しレベルを###に修正してください。

Line 526の#### TBD-003 の決着は、## 8. 未確定事項一覧の直下で見出しレベルを2段階増やしています。markdownlintのMD001違反です。

修正例
-#### TBD-003 の決着(2026-08-02)
+### TBD-003 の決着(2026-08-02)

静的解析(markdownlint-cli2のMD001)に基づきます。

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#### TBD-003 の決着(2026-08-02)
### TBD-003 の決着(2026-08-02)
🧰 Tools
🪛 markdownlint-cli2 (0.23.1)

[warning] 526-526: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4

(MD001, heading-increment)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/requirements.md` at line 526, docs/requirements.md の「TBD-003
の決着」見出しを、親見出し「8. 未確定事項一覧」の直下に適切な階層となるよう `###` レベルへ変更し、他の内容は変更しないでください。

Source: Linters/SAST tools

Comment on lines +224 to +230
- [x] ~~`[!!]` 税・割引の「調整行(既定:折半)」方式が実運用で妥当か(TBD-003)~~ → **妥当でなかったので金額比の自動按分に変更した(2026-08-02)**。経緯は `docs/requirements.md > TBD-003 の決着`
- [ ] `[!!]` 軽減税率が混在するレシートで按分のズレが許容範囲か(TBD-003の残課題)
- 仕様参照: `docs/requirements.md > TBD-003 の決着`「既知の限界」
- 実装: `lib/receipt.ts > distributeDifference`
- 観測事実: 金額比の按分は税率が一律である前提。食品8%と酒・日用品10%が混ざると、食品側に税が数円〜数十円多く乗る
- 前提条件: 軽減税率対象(食品)と標準税率(酒・日用品)が混在する税別レシートで運用する
- 期待結果: ズレが二人にとって気にならない範囲に収まっている。気になるならAIに品目ごとの税率を返させて税率グループごとに按分する(AIスキーマ・プロンプトの変更が必要)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

TBD-003 の識別子重複を解消してください。

Line 224はレシート差額配分のTBD-003を完了にしています。しかしLine 217は精算端数処理にもTBD-003を使用しています。精算端数処理の項目を別IDへ変更し、関連文書の参照も更新してください。同じIDでは未解決事項を正しく追跡できません。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/verification-checklist.md` around lines 224 - 230,
docs/verification-checklist.md内で精算端数処理に付与されているTBD-003を、未使用の別IDへ変更し、同項目を参照する関連文書のリンクや記載も新しいIDに更新してください。レシート差額配分の完了済みTBD-003は変更せず、各未解決事項が一意の識別子で追跡できる状態を保ってください。

## 端数のテストが何も検証していなかった

「端数が出ても合計が合う」テストに `[101,101,101] → 333円` を使っていたが、
333 × 101 ÷ 303 = 111 でちょうど割り切れるため、繰り上がりが1回も起きて
いなかった。`[1,1,1] → 10円` に差し替え、`[3,4,3]` になることを直接確かめる
(品目ごとに丸めると3円ずつで9円にしかならないケース)。
品目10件での誤差の散り方、上限9,999,999円ちょうど、上限超過の各境界も追加。

## 配分の入力ガード

- 合計金額が整数でないときは配分しない。累積の目標値を丸める前提が崩れ、
  配分後の合計が totalAmount と一致しなくなるため(呼び出し元は整数に丸めて
  から渡すが、関数単体の契約としても保証する)
- 品目合計が Infinity になる入力を弾く。あわせて配分後の判定を
  「範囲内か」から「整数かつ範囲内か」に変える。比較演算は NaN に対して常に
  false を返すので、範囲だけを見ていると NaN の金額が素通りしていた
- 上記の検査を「差額0なら何もしない」より前に置く。後ろに置くと、0円どうし・
  NaN どうしのような壊れた入力を「正常」として通してしまう

いずれも normalizeParsedReceipt 経由では到達しない入力だが、
distributeDifference は export しているので単体で成立させる。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@yamk12nfu

Copy link
Copy Markdown
Owner Author

Codexレビュー対応

指摘6件をトリアージし、このPRのスコープ内の2件(#1・#2・#6)を ced20e4 で対応した。

対応した

テストの穴(指摘#6) — これが一番効いた指摘。「端数が出ても合計が合う」テストに [101,101,101] → 333円 を使っていたが、333 × 101 ÷ 303 = 111 でちょうど割り切れるため繰り上がりが1回も起きていなかった。端数処理を検証したつもりで何も検証できていない状態。

[1,1,1] → 10円 に差し替え、[3,4,3] になることを直接確かめるようにした(品目ごとに丸めると3円ずつで9円にしかならないケース)。あわせて品目10件での誤差の散り方、上限9,999,999円ちょうど、上限超過の境界も追加。

入力ガード(指摘#1・#2) — どちらも normalizeParsedReceipt 経由では到達しない入力だが、distributeDifference は export しているので関数単体で成立させた。

  • 合計金額が整数でないときは配分しない(累積の目標値を丸める前提が崩れる)
  • 品目合計が Infinity になる入力を弾き、配分後の判定を「範囲内か」から「整数かつ範囲内か」に変更。比較演算は NaN に対して常に false を返すので、範囲だけでは NaN の金額が素通りしていた
  • 上記の検査を「差額0なら何もしない」より前に移動。後ろだと0円どうし・NaN どうしの壊れた入力を「正常」として通してしまう

テストは 205件 → 211件。

別issueに切り出した

いずれもこのPRが持ち込んだ挙動ではなく以前から存在するものなので、スコープを分けた。

指摘の評価

按分ロジックそのものは問題なしと判断。Codexも「production inputs では配分後の合計が totalAmount と必ず一致する」と認めている(指摘#1の前段)。

差額0の場合の早期リターンが、合計だけを見て各品目の妥当性を見ていなかった。
1.5円 + 0.5円 = 2円 のような保存できない品目が「差額0だから正常」として
素通りし、distributed も skipped も false で返っていた。

品目1件ずつを検査する isSavableItem を足し、入力の検査でも配分後の検査でも
使う(金額 V-403: 1円以上9,999,999円以下の整数 / 数量: 1以上の整数)。
Number.isInteger を含むので NaN も弾ける。

あわせて、検査が「差額0なら何もしない」より前にあることを固定するテストを
追加した。前回のレビューで「順序を戻しても通ってしまう」と指摘された穴で、
差額0のケースを使わないと順序を検証できない。

テストが実際に効いていることを変異で確認した:
- isSavableItem の検査を外す → 2件失敗
- 検査を等価判定の後ろに移す    → 2件失敗

いずれも normalizeParsedReceipt 経由では sanitizeItem が先に均すので到達
しないが、distributeDifference は export しているので単体で成立させる。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@yamk12nfu

Copy link
Copy Markdown
Owner Author

Codex再レビュー対応 (6395d37)

指摘#4(新規)と#2の残り: 等価判定の抜け道

差額0の早期リターンが合計だけを見て各品目の妥当性を見ていなかった。1.5円 + 0.5円 = 2円 のような保存できない品目が「差額0だから正常」として素通りし、distributed も skipped も false で返っていた。

品目1件ずつを検査する isSavableItem を足し、入力の検査でも配分後の検査でも使うようにした(金額 V-403: 1円以上9,999,999円以下の整数 / 数量: 1以上の整数)。Number.isInteger を含むので NaN も同時に弾ける。前回ばらばらに足したガードがこれ1つに畳まれ、判定基準が1箇所になった。

指摘#3: テストが特定のガードを分離できていない

「非整数の totalAmount」のテストは正当な指摘。base=100 / total=100.4 と不一致なケースを使っていたため、ガードを等価判定の後ろに戻しても通ってしまう。差額0のケースでないと順序は検証できないので、[1.5, 0.5] → 2 などを使うテストを追加した。

追加したテストが実際に効いていることを変異で確認済み:

変異 結果
isSavableItem の検査を外す 2件失敗
検査を等価判定の後ろに移す 2件失敗

NaN/Infinity のテストについては指摘のとおりで、!Number.isFinite(base) を単独で外しても後段の整数検査が拾うため、あのガードだけを分離することはできない。ただしテストは「この入力は配分してはならない」という要求される振る舞いを固定しており、どのガードが発火するかは実装の自由度だと考えて、そのままにした。!Number.isFinite(base) は意図を示す防御として残している。

指摘#2: 退行なし

normalizeParsedReceipt 経由の実レシートに退行がないことは、こちらでも全212件のテストと本番ビルドで確認した。


テストは 211件 → 212件(distributeDifference 単体は30件)。型チェック・lint・本番ビルドすべて通過。

yamk12nfu and others added 2 commits August 2, 2026 02:44
`receipts.parse ok ... items=4` までしか出しておらず、税別レシートで差額の
配分が走ったかどうかを後から確認する手段が無かった。保存された品目を見ても、
品目合計と合計金額がたまたま一致していると「配分が走った」のか「差額0だった」
のか区別が付かない。

distributed と skipped を足す。真偽値だけなのでレシートの中身は漏れない
(要件 5.4「レシートの中身はログに出さない」)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
実機で税別レシートを読ませたところ、注記は出ていたが読み飛ばされた。原因は2つ。

1. 枠線と灰色の文字だけで背景色が無く、画面上部の常設の説明文
   「レシートを撮影すると、品目と金額をAIが読み取ります。」と同じ見た目に
   なっていた。「このレシートで今起きたこと」を伝える一言が案内文に見える
2. 文言だけで金額が無く、ユーザーがレシートと突き合わせて確認できない

背景色を敷いて文字色を強め、配分した金額と合計を文言に入れる。

  消費税などの差額 ¥354 を各品目に上乗せしました。
  合計 ¥1,557 がレシートの支払額と一致していれば大丈夫です

差額が負(総額からの値引き)のときは「差し引きました」に切り替える。
金額を渡すため NormalizedReceipt に distributedAmount を足した。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant