5
Help us understand the problem. What are the problem?

posted at

updated at

kintoneにサーバーレスで印刷機能を実装する(AWS Lambda + Thinreports)

最初に

kintoneに限らず業務系のWebサービスでは、帳票印刷が必要になってきます。
(まだまだ紙で印刷したり、メールに添付したり、ダウンロードしたり)

ここでは、kintone の印刷機能について調査と独自実装する場合の方法についての考え方をまとめたいと思います。

主な kintone 帳票印刷ソリューション

(カッコ内は販売会社名)

kintone連携ソリューション公式ページから個人的に使ったり、知っている主な製品をまとめてみました。
(ここでの印刷はPDFで出力することを指しています)

  • プリントクリエイター(トヨクモ)
    • コース毎の月額定額のサブスクリプションモデル。専用のサービス上で印刷レイアウトをプレビューで確認しながら作成可能。印刷枚数は無制限。
    • 専用サービスにログインして、PDFで作成したテンプレートファイルをアップロード。アップロードしたPDFテンプレート上にkintoneのフィールドをマッピングしていきます。(マウスで矩形を作り、そこにフィールドをマッピングする感じです)
  • RepotoneU PDF(株式会社ソウルウェア)
    • PDF印刷専用プラグイン型買い切り制。プラグイン設定画面にて印刷レイアウトを作成。Excel出力版や毎月一定額を支払うサブスクリプションモデルもあります。
    • プラグイン画面でPDFで作成したテンプレートファイルをアップロード。アップロードしたPDFテンプレート上にkintoneのフィールドをマッピングしていきます。(マウスで矩形を作り、そこにフィールドをマッピングする感じです)
  • ReportsConnect for kintone(株式会社ケーピーエス)
    • 月額定額のサブスクリプション。PCにインストールしたレイアウトツールiReportデザイナーを使ってレイアウトを作成。無料枠有り。
  • gusuku Customine(アールスリーインスティテュート)
    • ノーコードでkintoneにJavaScriptのカスタマイズを追加できるサービス。このサービスのできることに「Excel/PDF/CSV」の出力機能があります。Customineでは、Excelでテンプレートを作成し、テンプレートにてkintoneのフィールドコードをマッピングします。そのテンプレートにkintoneのレコードのフィールドの値を流し込むような仕組みです。テンプレートのマッピングはこちらのマニュアルを参照
    • https://support.gusuku.io/ja-JP/support/solutions/articles/36000303634
  • csvラポ kintone連携プラグイン
    • kintoneの一覧印刷に特化した帳票サービス。レイアウトを必要としないのが特徴。

Thinreports を使った帳票印刷

サーバーレスが普通になってきた昨今、帳票印刷はサーバーレスで自前で実装することも可能になってきましたね。

概要

Thinreportsは専用のデザイナーアプリ Thinreports EditorとRubyのSDK Thinreports Generatorを使ってPDFを出力するソリューションです。

デザイナーではドラッグ&ドロップでレイアウトを作成出来ます。
データとレイアウトファイルを繋ぐコードをRubyで記述します。

専用サーバーで利用する

Ruby on Rails 等の Ruby製 WebアプリケーションフレームワークにレイアウトファイルとSDKをインストールすることでWebアプリケーションにPDF出力機能を追加することが出来ます。

サーバーレスで利用する

AWS Lambda などを利用することで専用のサーバーを自前で用意することなくPDF出力機能を追加出来ます。

サーバーレスでPDF処理を実装する

システム構成

システム構成イメージです。

kintoneAwsLambdaThinreportsPDF.png

AWS Lambda を利用してPDF印刷を実装する手順です。

流れ

  1. Thinreportsデザイナーで印刷レイアウトを作成
  2. SDK(Thinreports Generator for Ruby) https://rubygems.org/gems/thinreports を利用して印刷プログラムを作成
  3. 印刷レイアウトと印刷プログラムを AWS Lambda に登録
  4. kintone に設置した JavaScript から Lambda を実行
  5. kintone にPDFファイルがアップロードされる

順に説明していきます。

1. Thinreportsデザイナーで印刷レイアウトを作成

スクリーンショット 2019-10-27 1 19.31.32.png

下記リンクを参照。

2. SDK を利用して印刷プログラムを作成

PDF生成

AWS Lambda にて Thinreports Generator を使って PDFファイルを生成します。

拙稿をご覧ください。

hello-world.rb
# coding: utf-8
require 'thinreports'
require 'json'
require 'aws-sdk-s3'
require './file_upload.rb'
require './file.rb'

