読者です 読者をやめる 読者になる 読者になる

FIELD NOTES: 書を持って街へ出よう

合同会社フィールドワークス プログラマ兼代表のブログ

Field Reportsの使い方:連続帳票の作成

通常「連続帳票」と言えばラインプリンタで使用する長い用紙のことを言いますが,ここでは,複数ページで構成される明細書状の帳票を連続帳票と呼んでいます。例えば,クレジットカードの利用明細,銀行のステートメントのような帳票です。

プログラムの作成

作成したプログラムは,以下のとおりです。
前回と同様に請求書のテンプレートを使用ています。

#!/usr/bin/env python
# coding: utf-8

from field import reports

param = {
    # テンプレート
    "template": [
        {"*": {"src": "./mitumori.pdf", "rows": 10}}
    ],

    # フィールド値
    "context": [
        {
            "date": "${NOW}",
            "number": "10R0002",
            "to": "△△△惣菜株式会社",
            "title": "今週の献立",
            "delivery_date": "2011-03-01",
            "delivery_place": "貴社指定場所",
            "payment_terms": "銀行振込",
            "expiration_date": "発行から3ヶ月以内",
            "stamp1": {"icon": "./stamp.png"},
            "table": [
                [1, "N001", "豚肉", "200g", 230, 460],
                [2, "G001", "エビ", "16尾", 40, 640],
                [3, "Y001", "小松菜", "1束", 120, 120],
                [4, "Y002", "ミツバ", "2束", 30, 60],
                [5, "Y003", "ショウガ", "1かけ", 30, 30],
                [6, "Y004", "大根", "1本", 120, 120],
                [7, "Y005", "水煮コーン", "1缶", 130, 130],
                [8, "K001", "片栗粉", "1袋", 150, 150],
                [9, "K002", "コーンスープの元", "2袋", 40, 80],
                [10, "T001", "", "1パック", 140, 140],
                [11, "T002", "牛乳", "1本", 180, 180],
                [12, "M001", "絹ごし豆腐", "2丁", 60, 120],
                [13, "C001", "みりん", "大さじ2", "", ""],
                [14, "C002", "しょうゆ", "大さじ7", "", ""],
                [15, "C003", "", "大さじ4", "", ""],
                [16, "C004", "砂糖", "大さじ3", "", ""],
                [17, "C005", "コショウ", "少々", "", ""],
                [18, "C006", "粉山椒", "少々", "", ""],
                [19, "C007", "赤唐辛子", "1パック", 180, 180],
                [20, "C008", "ごま油", "大さじ2", "", ""],
                [21, "C009", "サラダ油", "大さじ4", "", ""],
                [22, "S001", "", "大さじ3", "", ""],
                [23, "O001", "ホットケーキミックス", "200g", 80, 160],
                [24, "O002", "だし汁", "300ml", "", ""]
            ],
            "sub_total": 2570,
            "tax": 128,
            "total1": 2699,
            "total2": 2699,
            "remark": """エビと豆腐のショウガ炒め(4人分)
ミツバの卵とじ(4人分)
大根のピリ辛漬け(4人分)
コーンカップケーキ (4人分)"""
        }
    ],

    # スタイル指定
    "style": [
        {"*.date": {"datetime": "GGE年M月D日"}},
        {"*.delivery_date": {"datetime": "GGE年M月D日"}},
        {"*.total1": {"format": "###,###円"}},
        {"*.total2": {"format": "###,###円"}},
        {"*.sub_total": {"format": "###,###円"}},
        {"*.tax": {"format": "###,###円"}},
        {"*.table.*.[4:6]": {"format": "###,###円"}},
        {"[1:].stamp1": {"visible": False}},
        {"[1:].remark": {"visible": False}},
        {"[0:-1].sub_total": {"visible": False}},
        {"[0:-1].tax": {"visible": False}},
        {"[0:-1].total2": {"visible": False}},
        {"*.table.[1::2]": {"background-color": [220, 220, 255]}}
    ]
}

if __name__ == "__main__":
    reports.set_log_level(3)
    reports.render(param, "out.pdf")

ちょっと長いですが,出力結果を全ページ分掲載します。
f:id:fet:20110309154713p:image
f:id:fet:20110309154714p:image
f:id:fet:20110309154715p:image

テンプレートの指定

テンプレートの指定に関しては,ポイントが2つあります。

  {"*": {"src": "./mitumori.pdf", "rows": 10}}

1点目は,テンプレートの名称を「*」にしていることです。
テンプレート名が「*」だと,必要なページ数に応じて「0, 1, 2, …」のように自動的に展開されます。

この例では全部で3ページなので,以下のように指定したのと同等に解釈されます。

    "template": [
        {"0": {"src": "./mitumori.pdf", "rows": 10}},
        {"1": {"src": "./mitumori.pdf", "rows": 10}},
        {"2": {"src": "./mitumori.pdf", "rows": 10}}
    ],

2点目は「"rows": 10」で,テーブルの最大行数を指定していることです。
この数値を元にテーブルの要素が1ページに収まるかどうかを判断して,収まらなければ自動的にテーブルのデータを分割します。

