Docurain Labo

Docurainサービス開発日記

Docurainを使って年賀状作成にチャレンジ!

もうすぐ年賀状の季節です(2021年09月執筆)。企業ではだいたい12月頭くらいから、年賀状作成の作業に入るのではないでしょうか。多くの企業では外部のサービスを使ったり、年賀状作成ソフトウェアを使ったりしていますが、今年はDocurainを使って年賀状作成に挑戦してみるのはいかがでしょうか。

今回はハガキ向けに作成できる特殊なテンプレートをベースに、年賀状作成に挑戦してみます。

データ作成について

年賀状を作るにあたって、多くの企業では送付先の住所や宛名を表計算ソフトウェア(Excelなど)で管理しているかと思います。今回は次のような項目で準備したと想定します。

項目 内容
zipcode 郵便番号。7桁の数字
address1 住所1。都道府県 + 市区
address2 住所2。番地まで
address3 住所3。ビルなど
company 会社名 + 部署名
name 宛先のお名前

この項目に沿って、データを作成していきます。この内容を address.csv として、保存します。なお、この画像に記載されている住所、会社名、名前はすべてダミーデータで生成したものです。

f:id:moongift:20210928185702p:plain

自社情報について

自社情報はJSONで記述します。 entity.json というファイル名で、次のような内容になります。

{
  "company": "会社名",
  "name": "名前",
  "zipcode": "郵便番号(7桁の数字)",
  "address": "住所"
}

テンプレートについて

今回はハガキにぴったり印刷できる、特殊なテンプレートを使っています。 (このような用途にご興味があるDocurainユーザの方はご連絡ください)

このテンプレートでは、各列、行の幅が1mmになるように設計されており、位置合わせが容易になっています。

f:id:moongift:20210928185939p:plain

そして、こちらのテンプレートに合わせて、各項目を埋めていきます。年賀状なので、基本的に各項目は縦書きに設定しています。

配置について

年賀状では送付先の郵便番号、住所、宛名に加えて、自社の郵便番号、住所、宛名の位置が重要になります。今回は既存の年賀状の寸法に合わせて、次のように設計しています。

項目 開始位置(左上の位置) 高さ
郵便番号 上12mm 左45mm 48mm 9mm
住所1 上23mm 左85mm 8mm 100mm
住所2 上23mm 左77mm 8mm 100mm
住所3 上23mm 左69mm 8mm 100mm
会社名 上23mm 左55mm 10mm 100mm
担当者名 上23mm 左45mm 10mm 100mm

自社情報の配置は次の通りです。

項目 開始位置(左上の位置) 高さ
住所1 上67mm 左18mm 10mm 55mm
会社名 上67mm 左13mm 5mm 55mm
担当者名 上67mm 左8mm 5mm 55mm
郵便番号 上123mm 左6mm 29mm 7mm

各項目は必要に応じて均等割り付け(郵便番号)や下配置(名前)としています。郵便番号を各桁ごとに分けて設計することできますが、1mm以下での調整が必要になるので、今回は均等割り付けとしています。

そして同じ大きさのページを1つ作成し、ハガキの文面として作成します。今回はEB0182022寅ベーシック年賀状・018 | 年賀状2022(令和4年・寅年・とら) 素材 | 年賀状・無料ダウンロード | 年賀状ならブラザーの画像を使わせてもらいました。

f:id:moongift:20210928185959p:plain

テンプレートの繰り返し処理

ハガキは奇数ページが宛名面、偶数ページが文面という形になります。そこで、奇数ページがはじまる行の上の行、A列に次のように記述します。

A
#set($e=$ROOT)
#foreach ($c in $e.clients)

このように書くことで $!{e.company} とすれば自社の会社名が取得できたり、 $!{c.address1} とすれば送付先の住所1が取得できます(下記データ構造について、も参照してください)。また、偶数ページの最終行の下、A列には次のように記述します。

A
#end

