CodeZine(コードジン)

特集ページ一覧

【DioDocs帳票生成ハンズオン】強力な新機能「テンプレート構文」を使ってみる

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

目次

本格的な帳票の作成(1)

概要

 さて、ここまでは非常に簡単な例を示してきましたが、ここからは本格的な帳票を作成していきましょう。

 今回はデータソースにはデータテーブルを利用します。この帳票は請求書です。AdventureWorksでは請求書を作るにあたって、次のようなテーブルを利用することとします。

利用するテーブル
利用するテーブル

 なおテーブルはすべてではありませんし、列も利用するもののみを記述しています。それぞれのテーブルの概要は以下の通りです。

スキーマ テーブル 概要
Sales SalesOrderHeader 販売情報の概要
Sales SalesOrderDetail 販売情報の製品別明細
Production Product 製品マスター
Sales Customer 販売先の顧客
Sales Store 顧客の店舗
Person Person 顧客の担当者
Person Address 請求先住所
Person StateProvince 請求先住所の「州」

 このデータをもとに、次のような帳票を作成します。

 赤字部分が動的に設定する項目です。各項目のテーブルとのマッピングは次の通りです。

項目 セル テーブルおよび列
郵便番号 B2 Address.PostalCode
住所 B3 StateProvince.Name + ' '
Address.City + ' ' +
Address.AddressLine1 + ' ' +
Address.AddressLine2 + ' ' +
店舗名 B4 Store.Name
担当者名 B6 Person.FirstName + ' ' + Person.LastName
請求日 F2 請求書作成日
支払期限 F3 請求日+30日
No. A13 明細の行番号
製品名 B13 Product.Name
単価 C13 SalesOrderDetail.UnitPrice
数量 D13 SalesOrderDetail.OrderQty
割引率 E13 SalesOrderDetail.UnitPriceDiscount
小計 F13 SalesOrderDetail.LineTotal
税抜き価格 F14 SalesOrderHeader.SubTotal
消費税 F15 SalesOrderHeader.TaxAmt
送料 F16 SalesOrderHeader.Freight
請求総額 F18 SalesOrderHeader.TotalDue

 なお住所が日本表記で、担当者名が英語表記となっています。日本語帳票なので住所は日本表記にしました。ただデータは英語です。アルファベットの氏名を姓名順で表示するのは気持ちが悪いので今回はこのようにしました。

 上記項目のうち、Noから小計までは明細単位で行が増減します。

単一項目の設定

 まずは明細以外の値を設定しましょう。

クエリの修正

 まずはデータを取得するクエリを修正します。SelectInvoices.sqlを開き、以下の通り修正しましょう。

use AdventureWorks;

select 
  GETDATE() as InvitationDay,
  DATEADD(DAY, 30, GETDATE()) as DueDate,
  Address.PostalCode,
  ISNULL(StateProvince.Name, '') + ' '  + ISNULL(Address.City, '') + ' ' + ISNULL(Address.AddressLine1, '') + ' ' + ISNULL(Address.AddressLine2, '') as CustomerAddress,
  Store.Name as StoreName,
  Person.FirstName + ' ' + Person.LastName as CustomerPerson,
  SalesOrderHeader.SubTotal,
  SalesOrderHeader.TaxAmt,
  SalesOrderHeader.Freight,
  SalesOrderHeader.TotalDue
from
  Sales.SalesOrderHeader
  inner join Person.Address
    on SalesOrderHeader.BillToAddressID = Address.AddressID
  inner join Person.StateProvince
    on Address.StateProvinceID = StateProvince.StateProvinceID
  inner join Sales.Customer
    on Sales.SalesOrderHeader.CustomerID = Customer.CustomerID
  inner join Sales.Store
    on Customer.StoreID = Store.BusinessEntityID
  inner join Person.Person
    on Customer.PersonID = Person.BusinessEntityID
where
  SalesOrderHeader.SalesOrderID = 44533

 今回はSalesOrderHeader.SalesOrderIDに44533を固定で指定しています。

Excelテンプレートの修正

 続いてExcelのテンプレートを修正しましょう。各項目について、次のようにセルに値を設定してください。

項目 セル テンプレート
郵便番号 B2 {{Invoice.PostalCode}}
住所 B3 {{Invoice.CustomerAddress}}
店舗名 B4 {{Invoice.StoreName}}
担当者名 B6 {{Invoice.CustomerPerson}}
請求日 F2 {{Invoice.InvitationDay}}
支払期限 F3 {{Invoice.DueDate}}
税抜き価格 F14 {{Invoice.SubTotal}}
消費税 F15 {{Invoice.TaxAmt}}
送料 F16 {{Invoice.Freight}}
請求総額 F18 {{Invoice.TotalDue}}
テンプレートの修正
テンプレートの修正

 では実行してみましょう。次のような結果が得られるはずです。

