仕組み化

ボタン1つで200枚——食品表示ラベルをExcel VBAで自動化した弁当屋の記録【コード公開】

この記事の結論

食品表示ラベルを1枚ずつ手作業で印刷していた作業を、Excel VBA+ブラザーラベルプリンターで全自動化しました。ボタンを1つ押すだけで、当日の生産数分のラベルがすべて出てきます。

商品名・原材料・アレルゲン・消費期限・価格・QRコードまで自動出力。50枚の印刷に30分以上かかっていたのが、200枚でも約5分に変わりました。日付の手入力ミスも、委託販売先ごとのラベル切り替え忘れも、構造的に起きなくなっています。

200枚/日
1日の印刷枚数
30分→5分
50枚→200枚で逆に短縮
0
日付ミス・切り替え漏れ

前提:弁当屋の食品表示は量が多く、毎日変わる

私は人口5万人未満の地方都市で弁当・惣菜店を2店舗営んでいます(詳しくはこの人について)。弁当・惣菜には食品表示法に基づくラベルが必要です。商品名・原材料・アレルゲン・消費期限・価格など、決まった項目を毎日印刷して貼る作業が発生します。

厄介なのは「毎日枚数が変わる」こと。その日の生産数によって印刷枚数が変わるため、前日や当日の朝に決まった数だけ印刷する必要があります。

以前:商品を1つずつ呼び出して、日付を毎回手入力していた

導入前はスマートフォンと連携するラベルライターを使っていました。操作は、印刷したい商品をアプリで呼び出し、枚数を指定して送信する——これを商品の数だけ繰り返す手順です。

毎回、日付を手入力で変更する必要がありました。忘れたり、前日の日付のまま印刷してしまうミスが起きていました。委託販売先へ納品する商品は表示内容が一部異なるため、切り替えも手作業でした。

50枚の印刷に30分以上かかっていました。仕込みの時間を食う、毎日の地味な重荷でした。

以前使っていたもの

スマートフォン連携ラベルライター

1商品ずつアプリで呼び出す操作が必要で、日付変更も毎回手入力。枚数が増えると時間がかかる構造でした。

Amazonで見る ※ 当サイトはアフィリエイト広告(Amazon.co.jp)を利用しています。

仕組みの概要:2枚のシートとボタン1つ

Excelで2種類のシートを用意しています。

  • 商品DB:商品ごとの原材料・アレルゲン・消費期限(製造日からの日数)・価格・販売区分などを登録したマスタ
  • 生産指示:その日に作る商品名と生産数が入るシート(GASの注文管理システムと連動)

ボタンを1つ押すと、VBAが「生産指示」シートを上から読み、各商品の生産数分のラベルを順番に印刷します。消費期限は当日の日付+DBの日数で自動計算されるため、手入力は一切不要です。委託販売先向けの商品は、販売区分の設定に従って出力内容が自動で切り替わります。

QRコードはラベルのテンプレートに埋め込み済みで、毎回自動的に印刷されます。お客さんがスキャンすると店のページに飛ぶため、ラベルが広告としても機能しています。

【コード公開】Excel VBAのサンプルコード

使用ライブラリはブラザー公式の b-PAC SDK(無料)です。ブラザーの開発者向けサイトからダウンロードしてインストールすると、ExcelからCreateObjectで呼び出せます。

先にP-touch Editorでラベルのテンプレート(.lbxファイル)を作り、テキストオブジェクトに名前を付けておきます。VBAはそのオブジェクト名を指定して文字を差し込む仕組みです。

' ===================================================
' 食品表示ラベル 自動印刷システム
' 使用環境: Excel VBA + ブラザー b-PAC SDK 3.x
' ===================================================
Option Explicit

' ---- シート名 ----
Private Const WS_DB   As String = "商品DB"
Private Const WS_PROD As String = "生産指示"

' ---- 商品DBの列(1始まり)----
Private Const DB_NAME         As Long = 1  ' 商品名
Private Const DB_MATERIAL     As Long = 2  ' 原材料
Private Const DB_ALLERGEN     As Long = 3  ' アレルゲン(カンマ区切り)
Private Const DB_EXPIRY       As Long = 4  ' 消費期限(製造日からの日数)
Private Const DB_PRICE        As Long = 5  ' 価格(税込)
Private Const DB_CHANNEL      As Long = 6  ' 販売区分("自店" or "委託")
Private Const DB_CHANNEL_INFO As Long = 7  ' 委託先固有テキスト

' ---- 生産指示の列 ----
Private Const PROD_NAME As Long = 1  ' 商品名
Private Const PROD_QTY  As Long = 2  ' 当日生産数

' ---- テンプレートファイルパス ----
Private Const TEMPLATE_PATH As String = "C:\Labels\food_label.lbx"