これは上記の #foreach に対応する記述になります。このように書くことで、送付先ごとに宛名面および文面が生成されます。

テンプレートでの文字出力

テンプレートでは $!{〜} のように記述しています。!を入れることで、もし変換するキーワードがなかったとしても、$!{〜} のようなプログラマブルな文字列が消える仕組みです。

様を付ける

送付先の名前には「様」を末尾に付けています。これは $!{c.name} 様 のようにセルに記述するだけで対応できます。

データ構造について

今回は送付先の住所が複数存在します。そして、送付元(自社情報)が1つという形です。そこで、次のようなJSONを作成して、DocurainのAPIに送信します。

{ // 自社情報
  "company": "会社名",
  "name": "名前",
  "zipcode": "郵便番号",
  "address": "住所",
  "clients": [  // 送付先情報(配列)
    {
      "company": "会社名",
      "name": "名前",
      "zipcode": "郵便番号",
      "address1": "住所1",
      "address2": "住所2",
      "address3": "住所3",
    },
    {
      "company": "会社名",
      "name": "名前",
      "zipcode": "郵便番号",
      "address1": "住所1",
      "address2": "住所2",
      "address3": "住所3",
    },
    // 繰り返し
  ]
}

プログラミングコードについて

今回はRubyを使ってAPIを呼び出します。

利用するライブラリのインストール

HTTP用のライブラリを含め、次のライブラリを使っています。

  • faraday
  • faraday_middleware
  • ruby-filemagic

適当なディレクトリでGemfileを作成します。

$ bundle init
Writing new Gemfile to /path/to/dir/Gemfile

このファイルを編集します。今回は次のように変更しています。

# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

# gem "rails"
gem "faraday"
gem "faraday_middleware"
gem "ruby-filemagic"

そしてライブラリをインストールします。

$ bundle install

スクリプトの作成

適当なファイル(今回はupload.rbとしています)を作成し、ライブラリを読み込みます。CSV、JSONを扱うライブラリもインストールします。

require 'faraday'
require 'faraday_middleware'
require 'ruby-filemagic'

require 'json'
require 'csv'

DocurainのAPIを実行する処理

まず、Docurainの実行部分を作成します。ここからは api_call 関数の内容です。

# 帳票レンダリング(インスタント)APIコール
def api_call
  # この中に書きます
end

まず必要な変数を準備します。

token = 'YOUR_API_TOKEN'                  # Docurain API トークン
out_type = 'pdf'                          # 出力形式
template_path = './template.xlsx'         # テンプレートファイルパス
entity_json = JSON.parse(open('./entity.json').read)  # テンプレート置き換え文字列のJSON
path = "/api/instant/#{out_type}"         # APIのパス

住所を記述した address.csv を読み込みます。

# 住所用のCSVを読み込む
body = File.open('./address.csv').read
csv = CSV.new(body, headers: true, header_converters: :symbol, force_quotes: true)

Excel内にて縦書きを行う際の注意点と解決策して、以下が挙げられます。

  • -(ハイフン)が縦にならない
    -| に変換します
  • 数字は均等割り付けしても広がらない
    数字の間に (半角スペース)を入れます

この2つの処理を住所、郵便番号に対して行います。ちょっと冗長的なので、関数にまとめても良いでしょう。今回はわかりやすさのために、そのまま記述しています。

