0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude Opus 5に請求書ツールを作らせたら、気づけなかったのは「印刷」と「税務」だった

0
Last updated at Posted at 2026-09-18

株式会社UKMETAのエンジビズ開発チームです。フリーランスエンジニア向けの案件サイトを運営しています。

何を作ったか

SES(準委任)の請求書には「精算幅」という業界特有の計算があります。契約が 140〜180h なら、180h を超えた分は加算、140h に満たない分は減額。その時間単価が 中間割(月額 ÷ 中央値)か 上下割(超過は ÷ 上限、控除は ÷ 下限)かで変わり、弊社の掲載案件 987 件ではちょうど半々でした。

freee にも Misoca にもこの計算はないので、Claude Code(Opus 5)に Excel の請求書テンプレートを渡して作ってもらいました。

ITフリーランスの請求書の書き方|無料ジェネレーター付き

Gatsby + TypeScript の既存サイトに 1 ページ追加。計算ロジック、フォーム、A4 プレビュー、インボイス対応、localStorage 保存まで、最初の PR は実働 1 日でした。Excel の =rounddown(I17*G17,0)+... という数式まで読んで同じ計算にしてくれたのは正直驚きました。

ここまでは AI がほぼ全部やっています。この記事は、その後 AI が気づけず、人間が PDF を開いて初めて分かった 3 つ の話です。

1. 印刷したら 5 ページになった

最初のコードは、ブラウザ印刷の定番パターンでした。

@media print {
  body * { visibility: hidden; }
  #invoice, #invoice * { visibility: visible; }
  #invoice { position: absolute; left: 0; top: 0; }
}

PDF に保存すると 5 ページ。visibility: hidden は描画を消すだけで高さは残るので、フォームや FAQ の分だけ白紙が続きます。AI はこの CSS を「正しい」と判断して出しています。実際、Web 上のサンプルはほぼこの書き方です。

直し方は、印刷用の請求書を body 直下に Portal で描画し、他を display: none にすること。

{printRoot && createPortal(<InvoiceDocument {...props} />, printRoot)}
#invoice-print-root { display: none; }
@media print {
  body > *:not(#invoice-print-root) { display: none !important; }
  #invoice-print-root { display: block; }
}

これで 1 ページ。さらに PDF を見ると、表の見出しの背景色が消えている(Chrome は背景を印刷しない → print-color-adjust: exact)、右端の罫線だけ細い(<colgroup> の幅合計が 102% で右端が欠けていた)も見つかりました。どれも 印刷して目で見ないと分からない 種類のバグです。

2. right: 8 が 512px になった

数値入力の右端に「円」「h」を重ねる、ありふれた UI です。

<span sx={{ position: 'absolute', right: 8 }}></span>

これが画面外に飛びました。theme-ui では top / right / bottom / left の数値は space スケールのインデックス で、right: 8theme.space[8] = このプロジェクトでは 512px。right: '8px' で解決です。

AI は theme-ui の sx を大量に書いているのに、ここは素の CSS の感覚で書いていました。margin/padding のスケール変換は知っていても、position 系にも効くことは「知識としては持っていても、書くときに適用されない」典型でした。

3. 税込契約の請求書を税込で書いた

契約が「月額 88 万円(税込)」の案件があります。最初の実装は、入力された税込金額をそのまま単価欄に書き、「うち消費税 80,000」と添える形でした。計算としては合っています。

でも SES の請求書は 税抜単価で書く のが慣例で、超過・控除の時間単価も税抜に換算します。税込のまま出すと経理に差し戻されます。これは調べないと分からない業務ルールで、AI は聞かれなければ「正しい計算」を返すだけです。

換算すると端数の問題が出ます。税込 850,000 ÷ 1.1 = 772,727.27…。税抜小計 × 10% を足し直しても 850,000 に戻らず、合計が契約額とズレます。

// 税込 → 税抜は切り捨て、消費税は「税込合計 − 税抜小計」の差額で出す
const toNet = (gross: number, rate: number) =>
  Math.floor((gross * 100) / (100 + rate) + 1e-9)

const taxOf = (rate: 10 | 8) =>
  inclusive
    ? grossByRate[rate] - subtotalByRate[rate]
    : roundBy(subtotalByRate[rate] * (rate / 100), taxRounding)

税抜 772,727、消費税 77,273、合計 850,000。インボイスの「税率ごとに端数処理は 1 回」も満たします。方針を伝えたあとの実装と、この境界値のテストは AI が一発で書きました。

まとめ

  • Opus 5 は、仕様(Excel)があれば業務ツールを 1 日で形にする。計算の再現精度は高い
  • 気づけなかったのは 出力を物理的に見ないと分からないもの(印刷)と、聞かれないと出てこない業務慣習(税務)
  • つまり人間の仕事は「作る」より「刷って見る」「経理に聞く」側に寄った

月末に請求書を書く方は、よければ使ってみてください。入力内容はブラウザの外に出ません。

ITフリーランスの請求書の書き方|無料ジェネレーター付き・インボイス対応

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?