hiddy0329
@hiddy0329 (hiddy)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

thinreportsを使ったPDF出力機能の実装が上手くいきません

解決したいこと

ここに解決したい内容を記載してください。

Ruby on Railsで、「伝票作成アプリ」を実装しています。
作成した伝票をPDFとして出力する機能を作るところに着手しているのですが、
実装が上手くいきませんので、アドバイスをいただきたいです。

発生している問題・エラー

「PDF出力機能」を実装するために、
実際に出力する画面を視覚的に作成できる「Thinreports Editor」と、
そこにデータを埋め込むためのRubyのgemである「thinreports」を導入しました。

以下のようにコーディングし、アプリ画面の「pdf出力」ボタンを押すと、
「読み込めませんでした」という画面が出てしまいます。

エラーが起きている箇所のGyazo Gif

該当するソースコード

app/views/slips/show.html.erb

<div class="d-grid gap-5">
<h2 class="text-center bg-primary text-white">伝票詳細</h2>

<div class="container">
  <div class="row">
    <div class="col-md-6">
      <th>【出荷先】</th>
      <div class="form-control">
      <%= @slip.address_name %>
      </div>
    </div>
    <div class="col-md-6">
      <th>【送り主】</th>
      <p class="h2">
      <%= current_user.name %>
      </p>
    </div>
  </div>
</div>

<div class="container">
  <div class="row">
    <div class="col-md-4">
      <th>【出荷日】</th>
      <div class="form-control">
      <%= @slip.shipping_date %>
      </div>
    </div>
    <div class="col-md-4">
      <th>【伝票番号】</th>
      <div class="form-control">
      <%= @slip.slip_number %>
      </div>
    </div>
    <div class="col-md-4">
      <th>【送り状ナンバー】</th>
      <div class="form-control">
      <% if @slip.invoice_number.present? %>
        <%= @slip.invoice_number %>
      <% else %>
        なし
      <% end %>
      </div>
    </div>
  </div>
</div>

<div class="container">
  <table class="table">
    <thead>
      <tr>
        <th>品番</th>
        <th>色</th>
        <th>数量</th>
        <th>備考</th>
      </tr>
    </thead>
    <tbody class="bulk-registration-form">
      <% @slip.orders.each do |order| %>
        <% if order.order_number.present? %>
          <tr class="item">
            <td>
              <%= order.order_number %>
            </td>
            <td>
              <%= order.color %>
            </td>
            <td>
              <%= order.count %>
            </td>
            <td>
              <%= order.note %>
            </td>
          </tr> 
        <% end %>
      <% end %>
    </tbody>
  </table>
</div>

</div>
<div class="text-center mt-3">
  <%= link_to "PDF出力", slip_pdfs_path(@slip.id), class: 'btn btn-danger' %>
  <%= link_to "編集", edit_slip_path(@slip.id), class: 'btn btn-primary' %>
  <%= link_to "削除", slip_path(@slip.id), method: :delete, class: 'btn btn-primary' %>
</div>
<div class="text-center mt-3">
  <%= link_to "戻る", slips_path, class: 'btn btn-success' %>
</div>

@slipというインスタンスに個別の伝票情報が格納されており、それを詳細画面で表示するためのコードです。
下の方にPDF出力のための「link_toメソッド」を記述しており、
PDF出力用のコントローラーで伝票情報を扱えるようにしようと考え、
パスをそのように記述しました。

config/routes.rb

Rails.application.routes.draw do
  devise_for :users
  root "tops#index"
  resources :products, only:[:new, :create]
  resources :clients, only:[:new, :create]
  resources :slips do
    resources :pdfs, only: :index
  end
end

ネスト構造を利用して、行き先のpdfsコントローラーのindexアクションで伝票情報を扱えるようにしようと考えました。

app/controllers/pdfs_controller.rb

class PdfsController < ApplicationController
  def index
    report = Thinreports::Report.new(layout: "#{Rails.root}/app/pdfs/hello_world.tlf")

    file = report.generate(filename: 'hello_world.pdf')

    send_data(
      file,
      filename: "hello_world.pdf",
      type: "application/pdf",
      disposition: "inline")
  end
end

tinreports公式のガイドを参考に、まずは試しに出力だけでもできるかどうか試してみることにし、
PDF出力のためのコードを書きました。
うまく動作すれば、下の画像の通りのPDFが出力されるはずです。

↓↓該当のthinreports editorの画面
thinreports editor画面.png

自分で試したこと

自分で立てた仮説は以下の通りです。
①エディターのバージョンが古いのではないか?
②ルーティングの設定方法に問題があるのではないか?
③コントローラーの記述に問題があるのでは?

①に関しては、thinreportsの公式ガイドにあったスタートアップガイドに沿って試作をしてみたところ、
正しく動作したので、問題ないと考えています。

②に関しては、PDF出力時にthinreports editorに伝票の情報を埋め込むためにルーティング時点で送っておかなければならないと考えた実装ですが、問題があるのかどうかわかりません。

③に関しては、thinreportsファイルを呼び出し、ブラウザ上にPDFを出力するための記述をしているので問題はないと考えています。

以上、②③あたりに関して何か問題があるのかどうか、また別に問題があるのかどうか、
もしお気づきの点がありましたら教えていただきたいです。

0

3Answer

report.generate(filename: 'hello_world.pdf') はカレントディレクトリに hello_world.pdf を書き出し、 nil を返します。 file が nil になるせいで send_data(file, ...) が正しいデータを送れていません。

report.generate のように引数なしで呼び出せば生成した PDF データを返します。以下のようにしてください。

  def index
    report = Thinreports::Report.new(layout: "#{Rails.root}/app/pdfs/hello_world.tlf")

    # 少なくとも1枚はページがないと破損したPDF扱いになるので適当に作っておく
    report.start_new_page

    file = report.generate

    send_data(
      file,
      filename: "hello_world.pdf",
      type: "application/pdf",
      disposition: "inline")
  end
0Like

Comments

  1. @hiddy0329

    Questioner

    的確なアドバイスをいただき、ありがとうございます!!
    「hello_world.pdf」と無駄に命名してしまったせいで、空のPDFファイルが出来上がってしまっていたということなのですね!

    今後の実装に活かしていきます!!

もう一つ質問なのですが、

伝票に登録してある商品情報は「ordersテーブル」で以下の画像のように管理しています。
一つの伝票(テーブルはslipsテーブル)に対していくつもの商品が紐づいています。
ordersテーブル画面.png

PDF上に個別の伝票に登録してあるこれらの商品情報も全て出力したいと考えています。

単体のデータであれば、以下のeditor画像のように、Idを指定してそこに値を埋め込む記述でうまくいきますが、複数あるデータを一気に埋め込むにはどのようにすればよろしいのでしょうか?
thinreports editor画面.png

インターネットでも検索したのですが、情報が古いものが多かったので、、、

0Like

複数あるデータを一気に埋め込むにはどのようにすればよろしいのでしょうか?

リスト機能を使えばよさそうです。

Thinreports は使ったことがないのでこれ以上の回答はできません。解決しなければ新しい質問として投稿したほうが他の回答者さんに見つけてもらいやすいと思います。

0Like

Your answer might help someone💌