生成結果
生成結果

複数行項目の設定

 さて、それでは商品ごとの明細行を設定しましょう。方法としては2つの選択肢があります。

  1. 明細用のDataTableを作って別名のデータソースとして登録する(もしくはDataSetに2つのDataTableを設定する)
  2. 明細を結合した複数行を返すクエリを利用する

 今回は後ほど「ワークシートテンプレート」を利用したいため、後者で行います。

クエリの修正

 では明細の取得をクエリに追加します。クエリを次のように修正しましょう。

use AdventureWorks;

select 
  GETDATE() as InvitationDay,
  DATEADD(DAY, 30, GETDATE()) as DueDate,
  Address.PostalCode,
  ISNULL(StateProvince.Name, '') + ' '  + ISNULL(Address.City, '') + ' ' + ISNULL(Address.AddressLine1, '') + ' ' + ISNULL(Address.AddressLine2, '') as CustomerAddress,
  Store.Name as StoreName,
  Person.FirstName + ' ' + Person.LastName as CustomerPerson,
  SalesOrderHeader.SubTotal,
  SalesOrderHeader.TaxAmt,
  SalesOrderHeader.Freight,
  SalesOrderHeader.TotalDue,
  Product.Name as ProductName,
  SalesOrderDetail.UnitPrice,
  SalesOrderDetail.OrderQty,
  SalesOrderDetail.UnitPriceDiscount,
  SalesOrderDetail.LineTotal
from
  Sales.SalesOrderHeader
  inner join Person.Address
    on SalesOrderHeader.BillToAddressID = Address.AddressID
  inner join Person.StateProvince
    on Address.StateProvinceID = StateProvince.StateProvinceID
  inner join Sales.Customer
    on Sales.SalesOrderHeader.CustomerID = Customer.CustomerID
  inner join Sales.Store
    on Customer.StoreID = Store.BusinessEntityID
  inner join Person.Person
    on Customer.PersonID = Person.BusinessEntityID
  inner join Sales.SalesOrderDetail
    on SalesOrderHeader.SalesOrderID = SalesOrderDetail.SalesOrderID
  inner join Production.Product
    on SalesOrderDetail.ProductID = Product.ProductID
where
  SalesOrderHeader.SalesOrderID = 44533

 お気づきかもしれませんが、明細以外の行は重複して取得されるため効率として必ずしも良くありません。

 単票であればクエリとDataTableを分けるのがやはり良いでしょうが、今回は後ほど複数PDFの一括生成をできるだけ簡単な実装で実現したいため、取得データの重複は諦めることにしています。

Excelテンプレートの修正

 クエリに引き続き、テンプレートも修正しましょう。各項目について、次のようにセルに値を設定してください。

項目 セル テンプレート
製品名 B13 {{Invoice.ProductName}}
単価 C13 {{Invoice.UnitPrice}}
数量 D13 {{Invoice.OrderQty}}
割引率 E13 {{Invoice.UnitPriceDiscount}}
小計 F13 {{Invoice.LineTotal}}
テンプレートの修正
テンプレートの修正

 では実行してみましょう。

実行結果
実行結果

 ん? ちょっとおかしいですね。先ほどのものと見比べてみましょう。

先ほどの生成結果と比較
先ほどの生成結果と比較

 追加されたセルによって、もともとあったセルが上書きされてしまっていますね。

 DioDocsでは複数行のデータ構造を作った場合、行単位で処理されているのではなく、セル単位で処理されているようです。したがって、デフォルトでは指定したセルの下に、値の数だけセルが上書きされます。

 解決方法はいくつかありますが、今回は「グローバルオプション」の「挿入モード」を利用します。次のコードを追加してください。

workbook.AddDataSource("Invoice", dataTable);

//Init template global settings
workbook.Names.Add("TemplateOptions.InsertMode", "EntireRowColumn");

workbook.ProcessTemplate();

 ProcessTemplateの前にグローバルセッティングを追加しています。では実行してみましょう。

下の行がつぶれなくなった
下の行がつぶれなくなった

 これで下の行がつぶれず、スライドされるようになりましたね。とは言え、まだまだ粗があるので、細かな部分を修正していきましょう。


  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

バックナンバー

連載:クラウド時代にマッチする、ドキュメント生成・更新APIライブラリ「DioDocs(ディオドック)」
All contents copyright © 2005-2020 Shoeisha Co., Ltd. All rights reserved. ver.1.5