[{"data":1,"prerenderedAt":2881},["ShallowReactive",2],{"content-/tax-patterns-voucher-viewer-2026-02-07":3,"all-pages-for-dir":2860,"related-/tax-patterns-voucher-viewer-2026-02-07":2861,"og-image-/tax-patterns-voucher-viewer-2026-02-07":2880},{"id":4,"title":5,"body":6,"category":2837,"concepts":2838,"description":2839,"extension":2840,"meta":2841,"navigation":328,"ogImage":2838,"path":2842,"project_name":2843,"published":2844,"publishedAt":2845,"seo":2846,"source":2847,"stem":2848,"tags":2849,"todo":2858,"unpublished":2844,"updatedAt":2838,"__hash__":2859},"pages/2026-02/2026-02-07/tax-patterns-voucher-viewer-2026-02-07.md","消費税パターン帳票ビューアー実装記録 - SVG仕訳表からHTML/CSS証憑テンプレートへの方針転換",{"type":7,"value":8,"toc":2802},"minimark",[9,13,17,21,29,32,36,41,44,47,54,57,64,67,71,78,81,85,88,202,205,208,215,225,228,231,237,241,244,525,541,545,556,561,1107,1113,1117,1379,1382,1386,1532,1535,1539,1542,1545,1573,1576,1892,1896,1899,1902,2003,2007,2010,2014,2017,2021,2027,2030,2050,2053,2198,2201,2205,2208,2254,2257,2374,2384,2388,2395,2412,2416,2419,2531,2537,2541,2547,2550,2577,2580,2583,2586,2737,2740,2743,2763,2766,2798],[10,11,12],"h1",{"id":12},"消費税パターン帳票ビューアー実装記録",[14,15,16],"p",{},"消費税経理処理パターン（931件）の各仕訳に対応する証憑（請求書・領収書等）をプレビュー表示するビューアーを実装した。当初はSVGで仕訳表を描画する方向で進めたが、ユーザーからのフィードバックで方針転換し、最終的にHTML/CSSによる4種類の証憑テンプレートに落ち着いた。",[18,19,20],"h2",{"id":20},"背景と目的",[14,22,23,24,28],{},"消費税パターンページ ",[25,26,27],"code",{},"/tax-patterns"," には931件の経理処理パターンが掲載されている。各パターンには取引内容（problem文）と仕訳データがあるが、「この仕訳はどんな帳票から起こされるのか」が視覚的にわからない。",[14,30,31],{},"目的は、仕訳データから対応する証憑（請求書・領収書等）を自動生成し、仕訳と並べて表示すること。これにより「取引 -> 証憑 -> 仕訳」の一連の流れが直感的に理解できるようになる。",[18,33,35],{"id":34},"最初のアプローチ-svg仕訳表失敗","最初のアプローチ: SVG仕訳表（失敗）",[37,38,40],"h3",{"id":39},"svgで仕訳テーブルを描画する方向","SVGで仕訳テーブルを描画する方向",[14,42,43],{},"最初はSVGで仕訳の借方・貸方テーブルを描画する実装を進めた。SVGは座標ベースでレイアウトを制御できるため、固定サイズの帳票表示に向いているという判断だった。",[14,45,46],{},"しかし、ユーザーから「イメージが違う」というフィードバックがあった。",[14,48,49,53],{},[50,51,52],"strong",{},"ユーザーの期待",": 仕訳テーブルの再表示ではなく、実際の請求書や領収書のような「証憑」のプレビューだった。",[37,55,56],{"id":56},"参考資料の提供",[14,58,59,60,63],{},"ユーザーからReact JSXファイル ",[25,61,62],{},"voucher_viewer.jsx"," が参考として提供された。このファイルでは、HTML/CSSで請求書風のレイアウトを構築し、propsでデータを流し込む方式が採用されていた。",[14,65,66],{},"この参考資料を基に、方針を「SVG仕訳表」から「HTML/CSS証憑テンプレート」に転換した。",[37,68,70],{"id":69},"svg実装のリバート","SVG実装のリバート",[14,72,73,74,77],{},"誤ったSVG実装を ",[25,75,76],{},"git revert"," で取り消し、HTML/CSS証憑テンプレートで再実装を開始した。",[18,79,80],{"id":80},"証憑テンプレートの設計",[37,82,84],{"id":83},"_4種類のテンプレート","4種類のテンプレート",[14,86,87],{},"931件のパターンを分析し、以下の4種類のテンプレートでカバーする設計にした。",[89,90,91,113],"table",{},[92,93,94],"thead",{},[95,96,97,101,104,107,110],"tr",{},[98,99,100],"th",{},"#",[98,102,103],{},"テンプレート",[98,105,106],{},"対象取引",[98,108,109],{},"件数",[98,111,112],{},"カバー率",[114,115,116,134,151,168,185],"tbody",{},[95,117,118,122,125,128,131],{},[119,120,121],"td",{},"1",[119,123,124],{},"Invoice（請求書）",[119,126,127],{},"売上・仕入・経費・建設系",[119,129,130],{},"595件",[119,132,133],{},"63.9%",[95,135,136,139,142,145,148],{},[119,137,138],{},"2",[119,140,141],{},"Receipt（領収書）",[119,143,144],{},"現金取引・小売",[119,146,147],{},"145件",[119,149,150],{},"15.6%",[95,152,153,156,159,162,165],{},[119,154,155],{},"3",[119,157,158],{},"Credit Note（返還請求書）",[119,160,161],{},"返品・値引き",[119,163,164],{},"24件",[119,166,167],{},"2.6%",[95,169,170,173,176,179,182],{},[119,171,172],{},"4",[119,174,175],{},"Sales Statement（売上計算書）",[119,177,178],{},"委託販売",[119,180,181],{},"2件",[119,183,184],{},"0.2%",[95,186,187,190,193,196,199],{},[119,188,189],{},"-",[119,191,192],{},"None（帳票なし）",[119,194,195],{},"決算整理・自家消費・対象外",[119,197,198],{},"165件",[119,200,201],{},"17.7%",[14,203,204],{},"決算整理仕訳（165件/17.7%）はそもそも外部帳票が存在しないため、Noneとして何も表示しない。",[37,206,207],{"id":207},"帳票タイプ自動判定ロジック",[14,209,210,211,214],{},"仕訳データの ",[25,212,213],{},"taxCategoryBase","（税区分）を第一優先で使い、取引の種類に応じて帳票タイプを自動判定する。",[216,217,222],"pre",{"className":218,"code":220,"language":221},[219],"language-text","判定優先順位:\n1. taxCategoryBase に「返還」を含む -> credit-note\n2. 全エントリが「対象外」 -> none（決算整理仕訳）\n3. problem に「委託販売」「売上計算書」 -> sales-statement\n4. problem に「現金で」 -> receipt\n5. その他 -> invoice\n","text",[25,223,220],{"__ignoreMap":224},"",[18,226,227],{"id":227},"実装の詳細",[37,229,230],{"id":230},"ファイル構成",[216,232,235],{"className":233,"code":234,"language":221},[219],"apps/web/app/\n  utils/\n    voucher-types.ts          # 共通型定義\n    voucher-mapper.ts         # データ変換ロジック\n  components/voucher/\n    InvoiceTemplate.vue       # 請求書（適格請求書）\n    ReceiptTemplate.vue       # 領収書（適格簡易請求書）\n    CreditNoteTemplate.vue    # 返還請求書（適格返還請求書）\n    SalesStatementTemplate.vue # 売上計算書（委託販売）\n  pages/tax-patterns/\n    voucher-demo.vue          # デモページ（タブ切替・左右分割）\n",[25,236,234],{"__ignoreMap":224},[37,238,240],{"id":239},"_1-型定義voucher-typests","1. 型定義（voucher-types.ts）",[14,242,243],{},"証憑データの共通インターフェースを定義する。",[216,245,249],{"className":246,"code":247,"language":248,"meta":224,"style":224},"language-typescript shiki shiki-themes vitesse-light vitesse-light","export type VoucherType = 'invoice' | 'receipt' | 'credit-note' | 'sales-statement' | 'none';\n\nexport interface VoucherLineItem {\n  description: string;\n  amount: number;\n  taxRate: string;       // \"10%\" | \"8%軽減\" | \"8%(旧)\" etc.\n  netAmount: number;     // 税抜金額\n  taxAmount: number;     // 消費税額\n}\n\nexport interface VoucherData {\n  type: VoucherType;\n  documentNo: string;\n  counterparty: string;\n  lineItems: VoucherLineItem[];\n  totalAmount: number;\n  remarks?: string;\n}\n","typescript",[25,250,251,323,330,344,359,372,389,404,419,425,430,442,455,467,479,493,505,520],{"__ignoreMap":224},[252,253,256,260,264,268,272,276,280,283,286,288,291,293,295,297,300,302,304,306,309,311,313,315,318,320],"span",{"class":254,"line":255},"line",1,[252,257,259],{"class":258},"sHkkW","export",[252,261,263],{"class":262},"stQ0i"," type",[252,265,267],{"class":266},"sSkh3"," VoucherType",[252,269,271],{"class":270},"shFtX"," =",[252,273,275],{"class":274},"sMJiu"," '",[252,277,279],{"class":278},"sdGka","invoice",[252,281,282],{"class":274},"'",[252,284,285],{"class":270}," |",[252,287,275],{"class":274},[252,289,290],{"class":278},"receipt",[252,292,282],{"class":274},[252,294,285],{"class":270},[252,296,275],{"class":274},[252,298,299],{"class":278},"credit-note",[252,301,282],{"class":274},[252,303,285],{"class":270},[252,305,275],{"class":274},[252,307,308],{"class":278},"sales-statement",[252,310,282],{"class":274},[252,312,285],{"class":270},[252,314,275],{"class":274},[252,316,317],{"class":278},"none",[252,319,282],{"class":274},[252,321,322],{"class":270},";\n",[252,324,326],{"class":254,"line":325},2,[252,327,329],{"emptyLinePlaceholder":328},true,"\n",[252,331,333,335,338,341],{"class":254,"line":332},3,[252,334,259],{"class":258},[252,336,337],{"class":262}," interface",[252,339,340],{"class":266}," VoucherLineItem",[252,342,343],{"class":270}," {\n",[252,345,347,351,354,357],{"class":254,"line":346},4,[252,348,350],{"class":349},"s4oTP","  description",[252,352,353],{"class":270},": ",[252,355,356],{"class":266},"string",[252,358,322],{"class":270},[252,360,362,365,367,370],{"class":254,"line":361},5,[252,363,364],{"class":349},"  amount",[252,366,353],{"class":270},[252,368,369],{"class":266},"number",[252,371,322],{"class":270},[252,373,375,378,380,382,385],{"class":254,"line":374},6,[252,376,377],{"class":349},"  taxRate",[252,379,353],{"class":270},[252,381,356],{"class":266},[252,383,384],{"class":270},";",[252,386,388],{"class":387},"sxvE3","       // \"10%\" | \"8%軽減\" | \"8%(旧)\" etc.\n",[252,390,392,395,397,399,401],{"class":254,"line":391},7,[252,393,394],{"class":349},"  netAmount",[252,396,353],{"class":270},[252,398,369],{"class":266},[252,400,384],{"class":270},[252,402,403],{"class":387},"     // 税抜金額\n",[252,405,407,410,412,414,416],{"class":254,"line":406},8,[252,408,409],{"class":349},"  taxAmount",[252,411,353],{"class":270},[252,413,369],{"class":266},[252,415,384],{"class":270},[252,417,418],{"class":387},"     // 消費税額\n",[252,420,422],{"class":254,"line":421},9,[252,423,424],{"class":270},"}\n",[252,426,428],{"class":254,"line":427},10,[252,429,329],{"emptyLinePlaceholder":328},[252,431,433,435,437,440],{"class":254,"line":432},11,[252,434,259],{"class":258},[252,436,337],{"class":262},[252,438,439],{"class":266}," VoucherData",[252,441,343],{"class":270},[252,443,445,448,450,453],{"class":254,"line":444},12,[252,446,447],{"class":349},"  type",[252,449,353],{"class":270},[252,451,452],{"class":266},"VoucherType",[252,454,322],{"class":270},[252,456,458,461,463,465],{"class":254,"line":457},13,[252,459,460],{"class":349},"  documentNo",[252,462,353],{"class":270},[252,464,356],{"class":266},[252,466,322],{"class":270},[252,468,470,473,475,477],{"class":254,"line":469},14,[252,471,472],{"class":349},"  counterparty",[252,474,353],{"class":270},[252,476,356],{"class":266},[252,478,322],{"class":270},[252,480,482,485,487,490],{"class":254,"line":481},15,[252,483,484],{"class":349},"  lineItems",[252,486,353],{"class":270},[252,488,489],{"class":266},"VoucherLineItem",[252,491,492],{"class":270},"[];\n",[252,494,496,499,501,503],{"class":254,"line":495},16,[252,497,498],{"class":349},"  totalAmount",[252,500,353],{"class":270},[252,502,369],{"class":266},[252,504,322],{"class":270},[252,506,508,511,514,516,518],{"class":254,"line":507},17,[252,509,510],{"class":349},"  remarks",[252,512,513],{"class":262},"?",[252,515,353],{"class":270},[252,517,356],{"class":266},[252,519,322],{"class":270},[252,521,523],{"class":254,"line":522},18,[252,524,424],{"class":270},[14,526,527,353,530,533,534,536,537,540],{},[50,528,529],{},"ポイント",[25,531,532],{},"taxRate"," は ",[25,535,356],{}," 型にしている。実データに ",[25,538,539],{},"8%(旧)"," が2件存在するため、列挙型ではなく文字列で受け取る設計にした（Codexレビュー指摘#2への対応）。",[37,542,544],{"id":543},"_2-データ変換ロジックvoucher-mapperts","2. データ変換ロジック（voucher-mapper.ts）",[14,546,547,548,551,552,555],{},"仕訳データ（",[25,549,550],{},"AccountingExample","）から証憑データ（",[25,553,554],{},"VoucherData","）への変換を担う。主要な関数は3つ。",[557,558,560],"h4",{"id":559},"detectvouchertype-帳票タイプ自動判定","detectVoucherType: 帳票タイプ自動判定",[216,562,564],{"className":246,"code":563,"language":248,"meta":224,"style":224},"export function detectVoucherType(example: AccountingExample): VoucherType {\n  // 1. 返品・値引き系: カテゴリ名 + 税区分「返還」で判定\n  const hasReturn = example.categoryName.includes('値引')\n    || example.categoryName.includes('戻し')\n    || example.categoryName.includes('戻り')\n    || example.patterns.some(p => p.entries.some(e =>\n        e.debit.taxCategoryBase.includes('返還')\n        || e.credit.taxCategoryBase.includes('返還')\n      ));\n  if (hasReturn) return 'credit-note';\n\n  // 2. 委託販売\n  if (example.problem.includes('委託販売') || example.problem.includes('売上計算書'))\n    return 'sales-statement';\n\n  // 3. 現金取引\n  if (example.problem.includes('現金で'))\n    return 'receipt';\n\n  // 4. 全エントリが「対象外」-> 内部処理（帳票なし）\n  const allOutside = example.patterns.every(p =>\n    p.entries.every(e =>\n      e.debit.taxCategoryBase === '対象外' && e.credit.taxCategoryBase === '対象外'\n    )\n  );\n  if (allOutside) return 'none';\n\n  // 5. デフォルト: 請求書\n  return 'invoice';\n}\n",[25,565,566,594,599,635,661,686,729,758,789,794,818,822,827,879,892,896,901,928,940,945,951,978,998,1044,1050,1056,1077,1082,1088,1102],{"__ignoreMap":224},[252,567,568,570,573,577,580,583,585,587,590,592],{"class":254,"line":255},[252,569,259],{"class":258},[252,571,572],{"class":262}," function",[252,574,576],{"class":575},"senZ8"," detectVoucherType",[252,578,579],{"class":270},"(",[252,581,582],{"class":349},"example",[252,584,353],{"class":270},[252,586,550],{"class":266},[252,588,589],{"class":270},"):",[252,591,267],{"class":266},[252,593,343],{"class":270},[252,595,596],{"class":254,"line":325},[252,597,598],{"class":387},"  // 1. 返品・値引き系: カテゴリ名 + 税区分「返還」で判定\n",[252,600,601,604,607,609,612,615,618,620,623,625,627,630,632],{"class":254,"line":332},[252,602,603],{"class":262},"  const ",[252,605,606],{"class":349},"hasReturn",[252,608,271],{"class":270},[252,610,611],{"class":349}," example",[252,613,614],{"class":270},".",[252,616,617],{"class":349},"categoryName",[252,619,614],{"class":270},[252,621,622],{"class":575},"includes",[252,624,579],{"class":270},[252,626,282],{"class":274},[252,628,629],{"class":278},"値引",[252,631,282],{"class":274},[252,633,634],{"class":270},")\n",[252,636,637,640,642,644,646,648,650,652,654,657,659],{"class":254,"line":346},[252,638,639],{"class":262},"    ||",[252,641,611],{"class":349},[252,643,614],{"class":270},[252,645,617],{"class":349},[252,647,614],{"class":270},[252,649,622],{"class":575},[252,651,579],{"class":270},[252,653,282],{"class":274},[252,655,656],{"class":278},"戻し",[252,658,282],{"class":274},[252,660,634],{"class":270},[252,662,663,665,667,669,671,673,675,677,679,682,684],{"class":254,"line":361},[252,664,639],{"class":262},[252,666,611],{"class":349},[252,668,614],{"class":270},[252,670,617],{"class":349},[252,672,614],{"class":270},[252,674,622],{"class":575},[252,676,579],{"class":270},[252,678,282],{"class":274},[252,680,681],{"class":278},"戻り",[252,683,282],{"class":274},[252,685,634],{"class":270},[252,687,688,690,692,694,697,699,702,704,706,709,712,714,717,719,721,723,726],{"class":254,"line":374},[252,689,639],{"class":262},[252,691,611],{"class":349},[252,693,614],{"class":270},[252,695,696],{"class":349},"patterns",[252,698,614],{"class":270},[252,700,701],{"class":575},"some",[252,703,579],{"class":270},[252,705,14],{"class":349},[252,707,708],{"class":270}," =>",[252,710,711],{"class":349}," p",[252,713,614],{"class":270},[252,715,716],{"class":349},"entries",[252,718,614],{"class":270},[252,720,701],{"class":575},[252,722,579],{"class":270},[252,724,725],{"class":349},"e",[252,727,728],{"class":270}," =>\n",[252,730,731,734,736,739,741,743,745,747,749,751,754,756],{"class":254,"line":391},[252,732,733],{"class":349},"        e",[252,735,614],{"class":270},[252,737,738],{"class":349},"debit",[252,740,614],{"class":270},[252,742,213],{"class":349},[252,744,614],{"class":270},[252,746,622],{"class":575},[252,748,579],{"class":270},[252,750,282],{"class":274},[252,752,753],{"class":278},"返還",[252,755,282],{"class":274},[252,757,634],{"class":270},[252,759,760,763,766,768,771,773,775,777,779,781,783,785,787],{"class":254,"line":406},[252,761,762],{"class":262},"        ||",[252,764,765],{"class":349}," e",[252,767,614],{"class":270},[252,769,770],{"class":349},"credit",[252,772,614],{"class":270},[252,774,213],{"class":349},[252,776,614],{"class":270},[252,778,622],{"class":575},[252,780,579],{"class":270},[252,782,282],{"class":274},[252,784,753],{"class":278},[252,786,282],{"class":274},[252,788,634],{"class":270},[252,790,791],{"class":254,"line":421},[252,792,793],{"class":270},"      ));\n",[252,795,796,799,802,804,807,810,812,814,816],{"class":254,"line":427},[252,797,798],{"class":258},"  if",[252,800,801],{"class":270}," (",[252,803,606],{"class":349},[252,805,806],{"class":270},")",[252,808,809],{"class":258}," return",[252,811,275],{"class":274},[252,813,299],{"class":278},[252,815,282],{"class":274},[252,817,322],{"class":270},[252,819,820],{"class":254,"line":432},[252,821,329],{"emptyLinePlaceholder":328},[252,823,824],{"class":254,"line":444},[252,825,826],{"class":387},"  // 2. 委託販売\n",[252,828,829,831,833,835,837,840,842,844,846,848,850,852,854,857,859,861,863,865,867,869,871,874,876],{"class":254,"line":457},[252,830,798],{"class":258},[252,832,801],{"class":270},[252,834,582],{"class":349},[252,836,614],{"class":270},[252,838,839],{"class":349},"problem",[252,841,614],{"class":270},[252,843,622],{"class":575},[252,845,579],{"class":270},[252,847,282],{"class":274},[252,849,178],{"class":278},[252,851,282],{"class":274},[252,853,806],{"class":270},[252,855,856],{"class":262}," ||",[252,858,611],{"class":349},[252,860,614],{"class":270},[252,862,839],{"class":349},[252,864,614],{"class":270},[252,866,622],{"class":575},[252,868,579],{"class":270},[252,870,282],{"class":274},[252,872,873],{"class":278},"売上計算書",[252,875,282],{"class":274},[252,877,878],{"class":270},"))\n",[252,880,881,884,886,888,890],{"class":254,"line":469},[252,882,883],{"class":258},"    return",[252,885,275],{"class":274},[252,887,308],{"class":278},[252,889,282],{"class":274},[252,891,322],{"class":270},[252,893,894],{"class":254,"line":481},[252,895,329],{"emptyLinePlaceholder":328},[252,897,898],{"class":254,"line":495},[252,899,900],{"class":387},"  // 3. 現金取引\n",[252,902,903,905,907,909,911,913,915,917,919,921,924,926],{"class":254,"line":507},[252,904,798],{"class":258},[252,906,801],{"class":270},[252,908,582],{"class":349},[252,910,614],{"class":270},[252,912,839],{"class":349},[252,914,614],{"class":270},[252,916,622],{"class":575},[252,918,579],{"class":270},[252,920,282],{"class":274},[252,922,923],{"class":278},"現金で",[252,925,282],{"class":274},[252,927,878],{"class":270},[252,929,930,932,934,936,938],{"class":254,"line":522},[252,931,883],{"class":258},[252,933,275],{"class":274},[252,935,290],{"class":278},[252,937,282],{"class":274},[252,939,322],{"class":270},[252,941,943],{"class":254,"line":942},19,[252,944,329],{"emptyLinePlaceholder":328},[252,946,948],{"class":254,"line":947},20,[252,949,950],{"class":387},"  // 4. 全エントリが「対象外」-> 内部処理（帳票なし）\n",[252,952,954,956,959,961,963,965,967,969,972,974,976],{"class":254,"line":953},21,[252,955,603],{"class":262},[252,957,958],{"class":349},"allOutside",[252,960,271],{"class":270},[252,962,611],{"class":349},[252,964,614],{"class":270},[252,966,696],{"class":349},[252,968,614],{"class":270},[252,970,971],{"class":575},"every",[252,973,579],{"class":270},[252,975,14],{"class":349},[252,977,728],{"class":270},[252,979,981,984,986,988,990,992,994,996],{"class":254,"line":980},22,[252,982,983],{"class":349},"    p",[252,985,614],{"class":270},[252,987,716],{"class":349},[252,989,614],{"class":270},[252,991,971],{"class":575},[252,993,579],{"class":270},[252,995,725],{"class":349},[252,997,728],{"class":270},[252,999,1001,1004,1006,1008,1010,1012,1015,1017,1020,1022,1025,1027,1029,1031,1033,1035,1037,1039,1041],{"class":254,"line":1000},23,[252,1002,1003],{"class":349},"      e",[252,1005,614],{"class":270},[252,1007,738],{"class":349},[252,1009,614],{"class":270},[252,1011,213],{"class":349},[252,1013,1014],{"class":262}," === ",[252,1016,282],{"class":274},[252,1018,1019],{"class":278},"対象外",[252,1021,282],{"class":274},[252,1023,1024],{"class":262}," && ",[252,1026,725],{"class":349},[252,1028,614],{"class":270},[252,1030,770],{"class":349},[252,1032,614],{"class":270},[252,1034,213],{"class":349},[252,1036,1014],{"class":262},[252,1038,282],{"class":274},[252,1040,1019],{"class":278},[252,1042,1043],{"class":274},"'\n",[252,1045,1047],{"class":254,"line":1046},24,[252,1048,1049],{"class":270},"    )\n",[252,1051,1053],{"class":254,"line":1052},25,[252,1054,1055],{"class":270},"  );\n",[252,1057,1059,1061,1063,1065,1067,1069,1071,1073,1075],{"class":254,"line":1058},26,[252,1060,798],{"class":258},[252,1062,801],{"class":270},[252,1064,958],{"class":349},[252,1066,806],{"class":270},[252,1068,809],{"class":258},[252,1070,275],{"class":274},[252,1072,317],{"class":278},[252,1074,282],{"class":274},[252,1076,322],{"class":270},[252,1078,1080],{"class":254,"line":1079},27,[252,1081,329],{"emptyLinePlaceholder":328},[252,1083,1085],{"class":254,"line":1084},28,[252,1086,1087],{"class":387},"  // 5. デフォルト: 請求書\n",[252,1089,1091,1094,1096,1098,1100],{"class":254,"line":1090},29,[252,1092,1093],{"class":258},"  return",[252,1095,275],{"class":274},[252,1097,279],{"class":278},[252,1099,282],{"class":274},[252,1101,322],{"class":270},[252,1103,1105],{"class":254,"line":1104},30,[252,1106,424],{"class":270},[14,1108,1109,1110,1112],{},"初期の実装ではカテゴリ名やproblem文の文言に依存していたが、Codexレビュー（指摘#7）で「文言依存の判定が脆い」と指摘され、",[25,1111,213],{},"（税区分）をベースにした判定ロジックに改善した。",[557,1114,1116],{"id":1115},"calctaxbreakdown-税込から税抜税額を算出","calcTaxBreakdown: 税込から税抜・税額を算出",[216,1118,1120],{"className":246,"code":1119,"language":248,"meta":224,"style":224},"export function calcTaxBreakdown(amount: number, taxRate: string): { net: number; tax: number } {\n  // 免税・非課税・不課税は消費税なし\n  if (taxRate === '免税' || taxRate === '非課税' || taxRate === '不課税') {\n    return { net: amount, tax: 0 };\n  }\n  const rateNum = taxRate === '8%軽減' || taxRate === '8%(旧)' ? 8 : 10;\n  const tax = Math.floor(amount * rateNum / (100 + rateNum));\n  return { net: amount - tax, tax };\n}\n",[25,1121,1122,1176,1181,1229,1259,1264,1311,1352,1375],{"__ignoreMap":224},[252,1123,1124,1126,1128,1131,1133,1136,1138,1140,1143,1146,1148,1150,1152,1155,1158,1160,1162,1164,1167,1169,1171,1174],{"class":254,"line":255},[252,1125,259],{"class":258},[252,1127,572],{"class":262},[252,1129,1130],{"class":575}," calcTaxBreakdown",[252,1132,579],{"class":270},[252,1134,1135],{"class":349},"amount",[252,1137,353],{"class":270},[252,1139,369],{"class":266},[252,1141,1142],{"class":270},",",[252,1144,1145],{"class":349}," taxRate",[252,1147,353],{"class":270},[252,1149,356],{"class":266},[252,1151,589],{"class":270},[252,1153,1154],{"class":270}," {",[252,1156,1157],{"class":349}," net",[252,1159,353],{"class":270},[252,1161,369],{"class":266},[252,1163,384],{"class":270},[252,1165,1166],{"class":349}," tax",[252,1168,353],{"class":270},[252,1170,369],{"class":266},[252,1172,1173],{"class":270}," }",[252,1175,343],{"class":270},[252,1177,1178],{"class":254,"line":325},[252,1179,1180],{"class":387},"  // 免税・非課税・不課税は消費税なし\n",[252,1182,1183,1185,1187,1189,1192,1194,1197,1199,1201,1203,1205,1207,1210,1212,1214,1216,1218,1220,1223,1225,1227],{"class":254,"line":332},[252,1184,798],{"class":258},[252,1186,801],{"class":270},[252,1188,532],{"class":349},[252,1190,1191],{"class":262}," ===",[252,1193,275],{"class":274},[252,1195,1196],{"class":278},"免税",[252,1198,282],{"class":274},[252,1200,856],{"class":262},[252,1202,1145],{"class":349},[252,1204,1191],{"class":262},[252,1206,275],{"class":274},[252,1208,1209],{"class":278},"非課税",[252,1211,282],{"class":274},[252,1213,856],{"class":262},[252,1215,1145],{"class":349},[252,1217,1191],{"class":262},[252,1219,275],{"class":274},[252,1221,1222],{"class":278},"不課税",[252,1224,282],{"class":274},[252,1226,806],{"class":270},[252,1228,343],{"class":270},[252,1230,1231,1233,1236,1240,1242,1244,1247,1250,1252,1256],{"class":254,"line":346},[252,1232,883],{"class":258},[252,1234,1235],{"class":270}," { ",[252,1237,1239],{"class":1238},"sz8Xr","net",[252,1241,353],{"class":270},[252,1243,1135],{"class":349},[252,1245,1246],{"class":270},", ",[252,1248,1249],{"class":1238},"tax",[252,1251,353],{"class":270},[252,1253,1255],{"class":1254},"sM54T","0",[252,1257,1258],{"class":270}," };\n",[252,1260,1261],{"class":254,"line":361},[252,1262,1263],{"class":270},"  }\n",[252,1265,1266,1268,1271,1273,1275,1277,1279,1282,1284,1287,1289,1291,1293,1295,1297,1300,1303,1306,1309],{"class":254,"line":374},[252,1267,603],{"class":262},[252,1269,1270],{"class":349},"rateNum",[252,1272,271],{"class":270},[252,1274,1145],{"class":349},[252,1276,1014],{"class":262},[252,1278,282],{"class":274},[252,1280,1281],{"class":278},"8%軽減",[252,1283,282],{"class":274},[252,1285,1286],{"class":262}," || ",[252,1288,532],{"class":349},[252,1290,1014],{"class":262},[252,1292,282],{"class":274},[252,1294,539],{"class":278},[252,1296,282],{"class":274},[252,1298,1299],{"class":262}," ? ",[252,1301,1302],{"class":1254},"8",[252,1304,1305],{"class":262}," : ",[252,1307,1308],{"class":1254},"10",[252,1310,322],{"class":270},[252,1312,1313,1315,1317,1319,1322,1324,1327,1329,1331,1334,1336,1339,1341,1344,1347,1349],{"class":254,"line":391},[252,1314,603],{"class":262},[252,1316,1249],{"class":349},[252,1318,271],{"class":270},[252,1320,1321],{"class":349}," Math",[252,1323,614],{"class":270},[252,1325,1326],{"class":575},"floor",[252,1328,579],{"class":270},[252,1330,1135],{"class":349},[252,1332,1333],{"class":262}," * ",[252,1335,1270],{"class":349},[252,1337,1338],{"class":262}," / ",[252,1340,579],{"class":270},[252,1342,1343],{"class":1254},"100",[252,1345,1346],{"class":262}," + ",[252,1348,1270],{"class":349},[252,1350,1351],{"class":270},"));\n",[252,1353,1354,1356,1358,1360,1362,1364,1367,1369,1371,1373],{"class":254,"line":406},[252,1355,1093],{"class":258},[252,1357,1235],{"class":270},[252,1359,1239],{"class":1238},[252,1361,353],{"class":270},[252,1363,1135],{"class":349},[252,1365,1366],{"class":262}," -",[252,1368,1166],{"class":349},[252,1370,1246],{"class":270},[252,1372,1249],{"class":349},[252,1374,1258],{"class":270},[252,1376,1377],{"class":254,"line":421},[252,1378,424],{"class":270},[14,1380,1381],{},"この関数には2つの重要な修正が入っている。",[557,1383,1385],{"id":1384},"resolvetaxrate-taxcategorybaseから表示用税率を決定","resolveTaxRate: taxCategoryBaseから表示用税率を決定",[216,1387,1389],{"className":246,"code":1388,"language":248,"meta":224,"style":224},"function resolveTaxRate(taxCategoryBase: string, taxRate?: string): string {\n  if (taxCategoryBase === '輸出免税売上') return '免税';\n  if (taxCategoryBase === '非課税売上') return '非課税';\n  if (taxCategoryBase === '不課税売上') return '不課税';\n  return taxRate || '10%';\n}\n",[25,1390,1391,1424,1453,1482,1511,1528],{"__ignoreMap":224},[252,1392,1393,1396,1399,1401,1403,1405,1407,1409,1411,1413,1415,1417,1419,1422],{"class":254,"line":255},[252,1394,1395],{"class":262},"function",[252,1397,1398],{"class":575}," resolveTaxRate",[252,1400,579],{"class":270},[252,1402,213],{"class":349},[252,1404,353],{"class":270},[252,1406,356],{"class":266},[252,1408,1142],{"class":270},[252,1410,1145],{"class":349},[252,1412,513],{"class":262},[252,1414,353],{"class":270},[252,1416,356],{"class":266},[252,1418,589],{"class":270},[252,1420,1421],{"class":266}," string",[252,1423,343],{"class":270},[252,1425,1426,1428,1430,1432,1434,1436,1439,1441,1443,1445,1447,1449,1451],{"class":254,"line":325},[252,1427,798],{"class":258},[252,1429,801],{"class":270},[252,1431,213],{"class":349},[252,1433,1191],{"class":262},[252,1435,275],{"class":274},[252,1437,1438],{"class":278},"輸出免税売上",[252,1440,282],{"class":274},[252,1442,806],{"class":270},[252,1444,809],{"class":258},[252,1446,275],{"class":274},[252,1448,1196],{"class":278},[252,1450,282],{"class":274},[252,1452,322],{"class":270},[252,1454,1455,1457,1459,1461,1463,1465,1468,1470,1472,1474,1476,1478,1480],{"class":254,"line":332},[252,1456,798],{"class":258},[252,1458,801],{"class":270},[252,1460,213],{"class":349},[252,1462,1191],{"class":262},[252,1464,275],{"class":274},[252,1466,1467],{"class":278},"非課税売上",[252,1469,282],{"class":274},[252,1471,806],{"class":270},[252,1473,809],{"class":258},[252,1475,275],{"class":274},[252,1477,1209],{"class":278},[252,1479,282],{"class":274},[252,1481,322],{"class":270},[252,1483,1484,1486,1488,1490,1492,1494,1497,1499,1501,1503,1505,1507,1509],{"class":254,"line":346},[252,1485,798],{"class":258},[252,1487,801],{"class":270},[252,1489,213],{"class":349},[252,1491,1191],{"class":262},[252,1493,275],{"class":274},[252,1495,1496],{"class":278},"不課税売上",[252,1498,282],{"class":274},[252,1500,806],{"class":270},[252,1502,809],{"class":258},[252,1504,275],{"class":274},[252,1506,1222],{"class":278},[252,1508,282],{"class":274},[252,1510,322],{"class":270},[252,1512,1513,1515,1517,1519,1521,1524,1526],{"class":254,"line":361},[252,1514,1093],{"class":258},[252,1516,1145],{"class":349},[252,1518,856],{"class":262},[252,1520,275],{"class":274},[252,1522,1523],{"class":278},"10%",[252,1525,282],{"class":274},[252,1527,322],{"class":270},[252,1529,1530],{"class":254,"line":374},[252,1531,424],{"class":270},[14,1533,1534],{},"輸出免税売上の場合に消費税率を「免税」として扱い、税額を0にする対応を追加した。",[37,1536,1538],{"id":1537},"_3-請求書テンプレートinvoicetemplatevue","3. 請求書テンプレート（InvoiceTemplate.vue）",[14,1540,1541],{},"最も汎用的なテンプレート。931件中595件（63.9%）をカバーする。インボイス制度の適格請求書フォーマットに準拠したレイアウトにした。",[14,1543,1544],{},"主な構成要素:",[1546,1547,1548,1552,1555,1558,1561,1564,1567,1570],"ul",{},[1549,1550,1551],"li",{},"タイトル（「請求書」＋「適格請求書」サブタイトル）",[1549,1553,1554],{},"宛名（取引先名 御中）",[1549,1556,1557],{},"請求金額ボックス（税込合計を大きく表示）",[1549,1559,1560],{},"発行者情報（社名・住所・電話・登録番号）",[1549,1562,1563],{},"商品明細テーブル（No./品名/数量/単価/税率/金額）",[1549,1565,1566],{},"税率別内訳テーブル（税抜金額/消費税額/税込金額）",[1549,1568,1569],{},"軽減税率注記（8%対象品目がある場合のみ表示）",[1549,1571,1572],{},"支払い条件セクション",[14,1574,1575],{},"税率別内訳テーブルでは、computed プロパティで明細行を税率ごとに集計している。",[216,1577,1579],{"className":246,"code":1578,"language":248,"meta":224,"style":224},"const taxSummary = computed(() => {\n  const map = new Map\u003Cstring, { net: number; tax: number; total: number }>();\n  props.voucher.lineItems.forEach(item => {\n    const existing = map.get(item.taxRate) || { net: 0, tax: 0, total: 0 };\n    existing.net += item.netAmount;\n    existing.tax += item.taxAmount;\n    existing.total += item.amount;\n    map.set(item.taxRate, existing);\n  });\n  return Array.from(map.entries()).sort(([a], [b]) => b.localeCompare(a));\n});\n",[25,1580,1581,1601,1651,1680,1738,1759,1778,1796,1822,1827,1887],{"__ignoreMap":224},[252,1582,1583,1586,1589,1591,1594,1597,1599],{"class":254,"line":255},[252,1584,1585],{"class":262},"const ",[252,1587,1588],{"class":349},"taxSummary",[252,1590,271],{"class":270},[252,1592,1593],{"class":575}," computed",[252,1595,1596],{"class":270},"(()",[252,1598,708],{"class":270},[252,1600,343],{"class":270},[252,1602,1603,1605,1608,1610,1613,1616,1619,1621,1623,1625,1627,1629,1631,1633,1635,1637,1639,1641,1644,1646,1648],{"class":254,"line":325},[252,1604,603],{"class":262},[252,1606,1607],{"class":349},"map",[252,1609,271],{"class":270},[252,1611,1612],{"class":262}," new ",[252,1614,1615],{"class":575},"Map",[252,1617,1618],{"class":270},"\u003C",[252,1620,356],{"class":266},[252,1622,1142],{"class":270},[252,1624,1154],{"class":270},[252,1626,1157],{"class":349},[252,1628,353],{"class":270},[252,1630,369],{"class":266},[252,1632,384],{"class":270},[252,1634,1166],{"class":349},[252,1636,353],{"class":270},[252,1638,369],{"class":266},[252,1640,384],{"class":270},[252,1642,1643],{"class":349}," total",[252,1645,353],{"class":270},[252,1647,369],{"class":266},[252,1649,1650],{"class":270}," }>();\n",[252,1652,1653,1656,1658,1661,1663,1666,1668,1671,1673,1676,1678],{"class":254,"line":332},[252,1654,1655],{"class":349},"  props",[252,1657,614],{"class":270},[252,1659,1660],{"class":349},"voucher",[252,1662,614],{"class":270},[252,1664,1665],{"class":349},"lineItems",[252,1667,614],{"class":270},[252,1669,1670],{"class":575},"forEach",[252,1672,579],{"class":270},[252,1674,1675],{"class":349},"item",[252,1677,708],{"class":270},[252,1679,343],{"class":270},[252,1681,1682,1685,1688,1690,1693,1695,1698,1700,1702,1704,1706,1708,1710,1713,1715,1717,1719,1721,1723,1725,1727,1729,1732,1734,1736],{"class":254,"line":346},[252,1683,1684],{"class":262},"    const ",[252,1686,1687],{"class":349},"existing",[252,1689,271],{"class":270},[252,1691,1692],{"class":349}," map",[252,1694,614],{"class":270},[252,1696,1697],{"class":575},"get",[252,1699,579],{"class":270},[252,1701,1675],{"class":349},[252,1703,614],{"class":270},[252,1705,532],{"class":349},[252,1707,806],{"class":270},[252,1709,1286],{"class":262},[252,1711,1712],{"class":270},"{ ",[252,1714,1239],{"class":1238},[252,1716,353],{"class":270},[252,1718,1255],{"class":1254},[252,1720,1246],{"class":270},[252,1722,1249],{"class":1238},[252,1724,353],{"class":270},[252,1726,1255],{"class":1254},[252,1728,1246],{"class":270},[252,1730,1731],{"class":1238},"total",[252,1733,353],{"class":270},[252,1735,1255],{"class":1254},[252,1737,1258],{"class":270},[252,1739,1740,1743,1745,1747,1750,1752,1754,1757],{"class":254,"line":361},[252,1741,1742],{"class":349},"    existing",[252,1744,614],{"class":270},[252,1746,1239],{"class":349},[252,1748,1749],{"class":262}," += ",[252,1751,1675],{"class":349},[252,1753,614],{"class":270},[252,1755,1756],{"class":349},"netAmount",[252,1758,322],{"class":270},[252,1760,1761,1763,1765,1767,1769,1771,1773,1776],{"class":254,"line":374},[252,1762,1742],{"class":349},[252,1764,614],{"class":270},[252,1766,1249],{"class":349},[252,1768,1749],{"class":262},[252,1770,1675],{"class":349},[252,1772,614],{"class":270},[252,1774,1775],{"class":349},"taxAmount",[252,1777,322],{"class":270},[252,1779,1780,1782,1784,1786,1788,1790,1792,1794],{"class":254,"line":391},[252,1781,1742],{"class":349},[252,1783,614],{"class":270},[252,1785,1731],{"class":349},[252,1787,1749],{"class":262},[252,1789,1675],{"class":349},[252,1791,614],{"class":270},[252,1793,1135],{"class":349},[252,1795,322],{"class":270},[252,1797,1798,1801,1803,1806,1808,1810,1812,1814,1816,1819],{"class":254,"line":406},[252,1799,1800],{"class":349},"    map",[252,1802,614],{"class":270},[252,1804,1805],{"class":575},"set",[252,1807,579],{"class":270},[252,1809,1675],{"class":349},[252,1811,614],{"class":270},[252,1813,532],{"class":349},[252,1815,1142],{"class":270},[252,1817,1818],{"class":349}," existing",[252,1820,1821],{"class":270},");\n",[252,1823,1824],{"class":254,"line":421},[252,1825,1826],{"class":270},"  });\n",[252,1828,1829,1831,1834,1836,1839,1841,1843,1845,1847,1850,1853,1856,1859,1862,1865,1868,1871,1873,1876,1878,1881,1883,1885],{"class":254,"line":427},[252,1830,1093],{"class":258},[252,1832,1833],{"class":349}," Array",[252,1835,614],{"class":270},[252,1837,1838],{"class":575},"from",[252,1840,579],{"class":270},[252,1842,1607],{"class":349},[252,1844,614],{"class":270},[252,1846,716],{"class":575},[252,1848,1849],{"class":270},"()).",[252,1851,1852],{"class":575},"sort",[252,1854,1855],{"class":270},"(([",[252,1857,1858],{"class":349},"a",[252,1860,1861],{"class":270},"],",[252,1863,1864],{"class":270}," [",[252,1866,1867],{"class":349},"b",[252,1869,1870],{"class":270},"])",[252,1872,708],{"class":270},[252,1874,1875],{"class":349}," b",[252,1877,614],{"class":270},[252,1879,1880],{"class":575},"localeCompare",[252,1882,579],{"class":270},[252,1884,1858],{"class":349},[252,1886,1351],{"class":270},[252,1888,1889],{"class":254,"line":432},[252,1890,1891],{"class":270},"});\n",[37,1893,1895],{"id":1894},"_4-領収書テンプレートreceipttemplatevue","4. 領収書テンプレート（ReceiptTemplate.vue）",[14,1897,1898],{},"現金取引・小売購入に使用。請求書より幅が狭く（max-width: 480px）、シンプルなレイアウトにしている。",[14,1900,1901],{},"特徴的なのは「但し書き」の自動生成。明細行の品名を結合して「但し ○○・△△として」の文言を作る。",[216,1903,1905],{"className":246,"code":1904,"language":248,"meta":224,"style":224},"const purposeText = computed(() => {\n  const names = props.voucher.lineItems.map(i => i.description);\n  return names.join('・') + 'として';\n});\n",[25,1906,1907,1924,1965,1999],{"__ignoreMap":224},[252,1908,1909,1911,1914,1916,1918,1920,1922],{"class":254,"line":255},[252,1910,1585],{"class":262},[252,1912,1913],{"class":349},"purposeText",[252,1915,271],{"class":270},[252,1917,1593],{"class":575},[252,1919,1596],{"class":270},[252,1921,708],{"class":270},[252,1923,343],{"class":270},[252,1925,1926,1928,1931,1933,1936,1938,1940,1942,1944,1946,1948,1950,1953,1955,1958,1960,1963],{"class":254,"line":325},[252,1927,603],{"class":262},[252,1929,1930],{"class":349},"names",[252,1932,271],{"class":270},[252,1934,1935],{"class":349}," props",[252,1937,614],{"class":270},[252,1939,1660],{"class":349},[252,1941,614],{"class":270},[252,1943,1665],{"class":349},[252,1945,614],{"class":270},[252,1947,1607],{"class":575},[252,1949,579],{"class":270},[252,1951,1952],{"class":349},"i",[252,1954,708],{"class":270},[252,1956,1957],{"class":349}," i",[252,1959,614],{"class":270},[252,1961,1962],{"class":349},"description",[252,1964,1821],{"class":270},[252,1966,1967,1969,1972,1974,1977,1979,1981,1984,1986,1988,1990,1992,1995,1997],{"class":254,"line":332},[252,1968,1093],{"class":258},[252,1970,1971],{"class":349}," names",[252,1973,614],{"class":270},[252,1975,1976],{"class":575},"join",[252,1978,579],{"class":270},[252,1980,282],{"class":274},[252,1982,1983],{"class":278},"・",[252,1985,282],{"class":274},[252,1987,806],{"class":270},[252,1989,1346],{"class":262},[252,1991,282],{"class":274},[252,1993,1994],{"class":278},"として",[252,1996,282],{"class":274},[252,1998,322],{"class":270},[252,2000,2001],{"class":254,"line":346},[252,2002,1891],{"class":270},[37,2004,2006],{"id":2005},"_5-返還請求書テンプレートcreditnotetemplatevue","5. 返還請求書テンプレート（CreditNoteTemplate.vue）",[14,2008,2009],{},"返品・値引きに使用。合計行のラベルが「返還合計」になり、返還理由セクションが追加されている。",[37,2011,2013],{"id":2012},"_6-売上計算書テンプレートsalesstatementtemplatevue","6. 売上計算書テンプレート（SalesStatementTemplate.vue）",[14,2015,2016],{},"委託販売の2件のみが対象。明細テーブルの2行目以降は手数料として負の値（赤字）で表示し、フッターに「差引精算額」を表示する。",[37,2018,2020],{"id":2019},"_7-デモページvoucher-demovue","7. デモページ（voucher-demo.vue）",[14,2022,2023,2024],{},"全テンプレートを統合するデモページ。URL: ",[25,2025,2026],{},"/tax-patterns/voucher-demo",[14,2028,2029],{},"レイアウト構成:",[1546,2031,2032,2038,2044],{},[1549,2033,2034,2037],{},[50,2035,2036],{},"ヘッダー",": 「証憑テンプレート デモ」＋全件数表示",[1549,2039,2040,2043],{},[50,2041,2042],{},"タブバー",": Invoice / Receipt / Credit Note / Sales Statement の4タブ切替",[1549,2045,2046,2049],{},[50,2047,2048],{},"サンプルカード",": 各タブで先頭10件を表示。左右分割レイアウトで「問題文＋仕訳」と「証憑プレビュー」を並べて表示",[14,2051,2052],{},"各タブのバッジに該当件数を表示し、分類の全体像がわかるようにしている。",[216,2054,2056],{"className":246,"code":2055,"language":248,"meta":224,"style":224},"const typeCounts = computed(() => {\n  const counts = new Map\u003CVoucherType, number>();\n  allExamples.value.forEach(ex => {\n    const type = detectVoucherType(ex);\n    counts.set(type, (counts.get(type) || 0) + 1);\n  });\n  return counts;\n});\n",[25,2057,2058,2075,2100,2123,2140,2181,2185,2194],{"__ignoreMap":224},[252,2059,2060,2062,2065,2067,2069,2071,2073],{"class":254,"line":255},[252,2061,1585],{"class":262},[252,2063,2064],{"class":349},"typeCounts",[252,2066,271],{"class":270},[252,2068,1593],{"class":575},[252,2070,1596],{"class":270},[252,2072,708],{"class":270},[252,2074,343],{"class":270},[252,2076,2077,2079,2082,2084,2086,2088,2090,2092,2094,2097],{"class":254,"line":325},[252,2078,603],{"class":262},[252,2080,2081],{"class":349},"counts",[252,2083,271],{"class":270},[252,2085,1612],{"class":262},[252,2087,1615],{"class":575},[252,2089,1618],{"class":270},[252,2091,452],{"class":266},[252,2093,1142],{"class":270},[252,2095,2096],{"class":266}," number",[252,2098,2099],{"class":270},">();\n",[252,2101,2102,2105,2107,2110,2112,2114,2116,2119,2121],{"class":254,"line":332},[252,2103,2104],{"class":349},"  allExamples",[252,2106,614],{"class":270},[252,2108,2109],{"class":349},"value",[252,2111,614],{"class":270},[252,2113,1670],{"class":575},[252,2115,579],{"class":270},[252,2117,2118],{"class":349},"ex",[252,2120,708],{"class":270},[252,2122,343],{"class":270},[252,2124,2125,2127,2130,2132,2134,2136,2138],{"class":254,"line":346},[252,2126,1684],{"class":262},[252,2128,2129],{"class":349},"type",[252,2131,271],{"class":270},[252,2133,576],{"class":575},[252,2135,579],{"class":270},[252,2137,2118],{"class":349},[252,2139,1821],{"class":270},[252,2141,2142,2145,2147,2149,2151,2153,2155,2157,2159,2161,2163,2165,2167,2169,2171,2173,2175,2177,2179],{"class":254,"line":361},[252,2143,2144],{"class":349},"    counts",[252,2146,614],{"class":270},[252,2148,1805],{"class":575},[252,2150,579],{"class":270},[252,2152,2129],{"class":349},[252,2154,1142],{"class":270},[252,2156,801],{"class":270},[252,2158,2081],{"class":349},[252,2160,614],{"class":270},[252,2162,1697],{"class":575},[252,2164,579],{"class":270},[252,2166,2129],{"class":349},[252,2168,806],{"class":270},[252,2170,1286],{"class":262},[252,2172,1255],{"class":1254},[252,2174,806],{"class":270},[252,2176,1346],{"class":262},[252,2178,121],{"class":1254},[252,2180,1821],{"class":270},[252,2182,2183],{"class":254,"line":374},[252,2184,1826],{"class":270},[252,2186,2187,2189,2192],{"class":254,"line":391},[252,2188,1093],{"class":258},[252,2190,2191],{"class":349}," counts",[252,2193,322],{"class":270},[252,2195,2196],{"class":254,"line":406},[252,2197,1891],{"class":270},[18,2199,2200],{"id":2200},"途中で発生した問題と修正",[37,2202,2204],{"id":2203},"浮動小数点誤差の修正165000011問題","浮動小数点誤差の修正（1650000/1.1問題）",[14,2206,2207],{},"税込金額から税抜・税額を算出する際、JavaScriptの浮動小数点演算で誤差が発生した。",[216,2209,2211],{"className":246,"code":2210,"language":248,"meta":224,"style":224},"// 修正前: 浮動小数点除算\nconst net = Math.floor(amount / (1 + rate));\n// 1,650,000 / 1.1 = 1499999.9999... -> Math.floor -> 1,499,999（1円ずれる）\n",[25,2212,2213,2218,2249],{"__ignoreMap":224},[252,2214,2215],{"class":254,"line":255},[252,2216,2217],{"class":387},"// 修正前: 浮動小数点除算\n",[252,2219,2220,2222,2224,2226,2228,2230,2232,2234,2236,2238,2240,2242,2244,2247],{"class":254,"line":325},[252,2221,1585],{"class":262},[252,2223,1239],{"class":349},[252,2225,271],{"class":270},[252,2227,1321],{"class":349},[252,2229,614],{"class":270},[252,2231,1326],{"class":575},[252,2233,579],{"class":270},[252,2235,1135],{"class":349},[252,2237,1338],{"class":262},[252,2239,579],{"class":270},[252,2241,121],{"class":1254},[252,2243,1346],{"class":262},[252,2245,2246],{"class":349},"rate",[252,2248,1351],{"class":270},[252,2250,2251],{"class":254,"line":332},[252,2252,2253],{"class":387},"// 1,650,000 / 1.1 = 1499999.9999... -> Math.floor -> 1,499,999（1円ずれる）\n",[14,2255,2256],{},"修正後は整数演算で端数を回避する方式に変更した。",[216,2258,2260],{"className":246,"code":2259,"language":248,"meta":224,"style":224},"// 修正後: 整数演算で端数回避\nconst rateNum = taxRate === '8%軽減' || taxRate === '8%(旧)' ? 8 : 10;\nconst tax = Math.floor(amount * rateNum / (100 + rateNum));\nreturn { net: amount - tax, tax };\n// 1,650,000 * 10 / 110 = 150,000（正確）\n// net = 1,650,000 - 150,000 = 1,500,000（正確）\n",[25,2261,2262,2267,2307,2341,2364,2369],{"__ignoreMap":224},[252,2263,2264],{"class":254,"line":255},[252,2265,2266],{"class":387},"// 修正後: 整数演算で端数回避\n",[252,2268,2269,2271,2273,2275,2277,2279,2281,2283,2285,2287,2289,2291,2293,2295,2297,2299,2301,2303,2305],{"class":254,"line":325},[252,2270,1585],{"class":262},[252,2272,1270],{"class":349},[252,2274,271],{"class":270},[252,2276,1145],{"class":349},[252,2278,1014],{"class":262},[252,2280,282],{"class":274},[252,2282,1281],{"class":278},[252,2284,282],{"class":274},[252,2286,1286],{"class":262},[252,2288,532],{"class":349},[252,2290,1014],{"class":262},[252,2292,282],{"class":274},[252,2294,539],{"class":278},[252,2296,282],{"class":274},[252,2298,1299],{"class":262},[252,2300,1302],{"class":1254},[252,2302,1305],{"class":262},[252,2304,1308],{"class":1254},[252,2306,322],{"class":270},[252,2308,2309,2311,2313,2315,2317,2319,2321,2323,2325,2327,2329,2331,2333,2335,2337,2339],{"class":254,"line":332},[252,2310,1585],{"class":262},[252,2312,1249],{"class":349},[252,2314,271],{"class":270},[252,2316,1321],{"class":349},[252,2318,614],{"class":270},[252,2320,1326],{"class":575},[252,2322,579],{"class":270},[252,2324,1135],{"class":349},[252,2326,1333],{"class":262},[252,2328,1270],{"class":349},[252,2330,1338],{"class":262},[252,2332,579],{"class":270},[252,2334,1343],{"class":1254},[252,2336,1346],{"class":262},[252,2338,1270],{"class":349},[252,2340,1351],{"class":270},[252,2342,2343,2346,2348,2350,2352,2354,2356,2358,2360,2362],{"class":254,"line":346},[252,2344,2345],{"class":258},"return",[252,2347,1235],{"class":270},[252,2349,1239],{"class":1238},[252,2351,353],{"class":270},[252,2353,1135],{"class":349},[252,2355,1366],{"class":262},[252,2357,1166],{"class":349},[252,2359,1246],{"class":270},[252,2361,1249],{"class":349},[252,2363,1258],{"class":270},[252,2365,2366],{"class":254,"line":361},[252,2367,2368],{"class":387},"// 1,650,000 * 10 / 110 = 150,000（正確）\n",[252,2370,2371],{"class":254,"line":374},[252,2372,2373],{"class":387},"// net = 1,650,000 - 150,000 = 1,500,000（正確）\n",[14,2375,2376,2379,2380,2383],{},[25,2377,2378],{},"amount / (1 + rate)"," を ",[25,2381,2382],{},"amount * rateNum / (100 + rateNum)"," に置き換えることで、中間値が大きくなりIEEE 754の精度で表現できる範囲に収まるようにした。",[37,2385,2387],{"id":2386},"輸出免税売上の消費税0対応","輸出免税売上の消費税0%対応",[14,2389,2390,2391,2394],{},"元の実装では全ての取引に対して10%または8%で税額を計算していた。しかし輸出免税売上（",[25,2392,2393],{},"taxCategoryBase === '輸出免税売上'","）は消費税が0%のため、税額=0、金額=税抜金額として扱う必要がある。",[14,2396,2397,2400,2401,2403,2404,2407,2408,2411],{},[25,2398,2399],{},"resolveTaxRate"," 関数を追加し、",[25,2402,213],{}," から表示用の税率文字列を解決する層を設けた。",[25,2405,2406],{},"calcTaxBreakdown"," は税率文字列が「免税」「非課税」「不課税」の場合に ",[25,2409,2410],{},"{ net: amount, tax: 0 }"," を返す。",[18,2413,2415],{"id":2414},"codexレビューと計画修正","Codexレビューと計画修正",[14,2417,2418],{},"実装計画に対してCodexによるレビューを受け、以下の指摘と対応を行った。",[89,2420,2421,2436],{},[92,2422,2423],{},[95,2424,2425,2427,2430,2433],{},[98,2426,100],{},[98,2428,2429],{},"重要度",[98,2431,2432],{},"指摘内容",[98,2434,2435],{},"対応",[114,2437,2438,2451,2466,2478,2491,2504,2518],{},[95,2439,2440,2442,2445,2448],{},[119,2441,121],{},[119,2443,2444],{},"重大",[119,2446,2447],{},"件数が886ではなく931件",[119,2449,2450],{},"修正済み。931件前提で再計算",[95,2452,2453,2455,2457,2463],{},[119,2454,138],{},[119,2456,2444],{},[119,2458,2459,2460,2462],{},"taxRateに",[25,2461,539],{},"が2件存在",[119,2464,2465],{},"string型に変更。列挙型ではなくバリデーションで対応",[95,2467,2468,2470,2472,2475],{},[119,2469,155],{},[119,2471,2444],{},[119,2473,2474],{},"擬似コードの未定義関数",[119,2476,2477],{},"完全な仕様を記載して解消",[95,2479,2480,2482,2485,2488],{},[119,2481,172],{},[119,2483,2484],{},"高",[119,2486,2487],{},"3表示経路の考慮不足",[119,2489,2490],{},"Phase 2（本体統合時）で対応予定",[95,2492,2493,2496,2498,2501],{},[119,2494,2495],{},"5",[119,2497,2484],{},[119,2499,2500],{},"JournalPatternCardにclick未実装",[119,2502,2503],{},"Phase 2で対応予定",[95,2505,2506,2509,2512,2515],{},[119,2507,2508],{},"6",[119,2510,2511],{},"中",[119,2513,2514],{},"モバイル未設計",[119,2516,2517],{},"Phase 1はデスクトップ限定。1024px以下で1カラムに切替",[95,2519,2520,2523,2525,2528],{},[119,2521,2522],{},"7",[119,2524,2511],{},[119,2526,2527],{},"文言依存の判定が脆い",[119,2529,2530],{},"税区分/カテゴリIDベースの判定に変更",[14,2532,2533,2534,2536],{},"特に指摘#2（",[25,2535,539],{}," の存在）と指摘#7（文言依存の判定ロジック）は実装に直接影響する重要な修正だった。",[18,2538,2540],{"id":2539},"phase-2本体統合の見送り凍結","Phase 2（本体統合）の見送り・凍結",[14,2542,2543,2544,2546],{},"Phase 1のプロトタイプ検証の結果、以下の理由でPhase 2（本体の ",[25,2545,27],{}," ページへの統合）は見送りとした。",[37,2548,2549],{"id":2549},"見送りの理由",[2551,2552,2553,2559,2565,2571],"ol",{},[1549,2554,2555,2558],{},[50,2556,2557],{},"支払い条件の推定が困難",": 分割払い・前受金・手形等をproblem文からNLP的に抽出する必要がある",[1549,2560,2561,2564],{},[50,2562,2563],{},"帳票の方向が逆転する",": 売上系は「当社が発行」、費用系は「取引先から受領」でテンプレートの発行者/宛名が逆になる",[1549,2566,2567,2570],{},[50,2568,2569],{},"非課税・免税の帳票フォーマット分岐",": 土地売買は契約書、輸出は英語INVOICE等、帳票種類がさらに分岐する",[1549,2572,2573,2576],{},[50,2574,2575],{},"決算整理仕訳（165件/17.7%）",": そもそも外部帳票が存在しない取引",[14,2578,2579],{},"これらを正確に実装する複雑さと対費用効果を考慮し、デモページはそのまま残すがこれ以上の開発は行わない判断とした。",[37,2581,2582],{"id":2582},"帳票表示内容マトリックス",[14,2584,2585],{},"Phase 1の検証で明らかになった、taxCategoryBase別の帳票表示パターンを整理しておく。",[89,2587,2588,2605],{},[92,2589,2590],{},[95,2591,2592,2594,2596,2599,2602],{},[98,2593,213],{},[98,2595,109],{},[98,2597,2598],{},"帳票タイプ",[98,2600,2601],{},"税率表示",[98,2603,2604],{},"税額計算",[114,2606,2607,2624,2638,2652,2665,2682,2696,2710,2723],{},[95,2608,2609,2612,2615,2618,2621],{},[119,2610,2611],{},"課税売上",[119,2613,2614],{},"91",[119,2616,2617],{},"Invoice",[119,2619,2620],{},"10% / 8%軽減",[119,2622,2623],{},"税込から算出",[95,2625,2626,2629,2632,2634,2636],{},[119,2627,2628],{},"課税仕入",[119,2630,2631],{},"593",[119,2633,2617],{},[119,2635,2620],{},[119,2637,2623],{},[95,2639,2640,2642,2644,2646,2649],{},[119,2641,1438],{},[119,2643,121],{},[119,2645,2617],{},[119,2647,2648],{},"免税（0%）",[119,2650,2651],{},"税額=0",[95,2653,2654,2656,2659,2661,2663],{},[119,2655,1467],{},[119,2657,2658],{},"20",[119,2660,2617],{},[119,2662,1209],{},[119,2664,2651],{},[95,2666,2667,2670,2673,2676,2679],{},[119,2668,2669],{},"課税売上返還",[119,2671,2672],{},"11",[119,2674,2675],{},"Credit Note",[119,2677,2678],{},"元取引の税率",[119,2680,2681],{},"返還額から算出",[95,2683,2684,2687,2690,2692,2694],{},[119,2685,2686],{},"課税仕入返還",[119,2688,2689],{},"13",[119,2691,2675],{},[119,2693,2678],{},[119,2695,2681],{},[95,2697,2698,2701,2703,2706,2708],{},[119,2699,2700],{},"有価証券譲渡",[119,2702,138],{},[119,2704,2705],{},"None",[119,2707,189],{},[119,2709,189],{},[95,2711,2712,2715,2717,2719,2721],{},[119,2713,2714],{},"課税貸倒",[119,2716,138],{},[119,2718,2705],{},[119,2720,189],{},[119,2722,189],{},[95,2724,2725,2727,2730,2733,2735],{},[119,2726,1019],{},[119,2728,2729],{},"1,523",[119,2731,2732],{},"含めない",[119,2734,189],{},[119,2736,189],{},[18,2738,2739],{"id":2739},"まとめ",[37,2741,2742],{"id":2742},"実装したもの",[1546,2744,2745,2748,2751,2757,2760],{},[1549,2746,2747],{},"4種類のHTML/CSS証憑テンプレート（Vue.jsコンポーネント）",[1549,2749,2750],{},"仕訳データから証憑データへの自動変換ロジック",[1549,2752,2753,2754,2756],{},"デモページ（",[25,2755,2026],{},"）で動作確認可能",[1549,2758,2759],{},"浮動小数点誤差を回避した整数ベースの税額計算",[1549,2761,2762],{},"輸出免税・非課税・不課税への対応",[37,2764,2765],{"id":2765},"学んだこと",[1546,2767,2768,2774,2786,2792],{},[1549,2769,2770,2773],{},[50,2771,2772],{},"ユーザーの期待を早期に確認する重要性",": SVGで仕訳テーブルを表示する方向で実装を進めてしまい、リバートが必要になった。プロトタイプを見せてフィードバックをもらうサイクルは短い方がよい",[1549,2775,2776,2779,2780,2782,2783,2785],{},[50,2777,2778],{},"JavaScriptの浮動小数点演算には注意",": 金額計算では ",[25,2781,2378],{}," ではなく ",[25,2784,2382],{}," のように整数演算に寄せる",[1549,2787,2788,2791],{},[50,2789,2790],{},"スコープの見極め",": 931件全てを完璧にカバーしようとすると複雑さが指数関数的に増大する。Phase 1（デモ）で検証してからPhase 2（統合）を判断するアプローチは正しかった",[1549,2793,2794,2797],{},[50,2795,2796],{},"Codexレビューの有効性",": データの件数ミスや型の問題を実装前に発見できた",[2799,2800,2801],"style",{},"html pre.shiki code .sHkkW, html code.shiki .sHkkW{--shiki-default:#1E754F;--shiki-dark:#1E754F}html pre.shiki code .stQ0i, html code.shiki .stQ0i{--shiki-default:#AB5959;--shiki-dark:#AB5959}html pre.shiki code .sSkh3, html code.shiki .sSkh3{--shiki-default:#2E8F82;--shiki-dark:#2E8F82}html pre.shiki code .shFtX, html code.shiki .shFtX{--shiki-default:#999999;--shiki-dark:#999999}html pre.shiki code .sMJiu, html code.shiki .sMJiu{--shiki-default:#B5695977;--shiki-dark:#B5695977}html pre.shiki code .sdGka, html code.shiki .sdGka{--shiki-default:#B56959;--shiki-dark:#B56959}html pre.shiki code .s4oTP, html code.shiki .s4oTP{--shiki-default:#B07D48;--shiki-dark:#B07D48}html pre.shiki code .sxvE3, html code.shiki .sxvE3{--shiki-default:#A0ADA0;--shiki-dark:#A0ADA0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .senZ8, html code.shiki .senZ8{--shiki-default:#59873A;--shiki-dark:#59873A}html pre.shiki code .sz8Xr, html code.shiki .sz8Xr{--shiki-default:#998418;--shiki-dark:#998418}html pre.shiki code .sM54T, html code.shiki .sM54T{--shiki-default:#2F798A;--shiki-dark:#2F798A}",{"title":224,"searchDepth":325,"depth":325,"links":2803},[2804,2805,2810,2814,2824,2828,2829,2833],{"id":20,"depth":325,"text":20},{"id":34,"depth":325,"text":35,"children":2806},[2807,2808,2809],{"id":39,"depth":332,"text":40},{"id":56,"depth":332,"text":56},{"id":69,"depth":332,"text":70},{"id":80,"depth":325,"text":80,"children":2811},[2812,2813],{"id":83,"depth":332,"text":84},{"id":207,"depth":332,"text":207},{"id":227,"depth":325,"text":227,"children":2815},[2816,2817,2818,2819,2820,2821,2822,2823],{"id":230,"depth":332,"text":230},{"id":239,"depth":332,"text":240},{"id":543,"depth":332,"text":544},{"id":1537,"depth":332,"text":1538},{"id":1894,"depth":332,"text":1895},{"id":2005,"depth":332,"text":2006},{"id":2012,"depth":332,"text":2013},{"id":2019,"depth":332,"text":2020},{"id":2200,"depth":325,"text":2200,"children":2825},[2826,2827],{"id":2203,"depth":332,"text":2204},{"id":2386,"depth":332,"text":2387},{"id":2414,"depth":325,"text":2415},{"id":2539,"depth":325,"text":2540,"children":2830},[2831,2832],{"id":2549,"depth":332,"text":2549},{"id":2582,"depth":332,"text":2582},{"id":2739,"depth":325,"text":2739,"children":2834},[2835,2836],{"id":2742,"depth":332,"text":2742},{"id":2765,"depth":332,"text":2765},"dev",null,"消費税経理処理パターン931件に対応する証憑（請求書・領収書・返還請求書・売上計算書）をHTML/CSSテンプレートで表示するビューアーを実装した記録。SVGによる仕訳表表示からHTML/CSS証憑テンプレートへの方針転換、浮動小数点誤差の修正、輸出免税売上の0%対応、Codexレビューを経た設計判断までを詳細に記述","md",{},"/tax-patterns-voucher-viewer-2026-02-07","mdx-playground",false,"2026-02-07T00:00:00.000Z",{"title":5,"description":2839},"make-diary","2026-02/2026-02-07/tax-patterns-voucher-viewer-2026-02-07",[2850,2851,2852,2853,2854,2855,2856,2857],"Vue.js","消費税","インボイス制度","証憑テンプレート","HTML/CSS","TypeScript","Nuxt3","Codexレビュー","memo","D_NFJYQw85m4I20Ld87_F_zB3mO26C880XZU-7d3b74",[],[2862,2866,2869,2873,2877],{"title":2863,"path":2864,"publishedAt":2865},"2026年2月5日の開発日記 - 連結精算表の大幅拡張と消費税クイズ機能強化","/diary-2026-02-05","2026-02-05T00:00:00.000Z",{"title":2867,"path":2868,"publishedAt":2865},"消費税経理処理パターンの問題集を強化した - Miller Columns 4カラム化・全文検索・条件付きハイライト","/tax-patterns-quiz-enhancement-2026-02-05",{"title":2870,"path":2871,"publishedAt":2872},"2026年2月4日の開発日記 - 連結会計エンジン10パターン対応と消費税パターンビューア完成","/diary-2026-02-04","2026-02-04T00:00:00.000Z",{"title":2874,"path":2875,"publishedAt":2876},"2026年4月27日の開発日記 - CFWS v2 順方向化を1日で Phase 1〜8 まで通し、AEHR/Lumentum/AAOI 投資メモも公開","/2026-04-27-diary","2026-04-27T00:00:00.000Z",{"title":2878,"path":2879,"publishedAt":2876},"キャッシュフロー精算表v2: 逆算スクリプトから「取引→仕訳→年次推移→CFWS」順方向ロジックへ全面書き換え（Phase 1〜8一気通貫実装記録）","/cfws-v2-forward-architecture","https://log.eurekapu.com/og/blog/tax-patterns-voucher-viewer-2026-02-07.png?v=2026-02-07T00%3A00%3A00.000Z&title=%E6%B6%88%E8%B2%BB%E7%A8%8E%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E5%B8%B3%E7%A5%A8%E3%83%93%E3%83%A5%E3%83%BC%E3%82%A2%E3%83%BC%E5%AE%9F%E8%A3%85%E8%A8%98%E9%8C%B2%20-%20SVG%E4%BB%95%E8%A8%B3%E8%A1%A8%E3%81%8B%E3%82%89HTML%2FCSS%E8%A8%BC%E6%86%91%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%81%B8%E3%81%AE%E6%96%B9%E9%87%9D%E8%BB%A2%E6%8F%9B&author=Kei%20Komatsu&sig=db28152400fa7320",1786424491808]