【銀行・経理】仕訳データで貸借が一致しない伝票を検出する(Currency 型で誤差を防ぐ)
伝票番号ごとに複数行ある仕訳データから、借方合計と貸方合計が一致しない伝票を見つけて一覧にするマクロです。Double 型で足し合わせると起きる小数の誤差と、Currency 型を使う理由も解説します。
想定する場面
会計システムへ仕訳を取り込む前のチェックや、銀行の事務部門での伝票の検証では、「1枚の伝票の中で借方と貸方の合計が一致しているか」を確認します。手作業で作った仕訳や、複数のシステムから集めたデータでは、行の入力漏れや金額の打ち間違いで貸借がずれることがあり、取り込み時にエラーになったり、後で原因を探すのに時間がかかったりします。
このマクロは、伝票番号ごとに借方と貸方の金額を合計し、一致しない伝票だけを差額付きで一覧にします。あわせて、金額欄に数値以外が入っている行も検出します。
データの形(例)
「仕訳」シートの列構成は説明用の架空のものです。1つの伝票は複数行に分かれ、各行には借方か貸方のどちらか、または両方の金額が入っています。
- A列:伝票番号
- B列:伝票日付
- C列:行番号
- D列:借方科目
- E列:借方金額
- F列:貸方科目
- G列:貸方金額
- H列:摘要
なぜ Currency 型を使うのか
VBA の Double 型は、0.1 のような小数を2進数で近似して持つため、足し算を繰り返すとごくわずかな誤差が生じます。外貨建ての金額や、単価×数量で計算した端数のある金額を Double で合計すると、貸借が実際には一致していても「差額 0.00000000001」のように不一致と判定されることがあります。
Currency 型は、小数点以下4桁までを整数として正確に扱う型で、金額の計算に向いています。次のコードで違いを確かめられます。0.1 を10回足した結果を 1 と比べると、Double では False、Currency では True になります。
Sub FloatDemo()
Dim d As Double, c As Currency, i As Long
For i = 1 To 10
d = d + 0.1
c = c + CCur(0.1)
Next i
Debug.Print d = 1, c = 1 ' False True
End Subコード
伝票番号をキーにした Dictionary に、伝票日付・行数・借方合計・貸方合計を持たせて集計します。金額は CCur で Currency 型に変換してから足し込みます。結果は「不一致伝票」シートに出力し、元の仕訳シートの該当行にも色を付けます。
Sub CheckJournalBalance()
Dim ws As Worksheet, outWs As Worksheet, data As Variant, out() As Variant
Dim dic As Object, k As Variant, rec As Variant
Dim lastRow As Long, i As Long, n As Long, badRows As Long
Dim dr As Currency, cr As Currency
Set ws = ThisWorkbook.Worksheets("仕訳")
Set outWs = ThisWorkbook.Worksheets("不一致伝票")
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
If lastRow < 2 Then MsgBox "仕訳データがありません": Exit Sub
data = ws.Range("A2:H" & lastRow).Value
ws.Range("A2:H" & lastRow).Interior.ColorIndex = xlNone
Set dic = CreateObject("Scripting.Dictionary")
For i = 1 To UBound(data, 1)
k = Trim(CStr(data(i, 1)))
If k <> "" Then
' 空欄は0、数値でない金額は検出して0として扱う
If IsEmpty(data(i, 5)) Then
dr = 0
ElseIf IsNumeric(data(i, 5)) Then
dr = CCur(data(i, 5))
Else
dr = 0: badRows = badRows + 1
ws.Cells(i + 1, 5).Interior.Color = RGB(255, 230, 150)
End If
If IsEmpty(data(i, 7)) Then
cr = 0
ElseIf IsNumeric(data(i, 7)) Then
cr = CCur(data(i, 7))
Else
cr = 0: badRows = badRows + 1
ws.Cells(i + 1, 7).Interior.Color = RGB(255, 230, 150)
End If
If dic.Exists(k) Then
rec = dic(k)
Else
rec = Array(k, data(i, 2), 0&, CCur(0), CCur(0))
End If
rec(2) = rec(2) + 1
rec(3) = rec(3) + dr
rec(4) = rec(4) + cr
dic(k) = rec
End If
Next i
outWs.Cells.Clear
outWs.Range("A1:F1").Value = Array("伝票番号", "伝票日付", "行数", "借方合計", "貸方合計", "差額(借−貸)")
If dic.Count > 0 Then
ReDim out(1 To dic.Count, 1 To 6)
For Each k In dic.Keys
rec = dic(k)
If rec(3) <> rec(4) Then ' Currency どうしなので誤差なく比較できる
n = n + 1
out(n, 1) = rec(0): out(n, 2) = rec(1): out(n, 3) = rec(2)
out(n, 4) = rec(3): out(n, 5) = rec(4): out(n, 6) = rec(3) - rec(4)
End If
Next k
End If
If n > 0 Then
outWs.Range("A2").Resize(n, 6).Value = out
outWs.Range("D2:F" & n + 1).NumberFormatLocal = "#,##0.00"
' 元の仕訳シートで、不一致伝票の行に色を付ける
For i = 1 To UBound(data, 1)
k = Trim(CStr(data(i, 1)))
If k <> "" Then
rec = dic(k)
If rec(3) <> rec(4) Then ws.Range("A" & i + 1 & ":D" & i + 1).Interior.Color = RGB(255, 199, 206)
End If
Next i
End If
MsgBox "伝票 " & dic.Count & " 件中、貸借不一致 " & n & " 件" & vbCrLf & _
"金額が数値でないセル:" & badRows & " 件"
End Subコードのポイントと注意点
比較の前にすべての金額を Currency 型にしているため、「rec(3) <> rec(4)」という単純な比較で、許容誤差を考えずに一致・不一致を判定できます。Double 型のまま比較する場合は「差の絶対値が 0.005 未満なら一致」のような許容範囲が必要になり、その値の決め方自体が新たなミスの原因になります。
CCur は小数点以下5桁目で丸めます(偶数丸め)。仕訳の金額は通常、円単位や小数第2位までなので問題になりませんが、為替換算などで5桁以上の端数を持つ金額を扱う場合は、どの段階で丸めるかを先に決めておく必要があります。
- 金額の列に「1,000円」のような文字列が入っていると数値と判定されないため、黄色で示した行を確認する
- 伝票番号の前後の空白や全角・半角の違いがあると、別の伝票として集計される
- 伝票日付は各伝票の最初の行のものを表示しているので、行ごとに日付が違う伝票は別途確認する
- 差額のある伝票を直したら、もう一度マクロを実行して0件になることを確かめる
※列構成は説明用の架空の例です。会計処理の正否は、社内の経理規程や会計システムの仕様に従って確認してください。