def lambda_handler(event:, context:)
  app_id = nil
  record_id = nil
  field_code = 'file'
  begin
    print 'JSON.parse event.body => '
    pp JSON.parse(event['body'])
    event_body = JSON.parse(event['body'])
    record = event_body['record']
    app_id = event_body['appId']
    record_id = event_body['recordId']
    print 'record => '
    pp record
    puts '$id => ', record['$id']['value']
    # puts 'text => ', record["text"]["value"]
    puts 'title => ', record['title']['value']
  rescue => e
    puts "#{e.class}: #{e.message}"
    puts e.backtrace
  end

  begin
    # region = 'S3バケットのリージョン'
    # bucket_name = 'バケット名'
    key = 'put-hello-ruby.pdf'
    # S3 Backet Object init
    s3 = Aws::S3::Resource.new(region: region)
    # pp s3
    obj = s3.bucket(bucket_name).object(key)
    # Thinreports Object init
    report = Thinreports::Report.new layout: 'hello_world'
    # 1st page
    report.start_new_page
    report.page.item(:world).value(record["$id"]["value"])
    report.page.item(:thinreports).value(record["text"]["value"])
    report.page.item(:title).value(record["title"]["value"])
    # pp report
    obj.put(body: report.generate)

    # /tmpファイルに書き込み
    filename = '/tmp/put-hello-ruby-' + Time.new.localtime("+09:00").strftime("%Y%m%d%H%M%S") + '.pdf'
    puts filename
    File.open(filename, 'w+') { |f| f<< report.generate }
    puts `ls -la /tmp`

    # ファイルアップロード
    res = file_upload(filename)
    pp res
    # pp JSON.parse(res.body)
    file_key = res['fileKey']

    # 添付ファイルフィールド更新
    res_file = file_update(app_id, record_id, field_code, file_key)
    pp res_file
  rescue => e
    puts "#{e.class}: #{e.message}"
    puts e.backtrace
  end

  { statusCode: 200, body: JSON.generate('Hello Lambda! Thinreports for Ruby') }
end

ファイルアップロード

Lambda から /tmp に作成した PDFファイルを、kintoneにアップロードします。

下記拙稿も参考にして下さい。

kintoneの領域にファイルアップロードが成功した時に、ファイルキーが返却されます。

file_upload.rb
require 'rubygems'
require 'bundler/setup'
require 'dotenv/load'
require 'net/http'
require 'uri'
require 'json'
require 'pp'

def file_upload(filename)
  puts __FILE__
  begin
    upload_file = File.open filename, "r"
    data = [["file", upload_file, {"filename" => filename, "content_type" => "application/pdf"}]]
    url = "https://#{ENV['SUBDOMAIN']}.cybozu.com/k/v1/file.json"
    uri = URI.parse(url)
    api_token = ENV['API_TOKEN']
    req = Net::HTTP::Post.new(uri.path)
    req['X-Cybozu-API-Token'] = api_token
    req['Content-Type'] = 'multipart/form-data'
    req.set_form(data, "multipart/form-data")
    pp req # <Net::HTTP::Post POST>
    res = ''
    Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http|
      res = http.request(req)
      res.each_header do |name, val|
        puts "name = #{name}, val = #{val}"
      end
      case res.code.to_i
      when 200
        pp JSON.parse(res.body) # {"fileKey"=>"b28a1ecc-e8dc-4a10-b030-7cf96b35b272"}
      else
        pp %Q(#{res.code} #{res.message})
        pp JSON.parse(res.body)
      end
    end
    upload_file.close
    JSON.parse(res.body)
  rescue => exception
    pp exception
    upload_file.close
    exception.message
  end
end

アップロードしたファイルとレコードの関連付け

下記拙稿を参考にして下さい。

前段で取得したファイルキーを使って、レコードの添付ファイルフィールドを更新します。

file.rb
require 'rubygems'
require 'bundler/setup'
require 'dotenv/load'
require 'net/http'
require 'uri'
require 'json'
require 'pp'

def file_update(app, id, field_code, file_key)
  puts __FILE__
  begin
    field_code = field_code || 'file'
    url = "https://#{ENV['SUBDOMAIN']}.cybozu.com/k/v1/record.json"
    uri = URI.parse(url)
    api_token = ENV['API_TOKEN']
    req = Net::HTTP::Put.new(uri.path)
    req['X-Cybozu-API-Token'] = api_token
    req['Content-Type'] = 'application/json'

    req.body = JSON.generate({
        "app" => app,
        "id" => id,
        "record" => {field_code => {"value" => [{"fileKey"=>file_key}]}}
      })
      Net::HTTP.start(uri.host, uri.port, :use_ssl => true) {|http|
      res = http.request(req)
      res.each_header do |name, val|
        puts "name=#{name}, val=#{val}"
      end
      case res.code.to_i
      when 200
        pp JSON.parse(res.body)
        JSON.parse(res.body)
      else
        pp %Q(#{res.code} #{res.message})
        pp JSON.parse(res.body)
        JSON.parse(res.body)
      end
    }
  rescue => exception
    pp exception
    exception.message
  end
end

3. 印刷レイアウトと印刷プログラムを AWS Lambda に登録

作成した プログラムファイル及びライブラリ、印刷レイアウトファイルをZip圧縮して Lambda にアップロードします。

下記拙稿を参考にして下さい。

4. kintone に設置した JavaScript から Lambda を実行

下記拙稿を参考にして下さい。

アプリに添付ファイルのフィールドを追加しています。

スクリーンショット 2019-10-27 10.26.14.png

スクリーンショット 2019-10-27 10.29.19.png

5. kintone にPDFファイルがアップロードされる

成功すると添付ファイルフィールドにPDFファイルがアップロードされます。
詳細画面を再読み込みして下さい。

スクリーンショット 2019-10-27 19.30.18.png

スクリーンショット 2019-10-27 19.30.28.png

まとめ

AWS Lambdaを始めとするサーバーレスのサービスが一般的になり、フロントエンドとバックエンドを隔てる壁も随分と低く、曖昧になってきたと感じています。

フロントエンドのプログラマもバックエンドの世界を知る機会があると思いますし、バックエンドの方もフロントエンドを理解した上でバックエンドの処理を考える良い機会があるのではと感じています。


参考リンク

Thinreports関連

AWS関連

kintone関連

Ruby関連

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
5
Help us understand the problem. What are the problem?