EXCEL VBA

コントロールブレイク応用編 VBAで帳票イメージを作成

EXCEL VBA

こんにちは! 健史です。

この春、当サイトを訪問下さっておられる方からメールを頂きました。

「帳票系の処理を作成したいが、ヒント・アドバイスをもらえれば」との内容でした。

すぐさまプログラムを作成し、データとともに送信致しました。

作りたてだったこともありコメントは1つしか入れていなかったのですが、理解されたとの返信があり安心しました。

ご本人の了解を得られたことから、記事にします。


尚、本記事で紹介するプログラムは、送信したプログラムをブラッシュアップしており、ロジックが若干異なっています。

当初作成したプログラムでも問題なく動作しますが、ずっと違和感というか「ちょっと何かが違う」と、どこか引っかかっていました。

記事を読み返していて気づきましたので、ブラッシュアップしました。

スポンサーリンク

処理概要

処理イメージ

・入力

・出力

※見やすいように罫線をハンドで挿入しています。

概要説明

◇EXCELシートにあるソート済みデータを先頭行から順次読み、
・明細帳表を作成する
・数量・金額などを集計

◇明細帳表1ページに最大6行印字したら、もしくはキーであるA列の[商品コード]が変わったら
・明細帳表は、改ページ

◇キーであるA列の[商品コード]が変わったら
・集計した項目を明細帳表とは別に集計帳表として印字

です。

なお「集計帳表」は厳密には「集計帳票」なのですが、この記事では「帳表」で統一しています。

プログラムで使用するExcelシートは
・シート1:入力データ
・シート2:明細帳表
・シート3:集計帳表
として作成しています。

そのためシート2、シート3を追加しておきます。

◇サンプルデータは、商品コード別・日毎の売り上げデータで、商品コード>日でソート済みです。
シート1に貼り付けます。

コピーしてペーストするときは、Excelシートのセル:A1で[右クリック]-[形式を選択して貼り付け(S)]から[貼り付ける形式(A)]に"テキスト"を選択します。

商品01	20220301	100
商品01	20220302	100
商品02	20220303	100
商品02	20220304	100
商品02	20220305	100
商品02	20220306	100
商品02	20220307	100
商品02	20220308	100
商品02	20220309	100
商品02	20220310	100
商品03	20220311	100

プログラム

'明細帳表の行数、明細行へ印字する最大行数の定義
    Const MAXPAGELINE = 10
    Const MAXLINE = 6
'各データをシートに見立てる。添え字は各データに対応
    Dim ix1, ix2, ix3 As Long
'帳表イメージ作成に使用する変数
    Dim cntLine, cntPage, wkGokei As Long
'コントロールブレイクの退避キー
    Dim savShohin As String
'帳表出力シートのクリア(オープン処理)
    Sheets(2).Cells.Clear
    Sheets(3).Cells.Clear
'明細ページに印字するページの初期設定
    cntPage = 0
'コントロールブレイク時に集計内容を印字する明細行のクリア
    ix3 = 0
'入力ファイルの1件目、読み込み
    ix1 = 1
'処理で集計する合計値をクリア
'メイン処理:入力ファイルが終わるまで繰り返す
    Do Until Sheets(1).Cells(ix1, 1) = ""
'初期処理 レベル1:ブレイクキーの退避、集計用ワークのクリア
      savShohin = Sheets(1).Cells(ix1, 1)
      wkGokei = 0
      Do Until Sheets(1).Cells(ix1, 1) = "" _
            Or savShohin <> Sheets(1).Cells(ix1, 1)
'初期処理 レベル2:ページ制御ワークの初期設定・初期処理、明細帳表の見出し印字
        cntPage = cntPage + 1   '明細帳表に印字するページをカウントアップ
        ix2 = (cntPage - 1) * MAXPAGELINE + 1 '明細帳表ページの先頭行を求める
        cntLine = 1   '明細帳表印字行数のカウント初期設定
'       見出しセット
        Sheets(2).Cells(ix2, 1) = Sheets(1).Cells(ix1, 1)
        Sheets(2).Cells(ix2, 5) = "ページ:" & cntPage
        ix2 = ix2 + 1  '1行空白をつくるための改行
'読み込み・集計:データ終了、キーブレイク、最大行数になるまで
        Do Until Sheets(1).Cells(ix1, 1) = "" _
            Or savShohin <> Sheets(1).Cells(ix1, 1) _
            Or cntLine > MAXLINE
          ix2 = ix2 + 1
          Sheets(2).Cells(ix2, 2) = Sheets(1).Cells(ix1, 2)
          Sheets(2).Cells(ix2, 3) = Sheets(1).Cells(ix1, 3)
          wkGokei = wkGokei + Sheets(1).Cells(ix1, 3)
          cntLine = cntLine + 1
          ix1 = ix1 + 1
        Loop
'ブレイク後の処理 レベル2:改ページ毎に後処理があれば記述
      Loop
'ブレイク後の処理 レベル1:キーブレイク時には集計帳表を印字
      ix3 = ix3 + 1
      Sheets(3).Cells(ix3, 1) = savShohin
      Sheets(3).Cells(ix3, 2) = wkGokei
    Loop
    MsgBox "処理終了!"

