Option Explicit
Const TRG_SHEET As String = "全体" '集計先シート名
Const SRC_SHEET As String = "実績" '集計元シート名
Const ROW_COUNT_OF_1BLOCK As Long = 28 '1ブロックの行数
Const ROW_START_NO As Long = 6 'データの開始行
Const ZENNEN As Long = 0 '前年
Const TOUNEN As Long = 1 '当年
Dim dicKCD As Object 'キー:分類区分コード 値:相対行番号(0オリジン)
Dim dicRow As Object 'キー:開始行番号 値:true
Dim dicT As Object 'キー:支店名+課名 値:開始行番号
Dim srowAllSum As Long '全体合計の開始行
Dim UriArray As Variant '売上集計対象 分類コード(82、86、85、87、88)
Public Sub 新全体集計()
Dim maxrow As Long
Dim i As Long
Dim j As Long
Dim wrow As Long
Dim wcol As Long
Dim srow As Long '開始行
Dim scol As Long '開始列
Dim siten As String '支店名
Dim ka As String '課名
Dim key As Variant 'キー
Dim t1 As Variant
Dim t2 As Variant
Dim ksh As Worksheet '管理シート
Dim tsh As Worksheet '全体シート
Dim zen_folder As String '前年フォルダー
Dim tou_folder As String '当年フォルダー
Dim zen_file As String '前年実績ファイル
Dim tou_file As String '当年実績ファイル
Dim block_count As Long '全体シートのブロック数
Set dicKCD = CreateObject("Scripting.Dictionary")
Set dicT = CreateObject("Scripting.Dictionary")
Set dicRow = CreateObject("Scripting.Dictionary")
Set tsh = Worksheets(TRG_SHEET)
Set ksh = Worksheets("管理")
UriArray = Array("82", "85", "86", "87", "88") '売上を集計する分類コード
'管理シートのチェック
zen_folder = ksh.Range("B2").Value
tou_folder = ksh.Range("B3").Value
zen_file = ksh.Range("C2").Value
tou_file = ksh.Range("C3").Value
Call check_folder("前年フォルダ", zen_folder)
Call check_folder("当年フォルダ", tou_folder)
Call check_file("前年ファイル", zen_folder & "\" & zen_file)
Call check_file("当年ファイル", tou_folder & "\" & tou_file)
'全体シートのチェック
maxrow = tsh.Cells(Rows.Count, "C").End(xlUp).row 'C列の最終行を求める
If (maxrow - ROW_START_NO + 1) Mod ROW_COUNT_OF_1BLOCK <> 0 Then
MsgBox ("全体シートの行数不正")
Exit Sub
End If
'ブロック数算出
block_count = (maxrow - ROW_START_NO + 1) \ ROW_COUNT_OF_1BLOCK
'分類区分コードのリストを作成する
For wrow = ROW_START_NO To (ROW_COUNT_OF_1BLOCK + ROW_START_NO - 1)
key = CStr(tsh.Cells(wrow, "D").Value)
If key <> "" Then
If dicKCD.exists(key) = False Then
dicKCD(key) = wrow - ROW_START_NO '相対行を記憶
Else
MsgBox ("全体シートの分類区分コード重複")
Exit Sub
End If
End If
Next
'ブロック開始行番号記憶
For i = 1 To block_count '1~最後のブロックまで処理
srow = GetRowNumber(i)
dicRow(srow) = True
Next
'全体シートの支店名・課名を記憶
For wrow = ROW_START_NO To maxrow
If tsh.Cells(wrow, "A").Value <> "" Then '支店名を取得
siten = tsh.Cells(wrow, "A").Value
End If
If tsh.Cells(wrow, "B").Value <> "" Then '課名を取得
ka = tsh.Cells(wrow, "B").Value
End If
If siten = "全体合計" Then '全体合計の記憶
key = "|||合計"
If dicT.exists(key) = False Then dicT(key) = wrow
Else
If Right(ka, 2) = "合計" Then '支店合計の記憶
key = siten & "||合計"
If dicT.exists(key) = False Then dicT(key) = wrow
End If
End If
key = siten & "|" & ka
If dicT.exists(key) = False Then
If dicRow.exists(wrow) = False Then
MsgBox ("開始行不正 行番号=" & wrow & " 支店=" & siten & " 課=" & ka)
Exit Sub
End If
dicT(key) = wrow '行番号を記憶
End If
Next
key = "|||合計"
If dicT.exists(key) = False Then
MsgBox ("全体合計が全体シートになし")
Exit Sub
End If
srowAllSum = dicT(key)
If MsgBox("集計を開始します", vbOKCancel) <> vbOK Then
Exit Sub
End If
t1 = Timer
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
'全体シートクリア
For i = 1 To block_count '1~最後のブロックまで処理
srow = GetRowNumber(i)
For j = 1 To 12 '1月~12月まで処理
scol = GetColNumber(j)
Call blockClear(tsh, srow, scol)
Next
Next
'前年データを集計
Call Sum1Book(zen_folder, zen_file, ZENNEN)
'当年データを集計
Call Sum1Book(tou_folder, tou_file, TOUNEN)
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
t2 = Timer
MsgBox ("処理完了 所要時間(秒)=" & t2 - t1)
End Sub
Private Sub check_folder(ByVal comment As String, ByVal path As String)
If Dir(path, vbDirectory) = "" Then
MsgBox (comment & "が不正[" & path & "]は存在しません")
End
End If
End Sub
Private Sub check_file(ByVal comment As String, ByVal path As String)
If Dir(path, vbNormal) = "" Then
MsgBox (comment & "が不正[" & path & "]は存在しません")
End
End If
End Sub
'1つのブックを集計する
Private Sub Sum1Book(ByVal src_folder As String, ByVal bookname As String, ByVal nendo As Long)
Workbooks.Open src_folder & "\" & bookname
Call Sum1Sheet(nendo)
'ブックを保存しないで閉じる(更新していないので保存不要)
Workbooks(bookname).Saved = True
Workbooks(bookname).Close
End Sub
'1つのシートを集計する
Private Sub Sum1Sheet(ByVal nendo As Long)
Dim ws As Worksheet
Dim maxrow As Long
Dim wrow As Long
Dim wcol As String
Dim mm As Long '月
Dim siten As String '支店
Dim ka As String '課
Dim key As Variant '支店・課のキー
Dim key2 As Variant '支店合計のキー
Dim bcd As String '分類区分コード
Dim val1 As Variant 'H列/I列の値
Dim val2 As Variant 'J列の値
Dim rowA As Long '全体シートの行
Dim rowY As Long '全体シートの支店合計の行
Dim rowZ As Long '全体シートの全体合計の行
Dim scol As Long '全体シートの対応月の開始列
Set ws = Worksheets(SRC_SHEET)
maxrow = ws.Cells(Rows.Count, "A").End(xlUp).row 'A列の最終行を求める
For wrow = 3 To maxrow
mm = get_month(ws.Cells(wrow, "A").Text)
If mm = -1 Then
Call abort_proc(nendo, wrow, "A", "売上日付不正(" & ws.Cells(wrow, "A").Text & ")")
End If
siten = ws.Cells(wrow, "B").Value '支店名取得
ka = ws.Cells(wrow, "C").Value '課名取得
key = siten & "|" & ka
If dicT.exists(key) = False Then
Call abort_proc(nendo, wrow, "B", "支店・課が全体シートに未登録(" & key & ")")
End If
key2 = siten & "||合計"
If dicT.exists(key2) = False Then
Call abort_proc(nendo, wrow, "B", "支店合計が全体シートに未登録(" & siten & ")")
End If
bcd = ws.Cells(wrow, "F").Value '分類区分コード取得
If dicKCD.exists(bcd) = False Then
Call abort_proc(nendo, wrow, "F", "分類区分コードが全体シートに未登録(" & bcd & ")")
End If
If is_uriage(bcd) = True Then
wcol = "I"
Else
wcol = "H"
End If
val1 = get_val(nendo, ws, wrow, wcol)
val2 = get_val(nendo, ws, wrow, "J")
rowA = dicT(key) + dicKCD(bcd)
rowY = dicT(key2) + dicKCD(bcd)
rowZ = srowAllSum + dicKCD(bcd)
scol = GetColNumber(mm)
'数量/価格の集計
Call add_val(nendo, rowA, scol + 3, val1)
Call add_val(nendo, rowY, scol + 3, val1)
Call add_val(nendo, rowZ, scol + 3, val1)
'粗利の集計
Call add_val(nendo, rowA, scol + 8, val2)
Call add_val(nendo, rowY, scol + 8, val2)
Call add_val(nendo, rowZ, scol + 8, val2)
Next
End Sub
'全体シートへ加算する
Private Sub add_val(ByVal nendo As Long, ByVal wrow As Long, ByVal wcol As Long, ByVal val As Variant)
If val = "" Then Exit Sub
If nendo = TOUNEN Then wcol = wcol + 1
ThisWorkbook.Worksheets(TRG_SHEET).Cells(wrow, wcol).Value = ThisWorkbook.Worksheets(TRG_SHEET).Cells(wrow, wcol).Value + val
End Sub
'分類区分コードが売上の集計か否かを返す
'true:売上の集計
'false:数量の集計
Private Function is_uriage(ByVal bcd As String) As Boolean
Dim i As Long
is_uriage = True
For i = 0 To UBound(UriArray)
If UriArray(i) = bcd Then Exit Function
Next
is_uriage = False
End Function
'文字で表示された月(1月...12月)の月を取得する
Private Function get_month(ByVal mstr As String) As Long
Dim mm As String
get_month = -1
If Right(mstr, 1) <> "月" Then Exit Function
mm = Left(mstr, Len(mstr) - 1)
If IsNumeric(mm) = False Then Exit Function
get_month = CLng(mm)
If get_month > 0 And get_month < 13 Then Exit Function
get_month = -1
End Function
'指定行、指定列のデータの取得及びチェック
Private Function get_val(ByVal nendo As String, ByVal ws As Worksheet, ByVal wrow As Long, ByVal wcol As String) As Variant
get_val = ws.Cells(wrow, wcol).Value
If IsNumeric(get_val) = False Then
Call abort_proc(nendo, wrow, wcol, " 列=" & wcol & " 不正データ(" & ws.Cells(wrow, wcol).Text & ")")
End If
End Function
'マクロ停止処理
Private Sub abort_proc(ByVal nendo As Long, ByVal wrow As Long, ByVal wcol As Variant, ByVal msg As String)
Dim name As String
If nendo = ZENNEN Then
name = "前年元データ"
Else
name = "当年元データ"
End If
MsgBox (name & " 行番号=" & wrow & " " & msg)
Worksheets(SRC_SHEET).Activate
Cells(wrow, wcol).Select
Application.Calculation = xlCalculationAutomatic
End
End Sub
'ブロッククリア(1課・1ケ月分)
Private Sub blockClear(ByVal ws As Worksheet, ByVal srow As Long, ByVal scol As Long)
Dim wrow As Long
Dim key As Variant
For Each key In dicKCD
wrow = dicKCD(key) + srow
ws.Cells(wrow, scol + 3).Value = ""
ws.Cells(wrow, scol + 4).Value = ""
ws.Cells(wrow, scol + 8).Value = ""
ws.Cells(wrow, scol + 9).Value = ""
Next
End Sub
'指定月から指定月対応のカラム位置(計画数量)を計算する
'カラム位置は1からの連番
'10月=6 ... 9月=186
Private Function GetColNumber(ByVal mm As Long)
Dim x, ix As Long
If mm < 10 Then mm = mm + 12
x = mm - 10
ix = x + x \ 3 + x \ 6
GetColNumber = 6 + 12 * ix
End Function
'指定ブロック番号の行番号を取得する
'1=6,2=34,21=566
Private Function GetRowNumber(ByVal blkNo As Long)
GetRowNumber = ROW_START_NO + (blkNo - 1) * ROW_COUNT_OF_1BLOCK
End Function
'列番号を英文字に変換
Function ConvertToLetter(ByVal iCol As Integer) As String
Dim iAlpha As Integer
Dim iRemainder As Integer
iAlpha = Int((iCol - 1) / 26)
iRemainder = iCol - (iAlpha * 26)
If iAlpha > 0 Then
ConvertToLetter = Chr(iAlpha + 64)
End If
If iRemainder > 0 Then
ConvertToLetter = ConvertToLetter & Chr(iRemainder + 64)
End If
End Function