' C_Const
Option Explicit
Public Const MASTER_A_DESTINATION_SHEET_NAME As String = "マスタA"
Public Const MASTER_B_DESTINATION_SHEET_NAME As String = "マスタB"
Public Const MASTER_C_DESTINATION_SHEET_NAME As String = "マスタC"
Public Function getWorkFolderPath() As String
getWorkFolderPath = ThisWorkbook.Path & "\" & "作業用"
End Function
' E_Error
Option Explicit
' エラーNo
Public Enum eErrorType
Unrecoverable = 2000 ' 外部要因によるプログラム続行不可エラー
InvalidConfig = 2010 ' コンフィグの設定ミス
ProgramBug = 2020 ' プログラムのバグ
End Enum
' throw メソッド
Public Sub RaiseUnrecoverableError(errorMessage As String)
Call raise(eErrorType.Unrecoverable, errorMessage)
End Sub
Public Sub RaiseInvalidConfigError(errorMessage As String)
Call raise(eErrorType.InvalidConfig, errorMessage)
End Sub
Public Sub RaiseProgramBugError(errorMessage As String)
Call raise(eErrorType.ProgramBug, errorMessage)
End Sub
Private Sub raise(errorType As eErrorType, errorMessage As String)
Err.raise errorType, "", errorMessage
End Sub
'P01_Main
Option Explicit
Public Sub entry()
On Error GoTo 0
Dim releaseDate As String
releaseDate = getUserSelectedReleaseDate
On Error GoTo ExecError
Dim toolWorkbook As workbook
Call P02_Backup.createBackup(releaseDate)
Set toolWorkbook = P03_ToolBookInitializer.initialize
Call P04_ImportXXMaster.MasterAImport(toolWorkbook, releaseDate)
' P05
' P06
Call MsgBox("", vbOKOnly, "処理が完了しました")
Exit Sub
ExecError:
Call MsgBox(Err.Description, vbCritical & vbOKOnly)
End Sub
' ユーザーが選択したリリース日を取得する
Private Function getUserSelectedReleaseDate() As String
Const message As String = "どっち"
Dim userAnswer As Long
userAnswer = MsgBox(message, vbYesNo, "ツールのリリース日を選択してください")
Dim releaseDate As String
If (userAnswer = vbYes) Then
releaseDate = Format(DateAdd("m", 1, Date), "YYYYMM") & "01"
Else
releaseDate = Format(Date, "YYYYMMDD")
End If
getUserSelectedReleaseDate = releaseDate
End Function
'P02_Backup
Option Explicit
' バックアップ・作業用フォルダに最新のツールを格納する
Public Sub createBackup(releaseDate As String)
Dim toolName As String
toolName = U_Config.getValue(eConfigKey.toolName)
Dim releaseFolderPath As String
releaseFolderPath = U_Config.getValue(eConfigKey.releaseFolder)
Dim backupFolderPath As String
backupFolderPath = U_Config.getValue(eConfigKey.backupFolder)
Dim workFolderPath As String
workFolderPath = C_Const.getWorkFolderPath
' 作業用フォルダをクリアする
Call clearWorkFolder(workFolderPath, toolName)
' 最新のツールを取得する
Dim latestToolFile As File
Set latestToolFile = findLatestToolFile(releaseFolderPath, toolName)
' バックアップフォルダにツールをコピーする
Call copyToBackupFolder(latestToolFile, backupFolderPath)
' 作業用フォルダーにツールをコピーする
Call copyToWorkFolder(latestToolFile, workFolderPath, toolName, releaseDate)
End Sub
' 作業用フォルダをクリアする
Private Sub clearWorkFolder(folderPath As String, fileName As String)
Dim fso As FileSystemObject
Set fso = New FileSystemObject
' 作業用フォルダが存在する場合
If (fso.FolderExists(folderPath)) Then
Dim matchedFiles As Collection
Set matchedFiles = U_File.findFilesByName(folderPath, fileName)
Dim f As File
For Each f In matchedFiles
On Error GoTo FileDeleteFailed
Call f.Delete(True)
On Error GoTo 0
Next
Else
fso.CreateFolder (folderPath)
End If
Exit Sub
FileDeleteFailed:
Call E_Error.RaiseUnrecoverableError( _
"作業フォルダのファイルが開かれているため、ファイルの削除に失敗しました。" & vbCrLf _
& "作業フォルダ内のすべてのファイルを閉じてから再試行してください。" & vbCrLf & vbCrLf _
& "[フォルダ] " & vbCrLf & folderPath)
End Sub
' 最新のツールファイルを取得する
Private Function findLatestToolFile(folderPath As String, toolName As String) As File
Dim latestToolFile As File
Set latestToolFile = U_File.findSingleFileByName(folderPath, toolName)
Set findLatestToolFile = latestToolFile
End Function
' バックアップフォルダに最新のファイルをコピーする
Private Sub copyToBackupFolder(toolFile As File, folderPath As String)
Dim fullpath As String
fullpath = U_File.joinPath(folderPath, toolFile.Name)
Call FileCopy(toolFile.Path, fullpath)
End Sub
' 作業用フォルダに最新ファイルをコピーする
Private Sub copyToWorkFolder(toolFile As File, folderPath As String, toolName As String, releaseDate As String)
Dim fileName As String
fileName = toolName & "_" & releaseDate & ".xlsm"
Dim fullpath As String
fullpath = U_File.joinPath(folderPath, fileName)
Call FileCopy(toolFile.Path, fullpath)
End Sub
'P03_ToolBookInitializeer
Option Explicit
' インポートの事前処理を行う
Public Function initialize() As workbook
Dim workFolderPath As String
workFolderPath = C_Const.getWorkFolderPath
Dim toolName As String
toolName = U_Config.getValue(eConfigKey.toolName)
' ツールのブックを開く
Dim toolBook As workbook
Set toolBook = openWorkBook(workFolderPath, toolName)
' 処理対象のシートを取得する
Dim targetSheetNames As Collection
Set targetSheetNames = New Collection
Call targetSheetNames.Add(C_Const.MASTER_A_DESTINATION_SHEET_NAME)
Call targetSheetNames.Add(C_Const.MASTER_B_DESTINATION_SHEET_NAME)
Call targetSheetNames.Add(C_Const.MASTER_C_DESTINATION_SHEET_NAME)
' パスワードを解除する
Call unprotectSheets(toolBook, targetSheetNames)
' セル値を可視化する
Call visibleSheetsValue(toolBook, targetSheetNames)
Set initialize = toolBook
End Function
Private Function openWorkBook(workFolderPath As String, toolName As String) As workbook
Dim toolFile As File
Set toolFile = U_File.findSingleFileByName(workFolderPath, toolName)
Dim wb As workbook
Set wb = Workbooks.Open(toolFile.Path)
Set openWorkBook = wb
End Function
' 各シートの保護を解除する
Private Sub unprotectSheets(wb As workbook, worksheetNames As Collection)
' パスワード不一致回数
Dim passwordMismatchCount As Long
passwordMismatchCount = 0
Dim ws As Worksheet
Set ws = U_Book.getWorksheet(wb, worksheetNames.Item(1))
PasswordInput:
' パスワードをアンロックする
Dim inputPassword As String
inputPassword = InputBox("パスワードを入力してください。", "パスワード入力")
On Error GoTo PasswordMismatch:
Call ws.Unprotect(inputPassword)
On Error GoTo 0
GoTo UnlockSucceed
' パスワード不一致
PasswordMismatch:
passwordMismatchCount = passwordMismatchCount + 1
If (passwordMismatchCount < 3) Then
Call MsgBox("パスワードが違います。再度入力をしてください", vbOKOnly & vbCritical)
Resume PasswordInput
ElseIf (passwordMismatchCount = 3) Then
Call E_Error.RaiseUnrecoverableError("パスワードの解除に失敗しました。処理を中断します。")
Exit Sub
End If
UnlockSucceed:
' 2シート目以降のパスワードを解除
Dim i As Long
For i = 2 To worksheetNames.Count
Set ws = U_Book.getWorksheet(wb, worksheetNames.Item(i))
Call ws.Unprotect(inputPassword)
Next
Call MsgBox("パスワードの解除に成功しました。", vbOKOnly & vbInformation, "パスワード解除")
End Sub
' セル値を可視化する
Private Sub visibleSheetsValue(wb As workbook, worksheetNames As Collection)
Dim i As Long
Dim ws As Worksheet
For i = 1 To worksheetNames.Count
Set ws = U_Book.getWorksheet(wb, worksheetNames.Item(i))
ws.Cells.Interior.ColorIndex = xlNone
ws.Cells.Font.Color = RGB(0, 128, 128)
Next
End Sub
' P04_ImportXXMaster
Option Explicit
' マスタAをインポートする
Public Sub MasterAImport(toolBook As workbook, releaseDate As String)
On Error GoTo AnyError
' マスタファイルを開く
Dim masterStoredFolderPath As String
masterStoredFolderPath = U_Config.getValue(eConfigKey.MasterAStoredFolder)
Dim masterFileName As String
masterFileName = U_Config.getValue(eConfigKey.MasterABookName)
Dim masterBook As workbook
Set masterBook = openMasterBook(masterStoredFolderPath, masterFileName, releaseDate)
' 対象シートを取得する
Dim sourceSheetName As String
sourceSheetName = U_Config.getValue(eConfigKey.MasterASourceSheetName)
Dim sourceSheet As Worksheet
Set sourceSheet = U_Book.getWorksheet(masterBook, sourceSheetName)
' インポートを行う
Dim destinationSheet As Worksheet
Dim sourceAddress As String
Dim destinationAddress As String
Set destinationSheet = toolBook.worksheets(C_Const.MASTER_A_DESTINATION_SHEET_NAME)
sourceAddress = U_Config.getValue(eConfigKey.MasterASourceAddress)
destinationAddress = U_Config.getValue(eConfigKey.MasterADestinationAddress)
Call import(sourceSheet, sourceAddress, destinationSheet, destinationAddress)
' マスタファイルを閉じる
Call U_ExcelBook.closeWorkbookSafety(masterBook, False)
Exit Sub
AnyError:
Dim tempErrNo As Long
Dim tempErrDescription As String
tempErrNo = Err.Number
tempErrDescription = Err.Description
Call U_Book.closeWorkbookSafety(masterBook, False)
Call Err.raise(tempErrNo, , tempErrDescription)
End Sub
' マスタファイルを開く
Private Function openMasterBook(masterFolderPath As String, masterFileName As String, toolReleaseDate As String) As workbook
Dim fso As FileSystemObject
Set fso = New FileSystemObject
' マスタファイルを取得する
Dim masterFile As File
Set masterFile = U_File.findSingleFileByName(masterFolderPath, masterFileName)
' ファイル名の更新日をチェックする
Dim masterUpdatedDate As String
masterUpdatedDate = Right(fso.GetBaseName(masterFile.Path), 8)
masterUpdatedDate = Left(masterUpdatedDate, 6)
Dim expectedUpdateDate As String
expectedUpdateDate = Left(toolReleaseDate, 6)
If (masterUpdatedDate <> expectedUpdateDate) Then
Dim errorMessage As String
errorMessage = "対象マスタのファイル名の更新月がツールのリリース月と一致しません。" & vbCrLf & vbCrLf _
& "[期待値] " & expectedUpdateDate & vbCrLf _
& "[実際値] " & masterUpdatedDate & vbCrLf & vbCrLf _
& "[ファイルパス] " & vbCrLf & masterFile.Path & vbCrLf
Call E_Error.RaiseUnrecoverableError(errorMessage)
End If
' マスタファイルを開く
On Error GoTo WorkBookOpenFailed
Dim wb As workbook
Set wb = Workbooks.Open(masterFile.Path, ReadOnly:=True)
Set openMasterBook = wb
On Error GoTo 0
Exit Function
WorkBookOpenFailed:
Call E_Error.RaiseUnrecoverableError("マスタファイルを開くことができませんでした。" & vbCrLf & vbCrLf _
& "[ファイルパス] " & vbCrLf & masterFile.Path)
End Function
' インポートを行う
Private Sub import(sourceSheet As Worksheet _
, sourceAddress As String _
, destinationSheet As Worksheet _
, destinationAddress As String)
Dim source As range
Dim destination As range
Set source = sourceSheet.range(sourceAddress)
Set destination = destinationSheet.range(destinationAddress)
Dim sourceHeader As range
Dim destinationHeader As range
Set sourceHeader = source.Rows(1).Cells
Set destinationHeader = destination.Rows(1).Cells
' ヘッダーをチェックする
Call validateHeader(sourceHeader, destinationHeader)
' 終端行を取得する
Dim lastRow As Long
lastRow = getLastRow(source)
' 値をインポートする
Call destinationSheet.Cells.ClearContents
destinationSheet.Cells.NumberFormatLocal = "@"
' 値をコピーする
destination.Resize(lastRow - 1).Value = source.Resize(lastRow - 1).Value
End Sub
' インポート元/先でヘッダーの定義が一致することを確認する
Private Sub validateHeader(sourceHeader As range, destinationHeader As range)
' 列数が一致するか
If (sourceHeader.Cells.Count <> destinationHeader.Cells.Count) Then
Call E_Error.RaiseUnrecoverableError("ヘッダーの列数が一致しません" & vbCrLf & vbCrLf _
& "[インポート元] " & sourceHeader.Cells.Count & " 列" & vbCrLf _
& "[インポート先] " & destinationHeader.Cells.Count & " 列")
End If
' ヘッダー名が一致するか
Dim i As Long
For i = 1 To sourceHeader.Cells.Count
Dim sourceHeaderName As String
Dim destinationHeaderName As String
sourceHeaderName = Trim(sourceHeader.Cells(1, i).Value)
destinationHeaderName = Trim(destinationHeader.Cells(1, i).Value)
If (sourceHeaderName <> destinationHeaderName) Then
Call E_Error.RaiseUnrecoverableError("インポート元とインポート先で" & i & "番目のヘッダー名が異なります" & vbCrLf _
& "[列番号] " & i & vbCrLf _
& "[インポート元] " & sourceHeaderName & vbCrLf _
& "[インポート先] " & destinationHeaderName)
End If
Next
End Sub
' 終端行を取得する
' 指定列範囲のうち、最も下側に値が入っている行を終端とする
Private Function getLastRow(range As range) As Long
Dim header As range
Set header = range.Rows(1)
Dim maxLastRow As Long
maxLastRow = -1
Dim ws As Worksheet
Set ws = range.Worksheet
Dim cell As range
For Each cell In header.Cells
Dim currentLastRow As Long
currentLastRow = ws.Cells(ws.Rows.Count, cell.Column).End(xlUp).Row
If (maxLastRow < currentLastRow) Then
maxLastRow = currentLastRow
End If
Next
getLastRow = maxLastRow
End Function
' U_Book
Option Explicit
' シートを取得する
Public Function getWorksheet(wb As workbook, sheetName As String) As Worksheet
If (wb Is Nothing) Then
E_Error.RaiseProgramBugError ( _
"ブックが開かれていないため、シートの取得に失敗しました。" & vbCrLf & vbCrLf _
& "[シート名] " & sheetName)
End If
Dim ws As Worksheet
On Error GoTo WorksheetNotFound
Set ws = wb.worksheets(sheetName)
Set getWorksheet = ws
Exit Function
WorksheetNotFound:
E_Error.RaiseUnrecoverableError ( _
"ブック中に指定の名前を持つシートが存在しません。" & vbCrLf & vbCrLf _
& "[ファイル名] " & wb.Name & vbCrLf _
& "[シート名] " & sheetName)
End Function
' ブックを閉じる
Public Sub closeWorkbookSafety(wb As workbook, isSave As Boolean)
If Not (wb Is Nothing) Then
On Error Resume Next
Call wb.Close(isSave)
On Error GoTo 0
End If
End Sub
' U_Config
Option Explicit
Private Const SHEET_NAME As String = "定義"
Private Const CONFIG_BEGIN_ROW As Long = 3
Private Const CONFIG_KEY_COLUMN As Long = 1
Private Const CONFIG_VALUE_COLUMN As Long = 2
' キー名
Public Enum eConfigKey
backupFolder = 1
releaseFolder
toolName
MasterAStoredFolder
MasterABookName
MasterASourceSheetName
MasterASourceAddress
MasterADestinationAddress
MasterBStoredFolder
MasterBBookName
MasterBImportSheetName
MasterCStoredFolder
MasterCBookName
MasterCImportSheetName
End Enum
' コンフィグの値を取得する
Public Function getValue(key As eConfigKey)
Dim japaneseKeyName As String
japaneseKeyName = convertKeyToJapanese(key)
Dim ws As Worksheet
Set ws = ThisWorkbook.worksheets(SHEET_NAME)
Dim lastRow As Long
lastRow = ws.Cells(Rows.Count, CONFIG_KEY_COLUMN).End(xlUp).Row
Dim i As Long
For i = CONFIG_BEGIN_ROW To lastRow
If (ws.Cells(i, CONFIG_KEY_COLUMN) = japaneseKeyName) Then
getValue = ws.Cells(i, CONFIG_VALUE_COLUMN)
Exit Function
End If
Next
Call E_Error.RaiseInvalidConfigError( _
"パラメータが定義されていません。" & vbCrLf _
& "パラメータキー名:" & japaneseKeyName)
End Function
' コンフィグキー名に対応する日本語キー名を取得する
Private Function convertKeyToJapanese(key As eConfigKey) As String
Dim dic As Dictionary
Set dic = New Dictionary
Call dic.Add(eConfigKey.backupFolder, "バックアップフォルダ")
Call dic.Add(eConfigKey.releaseFolder, "リリースフォルダ")
Call dic.Add(eConfigKey.toolName, "ツール名")
Call dic.Add(eConfigKey.MasterABookName, "マスタAファイル名")
Call dic.Add(eConfigKey.MasterAStoredFolder, "マスタA格納フォルダパス")
Call dic.Add(eConfigKey.MasterASourceSheetName, "マスタAインポート元シート名")
Call dic.Add(eConfigKey.MasterASourceAddress, "マスタAインポート元範囲")
Call dic.Add(eConfigKey.MasterADestinationAddress, "マスタAインポート先範囲")
Call dic.Add(eConfigKey.MasterBStoredFolder, "マスタB格納フォルダパス")
Call dic.Add(eConfigKey.MasterBBookName, "マスタBファイル名")
Call dic.Add(eConfigKey.MasterBImportSheetName, "マスタBシート名")
Call dic.Add(eConfigKey.MasterCStoredFolder, "マスタC格納フォルダパス")
Call dic.Add(eConfigKey.MasterCBookName, "マスタCファイル名")
Call dic.Add(eConfigKey.MasterCImportSheetName, "マスタCシート名")
If Not (dic.Exists(key)) Then
Call E_Error.RaiseProgramBugError( _
"configKeyに存在しないキー値が渡されました。" & vbCrLf _
& "キー値:" & key)
End If
convertKeyToJapanese = dic.Item(key)
End Function
' U_File
Option Explicit
' パスを結合する
Public Function joinPath(path1 As String, path2 As String) As String
joinPath = path1 & "/" & path2
End Function
' フォルダ内のファイル名の一致するすべてのファイルを取得する
Public Function findFilesByName(folderPath As String, fileName As String) As Collection
Dim fso As FileSystemObject
Set fso = New FileSystemObject
Dim targetFolder As Folder
' フォルダが存在しない場合
If Not fso.FolderExists(folderPath) Then
Call E_Error.RaiseUnrecoverableError( _
"指定されたパスが存在しません。" & vbCrLf & vbCrLf _
& "[フォルダパス]" & vbCrLf & folderPath)
End If
Set targetFolder = fso.GetFolder(folderPath)
Dim regex As RegExp
Set regex = New RegExp
regex.Pattern = fileName
Dim matchedFiles As Collection
Set matchedFiles = New Collection
Dim f As File
For Each f In targetFolder.files
If (regex.test(f.Name)) Then
' 隠しファイルでなければ
' 0x0010 : 2 → 隠しファイル属性
If ((f.Attributes And 2) = 0) Then
Call matchedFiles.Add(f)
End If
End If
Next
Set findFilesByName = matchedFiles
End Function
' フォルダ内のファイル名が一致するファイルを取得する
' 一致するファイルが0 または2以上である場合、例外を送出する
Public Function findSingleFileByName(filePath As String, fileName As String) As File
Dim files As Collection
Set files = findFilesByName(filePath, fileName)
If (files.Count = 0) Then
Call E_Error.RaiseUnrecoverableError( _
"指定されたフォルダ内にファイルが存在しません。" & vbCrLf & vbCrLf _
& "[フォルダ]" & vbCrLf & filePath & vbCrLf _
& "[検索ファイル名]" & vbCrLf & fileName)
ElseIf (files.Count >= 2) Then
Dim joinedFileNames As String
Dim f As File
For Each f In files
joinedFileNames = joinedFileNames & f.Name & vbCrLf
Next
Call E_Error.RaiseUnrecoverableError( _
"指定されたフォルダにファイルが複数存在します。" & vbCrLf & vbCrLf _
& "[フォルダパス]" & vbCrLf & filePath & vbCrLf _
& "[ファイル名]" & vbCrLf & joinedFileNames)
End If
Set findSingleFileByName = files.Item(1)
End Function