'=====================================================
' メイン:ボタンに割り当てて使う
' 当日の生産指示シートを読み、全商品分を一括印刷する
'=====================================================
Public Sub PrintAllLabels()
    Dim wsProd  As Worksheet
    Dim wsDb    As Worksheet
    Dim lastRow As Long
    Dim i       As Long
    Dim qty     As Long
    Dim itemName As String

    Set wsProd = ThisWorkbook.Worksheets(WS_PROD)
    Set wsDb   = ThisWorkbook.Worksheets(WS_DB)
    lastRow    = wsProd.Cells(wsProd.Rows.Count, PROD_NAME).End(xlUp).Row

    For i = 2 To lastRow
        itemName = wsProd.Cells(i, PROD_NAME).Value
        qty      = CLng(wsProd.Cells(i, PROD_QTY).Value)
        If qty > 0 Then
            Call PrintLabel(itemName, qty, wsDb)
        End If
    Next i

    MsgBox "印刷完了(合計 " & _
           Application.WorksheetFunction.Sum(wsProd.Columns(PROD_QTY)) & _
           " 枚)", vbInformation
End Sub

'=====================================================
' 1商品分のラベルをqty枚印刷する
'=====================================================
Private Sub PrintLabel(itemName As String, qty As Long, wsDb As Worksheet)
    Dim dbRow   As Long
    Dim expDate As String
    Dim doc     As Object

    dbRow = FindProduct(itemName, wsDb)
    If dbRow = 0 Then
        Debug.Print "商品マスタに存在しません: " & itemName
        Exit Sub
    End If

    ' 消費期限:今日 + DBに登録した日数
    expDate = Format(Date + CLng(wsDb.Cells(dbRow, DB_EXPIRY).Value), "yyyy年mm月dd日")

    ' b-PAC でテンプレートを開いてフィールドを埋める
    Set doc = CreateObject("bpac.Document")
    If Not doc.Open(TEMPLATE_PATH) Then
        MsgBox "テンプレートを開けませんでした: " & TEMPLATE_PATH, vbCritical
        Exit Sub
    End If

    With doc
        .GetObject("NAME").Text     = itemName
        .GetObject("MATERIAL").Text = wsDb.Cells(dbRow, DB_MATERIAL).Value
        .GetObject("ALLERGEN").Text = FormatAllergen(wsDb.Cells(dbRow, DB_ALLERGEN).Value)
        .GetObject("EXPIRY").Text   = "消費期限 " & expDate
        .GetObject("PRICE").Text    = Format(wsDb.Cells(dbRow, DB_PRICE).Value, "¥#,##0") & "(税込)"

        ' 委託販売先は固有テキストを差し込む(自店なら空欄)
        If wsDb.Cells(dbRow, DB_CHANNEL).Value = "委託" Then
            .GetObject("EXTRA").Text = wsDb.Cells(dbRow, DB_CHANNEL_INFO).Value
        Else
            .GetObject("EXTRA").Text = ""
        End If

        ' qty枚 印刷
        .StartPrint "", bpacPrintOptionNone
        .PrintOut 1, qty, bpacPrintOptionNone
        .EndPrint
        .Close False
    End With

    Set doc = Nothing
End Sub

'=====================================================
' 商品名で商品DBを検索して行番号を返す(なければ0)
'=====================================================
Private Function FindProduct(itemName As String, wsDb As Worksheet) As Long
    Dim lastRow As Long
    Dim i       As Long
    lastRow = wsDb.Cells(wsDb.Rows.Count, DB_NAME).End(xlUp).Row
    For i = 2 To lastRow
        If wsDb.Cells(i, DB_NAME).Value = itemName Then
            FindProduct = i : Exit Function
        End If
    Next i
    FindProduct = 0
End Function

'=====================================================
' アレルゲン文字列を「(〇〇・〇〇を含む)」形式に整形
'=====================================================
Private Function FormatAllergen(raw As String) As String
    If Len(Trim(raw)) = 0 Then
        FormatAllergen = "アレルゲン:なし"
    Else
        FormatAllergen = "(" & raw & "を含む)"
    End If
End Function

シート名・列番号・テンプレートパスは定数にまとめているので、自分の環境に合わせてここだけ書き換えれば動きます。b-PAC SDKのインストールとP-touch Editorでのテンプレート作成が事前準備として必要です。

商品DBシートの構造

マスタは普通のExcelシートです。1行目をヘッダーにして、2行目以降に商品を1行ずつ登録します。

  • A列:商品名(生産指示シートの商品名と完全一致させる)
  • B列:原材料(例:米、鶏肉、醤油…)
  • C列:アレルゲン(例:小麦・卵・大豆)
  • D列:消費期限(日数)(製造当日なら0、翌日なら1)
  • E列:価格(税込金額)
  • F列:販売区分("自店" または "委託")
  • G列:委託先固有テキスト(委託先名や表示追加情報)