内部的に,以下のようにページ毎にデータが分割されます。

    "context": [
        {
            "date": "${NOW}",
            "number": "10R0002",
            "to": "△△△惣菜株式会社",
            "title": "今週の献立",
            "delivery_date": "2011-03-01",
            "delivery_place": "貴社指定場所",
            "payment_terms": "銀行振込",
            "expiration_date": "発行から3ヶ月以内",
            "stamp1": {"icon": "./stamp.png"},
            "table": [
                [1, "N001", "豚肉", "200g", 230, 460],
                [2, "G001", "エビ", "16尾", 40, 640],
                [3, "Y001", "小松菜", "1束", 120, 120],
                [4, "Y002", "ミツバ", "2束", 30, 60],
                [5, "Y003", "ショウガ", "1かけ", 30, 30],
                [6, "Y004", "大根", "1本", 120, 120],
                [7, "Y005", "水煮コーン", "1缶", 130, 130],
                [8, "K001", "片栗粉", "1袋", 150, 150],
                [9, "K002", "コーンスープの元", "2袋", 40, 80],
                [10, "T001", "", "1パック", 140, 140]
            ],
            "sub_total": 2570,
            "tax": 128,
            "total1": 2699,
            "total2": 2699,
            "remark": """エビと豆腐のショウガ炒め(4人分)
ミツバの卵とじ(4人分)
大根のピリ辛漬け(4人分)
コーンカップケーキ (4人分)"""
        },
        {
            "date": "${NOW}",
            "number": "10R0002",
            "to": "△△△惣菜株式会社",
            "title": "今週の献立",
            "delivery_date": "2011-03-01",
            "delivery_place": "貴社指定場所",
            "payment_terms": "銀行振込",
            "expiration_date": "発行から3ヶ月以内",
            "stamp1": {"icon": "./stamp.png"},
            "table": [
                [11, "T002", "牛乳", "1本", 180, 180],
                [12, "M001", "絹ごし豆腐", "2丁", 60, 120],
                [13, "C001", "みりん", "大さじ2", "", ""],
                [14, "C002", "しょうゆ", "大さじ7", "", ""],
                [15, "C003", "", "大さじ4", "", ""],
                [16, "C004", "砂糖", "大さじ3", "", ""],
                [17, "C005", "コショウ", "少々", "", ""],
                [18, "C006", "粉山椒", "少々", "", ""],
                [19, "C007", "赤唐辛子", "1パック", 180, 180],
                [20, "C008", "ごま油", "大さじ2", "", ""]
            ],
            "sub_total": 2570,
            "tax": 128,
            "total1": 2699,
            "total2": 2699,
            "remark": """エビと豆腐のショウガ炒め(4人分)
ミツバの卵とじ(4人分)
大根のピリ辛漬け(4人分)
コーンカップケーキ (4人分)"""
        },
        {
            "date": "${NOW}",
            "number": "10R0002",
            "to": "△△△惣菜株式会社",
            "title": "今週の献立",
            "delivery_date": "2011-03-01",
            "delivery_place": "貴社指定場所",
            "payment_terms": "銀行振込",
            "expiration_date": "発行から3ヶ月以内",
            "stamp1": {"icon": "./stamp.png"},
            "table": [
                [20, "C008", "ごま油", "大さじ2", "", ""],
                [21, "C009", "サラダ油", "大さじ4", "", ""],
                [22, "S001", "", "大さじ3", "", ""],
                [23, "O001", "ホットケーキミックス", "200g", 80, 160],
                [24, "O002", "だし汁", "300ml", "", ""]
            ],
            "sub_total": 2570,
            "tax": 128,
            "total1": 2699,
            "total2": 2699,
            "remark": """エビと豆腐のショウガ炒め(4人分)
ミツバの卵とじ(4人分)
大根のピリ辛漬け(4人分)
コーンカップケーキ (4人分)"""
        }
    ],

逆に,上記のような分割済みのデータをプログラム側で作成することにより,テーブルの自動分割機能に頼らず改ページ位置を自由に決めることができます。

ちなみに「"rows": 10」は「"rows": {"table": 10}」の省略形で,すべてのテーブル要素の最大行数が同じ場合に限りこのような書き方ができます。1ページに復数のテーブルが存在し最大行数がそれぞれ異なる場合は,「"rows": {"table1": 10, "table2": 8}」のように書きます。

スタイル指定

以下の指定により,

  • 「承認印」「備考」の欄は,最初のページのみに表示する。
  • 「小計」「消費税」「税込価格」の欄は,最終ページのみ表示する。

というスタイル指定を行っています。

        {"[1:].stamp1": {"visible": False}},
        {"[1:].remark": {"visible": False}},
        {"[0:-1].sub_total": {"visible": False}},
        {"[0:-1].tax": {"visible": False}},
        {"[0:-1].total2": {"visible": False}},

また以下により,奇数行(1, 3, 5, … )の背景色を指定しています。

        {"*.table.[1::2]": {"background-color": [220, 220, 255]}}
広告を非表示にする