補足

多段階構造のコントロールブレイク処理

本プログラムは、2段階構造のブレイク処理になっています。

・レベル1
「ファイル終了」の次に大きな条件で制御しています。
 
ファイル終了、および、商品コードがブレイクしたら、集計帳票を印字する条件および処理です。
 
・レベル2
「商品コードのブレイク」を含みつつ、「商品コードのブレイク」の次の大きな条件で制御しています。
 
ファイル終了・商品コードのブレイク、および、ページにMAX行印字したら、改ページする条件および処理です。

改ページ処理

このプログラムで全体制御のポイントのひとつとしては、明細帳表の行数カウントに使用する[ix2]を制御することです。

COBOLの話で恐縮ですがプリンタへ出力する場合、改ページ命令があり、それにより1ページの印字位置がリセットされます。

改ページと同じ処理が
ix2 = (cntPage - 1) * MAXPAGELINE + 1 '明細帳表ページの先頭行を求める
です。

処理内で印字した行数をカウントアップし、最後まで印字した位置と印字ページと最大行数から次ページを求めるは複雑でわかりにくくなります。

あっ、↑の説明がわかりづらいですかね、すみません。

簡単に言うと、ix2をカウントアップしながら、次ページの先頭を求めるのは大変ということです。

明細帳表の最大行数を先頭で定義

Const MAXPAGELINE = 10

この値を変更することで、明細帳表の1ページの行数を任意に変更できます。

上記では、最大行数を10行にしています。

変えて試して頂ければと思います。

明細帳表に印字する最大明細数を先頭で定義

Const MAXLINE = 6

この値を変更することで、明細帳表1ページに印字する明細数を任意に変更できます。

上記では、最大明細数を6データにしています。

変えて試して頂ければと思います。


ただし最大行数(MAXPAGELINE) > 最大明細数(MAXLINE)の相関にする必要があることはわかって頂けると思います。

逆にしたテストはしていません。無駄ですから。

改ページを初期処理で実行している

仕様では、明細帳表の「改ページ条件」を「明細帳表1ページに最大6行印字したら、もしくはキーが変わったら」としています。

そのため「ブレイク後の処理で改ページする」かのように思えますが、初期処理で行っています。


見出し印字処理は、改ページを伴います。

データが1件でも存在すれば、必ず見出しは印字し、最初の1ページ目は不要ですが改ページ処理を実行しなければなりません。

最後のデータを読み終えた後は「キーブレイク状態」」にあり、集計したデータを印字する処理は必要です。

ですが改ページは必要ありません

よって、見出し印字=改ページ処理は、初期処理で実行するのが合理的です。


ここからは余談です・・・

このプログラムは1ページ目を改ページさせていません。当然!と思われますよね。

しかし、最初の1ページ目であっても改ページ処理を行うことがあります。

これにより何も印字されない用紙が紙送りされて出力されることがあります。

汎用コンピュータではほとんどでした。


多くの処理が実行されている汎用コンピュータ、コンピュータ室に専用の高速プリンタが設置され集中管理されています。

出力される帳表の種類も多く、印刷を終えた停止中のプリンタには「ページの途中まで印字した状態で帳表」が残っている場合があります。

その状態では、次のプログラムはページの途中から見出しを印字することになります。

そのようなことから「必ず改ページさせる」ことが必須でした。

何も印字していないページが残っている場合は、空白の用紙が紙送りされることになります。


オフコンなどの規模で業務担当近くにプリンタがある運用では、1ページ目でも改ページしないようにして!というニーズがほとんどでした。

帳表を出力したら、都度紙送りして取り出してしまい、プリンタのヘッダー(印字機構部分)は用紙の先頭に位置づけておくからです。

そして「1枚の紙も無駄にしたくない」ということでした。

最初にオフコンで帳表プログラムを作成したとき改ページさせたら、確認作業で「改ページしましたね!」と言われ、上記のことを言われた記憶があります。


本プログラムで「1ページ目も改ページさせるイメージにしたい」場合は、

ix2 = (cntPage - 1) * MAXPAGELINE + 1 '明細帳表ページの先頭行を求める

ix2 = cntPage * MAXPAGELINE + 1 '明細帳表ページの先頭行を求める

にします。

最後に

コンロトールブレイク処理の1つである帳表系プログラム、実際に印字していませんが、改ページ制御を伴う処理の記述方法を紹介しました。

これまで帳表系は、If文型のブレイク処理で作成してきました。

「いつかはループ型にしたい!」と思っていたのですが、今では帳表のプログラムを作成することもなく、実現に至りませんでした。

ですが、この記事で形にできたことをとても嬉しく思います!


COBOLで作成されておられる方にも理解しやすく応用しやすいように、WhileではなくUntilで作成しています。

COBOLにてIF文型で標準化されておられる組織・担当の方、今からでもUNTIL型にすることを検討し変更されてはいかがでしょうか。

構造化できてIF文型よりは見やすく、新人の方にも説明しやすく分かりやすいと思います。


参考にしていただければと思います。

EXCEL VBA
スポンサーリンク
- 面白かったらシェアお願いします! -
健史をフォローする
自分で改善

コメント