rows = csv.to_a.map do |row|
  # 郵便番号(数字7桁)を分解して、間にスペースを挿入
  row[:zipcode] = row[:zipcode].split(//).join(' ')
  # 住所に含まれる - を | に変換
  row[:address1] = row[:address1].gsub(/-/, '|')
  row[:address2] = row[:address2].to_s.gsub(/-/, '|')
  row[:address3] = row[:address3].to_s.gsub(/-/, '|')
  row.to_hash
end
# 郵便番号(数字7桁)を分解して、間にスペースを挿入
entity_json['zipcode'] = entity_json['zipcode'].split(//).join(' ')
# 住所に含まれる - を | に変換
entity_json['address'] = entity_json['address'].gsub(/-/, '|')
# JSONの clients の中に送付先顧客情報を追加
entity_json['clients'] = rows

次にテンプレートファイルのコンテンツタイプを取得します。

# テンプレートファイルのContent-Typeを取得
template_content_type = FileMagic.new(FileMagic::MAGIC_MIME).file(template_path, true)

これらの情報を使ってリクエストボディを作ります。

# リクエストボディの作成
params = {
  template: Faraday::FilePart.new(template_path, template_content_type),
  entity:  Faraday::ParamPart.new(entity_json, 'application/json')
}

後はFaraday(HTTPクライアント)を使って、APIを実行します。

# APIを実行する
conn = Faraday.new(:url => 'https://api.docurain.jp') do |builder|
  builder.request :multipart
  builder.adapter :net_http
end
# 認証ヘッダーの設定
conn.headers['Authorization'] = "token #{token}"
# 実行結果(レスポンス)を返す
conn.post(path, params)

これでAPI呼び出し処理の完成です。関数全体の内容は次のようになります。

require 'faraday'
require 'faraday_middleware'
require 'ruby-filemagic'

require 'csv'
require 'json'

# 帳票レンダリング(インスタント)APIコール
def api_call
  token = 'YOUR_API_TOKEN'                  # Docurain API トークン
  out_type = 'pdf'                          # 出力形式
  template_path = './template.xlsx'         # テンプレートファイルパス
  entity_json = JSON.parse(open('./entity.json').read)  # テンプレート置き換え文字列のJSON
  path = "/api/instant/#{out_type}"         # APIのパス
  # 住所用のCSVを読み込む
  body = File.open('./address.csv').read
  csv = CSV.new(body, headers: true, header_converters: :symbol, force_quotes: true)
  rows = csv.to_a.map do |row|
    # 郵便番号(数字7桁)を分解して、間にスペースを挿入
    row[:zipcode] = row[:zipcode].split(//).join(' ')
    # 住所に含まれる - を | に変換
    row[:address1] = row[:address1].gsub(/-/, '|')
    row[:address2] = row[:address2].to_s.gsub(/-/, '|')
    row[:address3] = row[:address3].to_s.gsub(/-/, '|')
    row.to_hash
  end
  # 郵便番号(数字7桁)を分解して、間にスペースを挿入
  entity_json['zipcode'] = entity_json['zipcode'].split(//).join(' ')
  # 住所に含まれる - を | に変換
  entity_json['address'] = entity_json['address'].gsub(/-/, '|')
  # JSONの clients の中に送付先顧客情報を追加
  entity_json['clients'] = rows
  # テンプレートファイルのContent-Typeを取得
  template_content_type = FileMagic.new(FileMagic::MAGIC_MIME).file(template_path, true)
  # リクエストボディの作成
  params = {
    template: Faraday::FilePart.new(template_path, template_content_type),
    entity:  Faraday::ParamPart.new(entity_json, 'application/json')
  }
  # APIを実行する
  conn = Faraday.new(:url => 'https://api.docurain.jp') do |builder|
    builder.request :multipart
    builder.adapter :net_http
  end
  # 認証ヘッダーの設定
  conn.headers['Authorization'] = "token #{token}"
  # 実行結果(レスポンス)を返す
  conn.post(path, params)
end

レスポンスを判定する

次にレスポンスの内容を判定して、レポートをファイル出力します。ここからは res_handle 関数の内容です。

# レスポンスハンドリング(適宜必要なハンドリングを行ってください)
def res_handle(res)
  # この中に書きます
end

まずHTTPステータスが200(正常終了)以外の場合はエラーメッセージを出して終了します。

if res.status != 200 # エラー判定
  puts res.body # エラーメッセージを返して終了
  return
end

次にレスポンスのContent-Typeから、出力するファイルの拡張子を指定します。

# 正常時、カレントディレクトリにファイル保存
# content-typeから拡張子に変換
extensions = {
  'application/pdf' => 'pdf',
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
  'application/vnd.ms-excel' => 'xls',
  'image/svg+xml' => 'svg',
  'image/png' => 'png',
  'image/jpeg' => 'jpg',
  'image/gif' => 'gif',
}
content_type = res.headers['content-type']
ext = extensions[content_type]

後はファイル名(今回は test に固定)を決めて、ファイル書き出しします。

# ファイル名を決めて書き出し
file_name = "test.#{ext}"
filePath = "#{Dir.pwd}/#{file_name}";
File.binwrite(filePath, res.body)
# メッセージを出力
puts "saved : #{filePath}"

この関数の全体像は次のようになります。

# レスポンスハンドリング(適宜必要なハンドリングを行ってください)
def res_handle(res)
  if res.status != 200 # エラー判定
    puts res.body # エラーメッセージを返して終了
    return
  end
  # 正常時、カレントディレクトリにファイル保存
  # content-typeから拡張子に変換
  extensions = {
    'application/pdf' => 'pdf',
    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
    'application/vnd.ms-excel' => 'xls',
    'image/svg+xml' => 'svg',
    'image/png' => 'png',
    'image/jpeg' => 'jpg',
    'image/gif' => 'gif',
  }
  content_type = res.headers['content-type']
  ext = extensions[content_type]
  # ファイル名を決めて書き出し
  file_name = "test.#{ext}"
  filePath = "#{Dir.pwd}/#{file_name}";
  File.binwrite(filePath, res.body)
  # メッセージを出力
  puts "saved : #{filePath}"
end

実行する

ではこの2つの関数をつないで実行します。

res = api_call  # APIコール
res_handle(res) # レスポンスハンドリング

実行すると、次のように結果が出力されます。

$ ruby upload.rb 
saved : /path/to/test.pdf

できあがったPDFの内容です。奇数ページが宛名、偶数ページが文面になります。

f:id:moongift:20210928190148p:plain

印刷する

後は両面印刷対応のプリンターや複合機で印刷します。まだ年賀状が販売される時期ではないので、Word 2019 for Mac:はがきの宛名印刷テンプレートを作成するにはの年賀状テンプレートを利用して、年賀状風に印刷してみました。

宛名側です。

f:id:moongift:20210928183014j:plain

文面側です。

f:id:moongift:20210928183041j:plain

まとめ

Docurainを使うことで、年賀状のような印刷物もExcelでデザインして作成できます。特殊なテンプレートが必要にはなりますが、1mm単位での正確な配置も指定できます。往復ハガキのような特殊な形式でも利用できます。

DocurainはWeb APIを介して利用するので、対象の宛先があっても素早く作成して、印刷が可能です。ぜひご利用ください!

帳票開発を、もっと簡単に「Docurain-ドキュレイン-」|帳票開発エンジン

Docurainを使って動的にグラフを作成する

Docurainを使えばExcelファイルをテンプレートとして、多彩な帳票やレポートを出力できます。報告書やレポートなどでは、グラフが使われることが多いです。Excelでレポートを作成したことがあれば、そのデータを使ってグラフを埋め込んだことがある方も多いでしょう。

今回はDocurainを使って動的に値を変更したグラフを作成します。グラフを使うことでビジュアル的にも、訴求力が向上したレポートを作成できるでしょう。

続きを読む

DocurainをDTP的に使いこなす(不動産風チラシを作る)

DocurainはExcelファイルをテンプレートとして、PDFや画像を生成します。主な用途としては納品書や請求書といった帳票が多いですが、実際にはそれだけに限らず利用されています。

今回はその一例として、不動産風チラシを作ってみます。

続きを読む

【エンジニア向け】これできっと分かる。Docurainのつまづきポイント解説します

Docurainは帳票エンジンと銘打っていますが、使いこなすともっともっとたくさんのことが実現できます。この記事でははじめてDocurainに触れる方が感じるであろう疑問点や、つまづきポイントを解説します。スムーズな帳票作成を実現させるためにも、ぜひご覧ください。

続きを読む

Docurainを試してみよう(サンプルデータを使って帳票を作成しよう)

DocurainはWeb APIを使って、簡単に納品書や請求書をはじめとする、帳票を生成するサービスになります。今回はこのDocurainの使い方について、サンプルテンプレートを利用して紹介します。

帳票作成APIの実行形式について

Docurainでは基本として、次の2パターンでの利用が可能です。

  • API実行時にテンプレートをアップロードする(インスタント)
  • 事前にテンプレートをアップロードしておく(事前保存テンプレート)

今回は、この2つの使い方について解説します。

トークンを作成する

Docurainを実行する際には事前にトークンを発行する必要があります。トークンはトークン | Docurainより作成が可能です。まず新規追加ボタンをクリックします。

f:id:moongift:20210719121134j:plain

形式として繰り返し使える通常トークンと、一度だけ使えるワンタイムトークンがあります。必要に応じて選択してください。また、APIを利用許可するIPアドレスを指定します。制限しない場合には *.*.*.* を指定してください。IPアドレスのフォーマットはこちらのドキュメントを参考にしてください。

また、通常トークンの場合は有効期限も指定します。厳密な設定が不要な場合には、十分に長い有効期限を設定してください。

f:id:moongift:20210719121240j:plain

この記事の中ではトークンは YOUR_TOKEN として記述していきますので、ご自身のものと置き換えつつ読み進めてください。

サンプルの帳票について

今回の記事で利用するサンプルの帳票は、公式ドキュメント中のものを利用します。展開してexamplesというフォルダがある状態での利用を想定しています。

f:id:moongift:20210719121319p:plain

インスタントでの実行例

インスタントでの実行は、テンプレートと置き換える文字列を一緒に渡します。 template 引数でテンプレートのExcelファイルを、 entity 引数で置き換え文字列のJSONファイルを指定します。サンプルの納品書は次のようになっています。

f:id:moongift:20210719121709p:plain

また、置き換えるデータ(納品書.json)の内容は次のようになります。

{
    "date": "2019-02-20",
    "customer": {
        "zip": "123-456",
        "address1": "東京都調布市松濤1146",
        "address2": "パレットハウス徳丸 109",
        "name": "水谷 知明"
    },
    "shop" : {
        "name": "Sample Shop Web通販事業部",
        "zip": "123-5678",
        "address": "東京都板橋区東坂下1-3-2",
        "staffName": "堀口"
    },
    "notes": "",
    "tax": 4152,
    "sum": 56052,
    "qrCode": "fsa89gb43wkfdakdsaf8v9ergbhjkfrtdlsgyre89sgf43qgfdr",
    "barCode": "2112345678900",
    "orders": [
        {
            "name": "AWE Sシリーズ CO2 減圧レギュレータ",
            "code": "2KW25-S",
            "count": 1,
            "unitPrice": 32400,
            "totalPrice": 32400,
            "notes": ""
        },
        {
            "name": " NESCO R134a ボールゲージ式マニホールドキット",
            "code": "AR44-FN",
            "count": 1,
            "unitPrice": 18900,
            "totalPrice": 18900,
            "notes": ""
        },
        {
            "name": " カネダ Oリング 油圧用 (ISO型)",
            "code": "NBR S10 7.8 Φ20",
            "count": 2,
            "unitPrice": 300,
            "totalPrice": 600,
            "notes": ""
        },
        {
            "name": "  以下余白"
        }
    ]
}

curlコマンドで書くと次のようになります。

curl -X POST https://api.docurain.jp/api/instant/pdf \
  -H 'Authorization:token YOUR_TOKEN' \
  -H 'Content-Type:multipart/form-data' \
  -F 'template=@./examples/納品書.xlsx;type=application/vnd.ms-excel' \
  -F 'entity=@./examples/納品書.json;type=application/json' \
  -o delivery1.pdf

これを実行すると delivery1.pdf というファイルが生成されて、返却されます。

f:id:moongift:20210719121350j:plain

事前保存テンプレートでの実行例

テンプレートをアップロードする

事前保存テンプレートの場合は、まず管理画面のテンプレートにて、テンプレートとなるExcelファイルをアップロードします。

f:id:moongift:20210719121424j:plain

テンプレート名は英数字と _ 、そして - のみ利用可能です。このテンプレート名はAPIのURLにも利用されるので、識別しやすいものを設定してください。

実行する

事前保存テンプレートで実行する場合にはURLが変わるので注意してください。 TEMPLATE_NAME となっている部分を、上記のテンプレート名と置き換えてください。後は、先ほどと同じように置き換え文字列が入ったJSONファイルを指定して実行します。

curl -X POST https://api.docurain.jp/api/pdf/TEMPLATE_NAME \
  -H 'Authorization:token YOUR_TOKEN' \
  -H 'Content-Type:application/json' \
  --data-binary @./examples/納品書.json \
  -o delivery1.pdf

上記コマンドの場合も、 delivery1.pdfというPDFファイルが生成されます。

管理画面で作成する

Docurainでは管理画面でもPDFを作成できます。API利用前にテストで実行される際にお試しください。こちらでもインスタント、事前保存テンプレート双方が利用できます。

f:id:moongift:20210719121501j:plain

管理画面から実行する場合には、作成してあるトークンから選択します。あらかじめ作成しておいてください。

まとめ

テンプレートが定型で、繰り返し利用できる場合には事前保存テンプレートを使うのが良いでしょう。クライアントごとに体裁が異なったり、一時的に特別なテンプレートを利用する場合にはインスタントをご利用ください。

帳票開発を、もっと簡単に「Docurain-ドキュレイン-」|帳票開発エンジン

複雑な集約条件においても数式自動シフトが使えるようになりました

明細のある見積書や請求書のような帳票では、ページごとの合計金額、全ページの総合計金額のような項目がよくあります。

1ページ目 2ページ目
f:id:yutay:20210729172942p:plain:w300 f:id:yutay:20210729172947p:plain:w300

その際にDourainでは簡単にページごと、グループごと、全ページの各単位で金額を計算することが可能です。

作成するテンプレートで範囲の定義を行い、その範囲ごとに計算を行うようにすることで実現させます。

早速やってみましょう!

上に載せたような見積書を作成します。

サンプルのテンプレートとデータ(JSON)です。
サンプルダウンロード

範囲について

範囲は#scopeを使用して定義します。

今回、範囲は以下の「全て」・「顧客」・「ページ」の3つの範囲を定義します。

f:id:yutay:20210729164123p:plain

範囲の定義は以下のように書きます。それぞれの範囲を#scope - #endで囲みます。

#scopeの構文は次の通りです。

#scope([範囲の名称] as [変数名])

f:id:yutay:20210729192410p:plain

「全て」の範囲定義の通り、as [変数名] は省略可能です。範囲の名称を変数として使用する必要がない場合(範囲の名称が固定で良い場合)は、as [変数名] を省略します。

これで範囲の定義は完了です。

範囲を使用した計算について

範囲ごとに計算を行うにはDR.SHIFT_FORMULA()を使用します。

DR.SHIFT_FORMULA()は数式の自動シフトを適応可能にしてくれる機能です。*1

依然として Excelの数式そのまま であるため、Excelがもつ数式記述支援機能(構文チェック、引数チェックなど)を全て活用することができます!!

DR.SHIFT_FORMULA()の構文は次の通りです。

=DR.SHIFT_FORMULA(値or式,"scope=[#scope()で代入している変数], target=[対象とするセル範囲]")

scopeを省略した場合、そのセルが所属する印刷範囲を範囲として見なします。

targetの値は、

  • all スコープ内の全ての範囲
  • nearest スコープ内の最近傍セル範囲

のいずれかを指定します。省略した場合はnearestと判定されます。

では、先ほど#scopeにて定義した範囲をDR.SHIFT_FORMULA()から参照して計算を行います。

サンプルを確認しつつ以下をご覧ください。

  • 全ページの合計金額

    f:id:yutay:20210730095220p:plain =DR.SHIFT_FORMULA(SUM(D20),"scope=全て, target=all")とすることで、範囲名称が「全て」の範囲の合計金額セル(D20)を参照しSUMにて合計を算出します。それにより総合計金額を出力します。

  • 顧客ごとの合計金額

    f:id:yutay:20210730100414p:plain =DR.SHIFT_FORMULA(SUM(J31),"scope=$outerScope, target=all")とすることで、範囲名称が$outerScope(顧客ごと)の範囲のページ合計セル(J31)を参照しSUMにて合計を算出します。それにより顧客ごとの合計金額を出力します。

  • ページごとの合計金額

    f:id:yutay:20210730100428p:plain =DR.SHIFT_FORMULA(SUM(J29:J29),"scope=$innerScope, target=all")とすることで、範囲名称が$innerScope(ページごと)の範囲の明細金額セル(J29)を参照しSUMにて合計を算出します。それによりページごとの合計金額を出力します。

  • 明細ごとの金額 f:id:yutay:20210729194310p:plain =DR.SHIFT_FORMULA(G29*I29)とすることで、数量(G29)*単価(I29)を算出します。それにより明細ごとの合計金額を出力します。 DR.SHIFT_FORMULAを使うことで明細行が複数になってもG29・I29のセル範囲は自動シフトするため、何も意識せずテンプレート上で該当のセルを指定すれば良いです。

データを準備して出力

今回使用するデータは以下です。

{
  "顧客リスト": [
    {
      "顧客名": "株式会社 顧客1",
      "見積No": 1127,
      "見積日": "2021-07-29",
      "明細": [
                { "名称": "項目1-1", "数量": 1, "単位": "", "単価": 10000 },
                { "名称": "項目1-2", "数量": 2, "単位": "", "単価": 10000 },
               ...中略...
                { "名称": "項目1-17", "数量": 17, "単位": "", "単価": 10000 }
      ]
    },
    {
      "顧客名": "株式会社 顧客2",
      "見積No": 1128,
      "見積日": "2021-07-29",
      "明細": [
                { "名称": "項目2-1", "数量": 1, "単位": "", "単価": 20000 },
                { "名称": "項目2-2", "数量": 2, "単位": "", "単価": 20000 },
               ...中略...
                { "名称": "項目2-20", "数量": 20, "単位": "", "単価": 20000 }
      ]
    }
  ]
}

出力結果です。

(サンプルテンプレートのA9セルにて明細15行ごとに分割して改ページするようにしています)

1ページ目(総合計)
f:id:yutay:20210729195227p:plain:w300
2ページ目(顧客1の1ページ目) 3ページ目(顧客1の2ページ目)
f:id:yutay:20210729195256p:plain:w300 f:id:yutay:20210729195303p:plain:w300
4ページ目(顧客2の1ページ目) 5ページ目(顧客2の2ページ目)
f:id:yutay:20210729195308p:plain:w300 f:id:yutay:20210729195319p:plain:w300

それぞれの範囲で計算され、「ページ合計金額」、「顧客ごとの合計金額」、「全ての合計金額」が正しく計算されてますね。

今回作成したサンプルは、無料トライアルページからも登録不要で試すことが出来ます。

他にも様々な機能がありますので、ぜひアカウント無料登録してマニュアルを参照しお試しください。

*1:数式の自動シフト:DocurainにてExcel/PDF出力後に命令行が削除されることを意識せずに、Docurainテンプレート上でExcelのセル範囲をそのまま使えることを指します。Docurainテンプレートにて、とあるセルを参照している数式がある場合、命令行が削除された後の行番号を考慮した数式でなければなりませんが、その命令行が削除された後の行番号に自動に適応します。