商品を追加・変更するときはこのシートを編集するだけです。VBAのコードは触りません。

結果:50枚30分以上→200枚5分、ミスがゼロに

仕組みが動き始めてから、変化は明確でした。

  • 50枚で30分以上かかっていたのが、200枚でも約5分(ボタンを押して5分後に全部出てくる)
  • 毎日変わる消費期限を手入力していたミスがゼロ
  • 委託先ごとのラベル切り替え漏れもゼロ
  • 生産数が増えても作業時間がほぼ変わらない

量が増えた(50枚→200枚)のに時間が短くなったのが、この仕組みの特徴です。手作業は量に比例して時間が増えますが、自動化は量が増えても時間はほぼ変わりません。

おまけ:QRコードで広告にもなっている

P-touch Editorのテンプレートにはあらかじめ店のQRコードを埋め込んでいます。毎日200枚のラベルに自動でQRコードが印刷されることになります。

委託販売先に並ぶ商品のラベルにもQRコードが入るため、買った人が手に取るたびに店のページに誘導できます。毎日200枚のラベルが、そのまま200枚の印刷広告として機能する——これは最初から狙って設計した部分です。ラベルの印刷コストは変わらず、広告効果だけが乗ってくる形です。

つまずいた点・注意したこと

  • 商品名の表記ゆれ:生産指示シートの商品名と商品DBの商品名が1文字でも違うと「商品が見つかりません」になります。生産指示シートは入力規則でリスト選択にして根本から防いでいます。
  • b-PAC SDKのインストール:初回のみ必要な作業ですが、ダウンロード先がわかりにくいです。ブラザーの開発者向けサイト(developer.brother.com)で「b-PAC」を検索すると見つかります。
  • テンプレートのオブジェクト名:P-touch Editorでテキストオブジェクトを配置したあと、プロパティでオブジェクト名を設定する手順を忘れがちです。VBAのGetObject("NAME")の引数と完全一致させる必要があります。

まとめ:商品を登録すれば、以後はボタン1つ

食品表示ラベルの印刷は毎日必ず発生する作業です。量が増えるほど、手作業のコストが膨らみます。一度仕組みを作れば、商品DBの登録だけで以後はメンテナンスがほぼ不要です。

コードは公開していますので、ブラザーのラベルプリンターをお使いであれば定数とテンプレートを合わせるだけで動くはずです。生産管理システムとの連動や委託先ごとの切り替えが必要ない場合は、DBと生産指示の2シートだけでシンプルに使えます。

現在使っているラベルプリンター

ブラザー ラベルプリンター

b-PAC SDKに対応したブラザー製ラベルプリンター。VBAからの自動印刷が可能で、飲食店の食品表示ラベルに使っています。

Amazonで見る ※ 当サイトはアフィリエイト広告(Amazon.co.jp)を利用しています。
使っているラベル用紙

ブラザー ラベル用紙

食品ラベル用に使っているラベル用紙。サイズ・素材は用途に合わせて選んでください。

Amazonで見る ※ 当サイトはアフィリエイト広告(Amazon.co.jp)を利用しています。

よくある質問FAQ

b-PAC SDKとは何ですか?どこで入手できますか?

ブラザー工業が提供する、ラベルプリンターをプログラムから操作するための無料ライブラリです。ブラザーの公式サイト(developer.brother.com)からダウンロードできます。インストール後はExcel VBAから「CreateObject("bpac.Document")」で呼び出せます。

VBAが書けなくても使えますか?

記事内のサンプルコードをそのままExcelに貼り付けて、シート名・列番号・テンプレートパスを自分の環境に合わせれば動きます。VBAの基本的なコピー&ペーストと定数の書き換えができれば、ゼロから書く必要はありません。

このシステムはブラザー以外のラベルプリンターでも使えますか?

b-PAC SDKはブラザー製ラベルプリンター専用です。他メーカーの場合はそのメーカーのSDKを使う必要があります。ただし「商品DBから当日生産数を読んで印刷を自動化する」という設計の考え方は、プリンターに関係なく応用できます。

食品表示法に必要な項目はすべてラベルに入れられますか?

商品DBに登録する項目を増やすことで対応できます。私の場合は商品名・原材料・アレルゲン・消費期限・価格・販売者情報・QRコードを出力しています。表示義務項目は食品の種類によって異なるため、所轄の保健所に確認したうえで必要な列をDBに追加してください。

委託販売先ごとにラベル内容を変えるにはどうすればいいですか?

商品DBに「販売区分」列(自店/委託)と「委託先固有テキスト」列を設けて、印刷時に区分を判定して出力内容を切り替えます。記事内のサンプルコードにこの処理が含まれています。一度登録すれば以後はメンテナンス不要です。