Skip to content

Commit fb90056

Browse files
authored
Move to grover for PDF generation & add a dedicated download button (#1194)
* Move to grover for PDF generation * incorporate the AI suggestions * fix lint * fix double auth * fix tests * fix last issues * fix errorhandeling * fix lint * add puppeteer * fix zatladder spending being zero * make table header and footer bold * fix lint
1 parent 126485d commit fb90056

File tree

14 files changed

+685
-64
lines changed

14 files changed

+685
-64
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ RUN apt-get update -qq && \
1515
libpq-dev \
1616
curl \
1717
netcat-traditional \
18-
wkhtmltopdf \
18+
chromium \
1919
libyaml-dev
2020

2121
# Add Node, required for asset pipeline.

Gemfile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ gem 'devise', '~>4.9.4'
1010
gem 'devise-i18n', '~>1.15.0'
1111
gem 'factory_bot_rails', '~> 6.5.1'
1212
gem 'faker', '~> 3.5.3'
13+
gem 'grover', '~> 1.2.4'
1314
gem 'http', '~> 5.3.1'
1415
gem 'jbuilder', '~> 2.14.1'
1516
gem 'jquery-rails', '~> 4.6.1'
@@ -43,8 +44,6 @@ gem 'sprockets-rails', '~> 3.5', '>= 3.5.2'
4344
gem 'turbo-rails', '~> 2.0', '>= 2.0.20'
4445
gem 'uglifier', '~> 4.2.1'
4546
gem 'validates_timeliness', '~> 7.1.0'
46-
gem 'wicked_pdf', '~> 2.8.2'
47-
gem 'wkhtmltopdf-binary', '~> 0.12.6.10'
4847

4948
group :development, :test do
5049
gem 'awesome_print', '~> 1.9.2'

Gemfile.lock

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ GEM
183183
google-protobuf (4.33.0-x86_64-linux-gnu)
184184
bigdecimal
185185
rake (>= 13)
186+
grover (1.2.4)
187+
nokogiri (~> 1)
186188
guard (2.19.1)
187189
formatador (>= 0.2.4)
188190
listen (>= 2.7, < 4.0)
@@ -596,10 +598,6 @@ GEM
596598
base64
597599
websocket-extensions (>= 0.1.0)
598600
websocket-extensions (0.1.5)
599-
wicked_pdf (2.8.2)
600-
activesupport
601-
ostruct
602-
wkhtmltopdf-binary (0.12.6.10)
603601
zeitwerk (2.7.3)
604602

605603
PLATFORMS
@@ -625,6 +623,7 @@ DEPENDENCIES
625623
factory_bot_rails (~> 6.5.1)
626624
faker (~> 3.5.3)
627625
foreman (~> 0.90.0)
626+
grover (~> 1.2.4)
628627
guard-livereload (~> 2.5.2)
629628
guard-rspec (~> 4.7.3)
630629
http (~> 5.3.1)
@@ -682,8 +681,6 @@ DEPENDENCIES
682681
uglifier (~> 4.2.1)
683682
validates_timeliness (~> 7.1.0)
684683
web-console (~> 4.2.1)
685-
wicked_pdf (~> 2.8.2)
686-
wkhtmltopdf-binary (~> 0.12.6.10)
687684

688685
BUNDLED WITH
689686
2.6.9

app/assets/stylesheets/pdf.scss

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1 @@
1-
// There is a bug with Bootstrap tables in PDF, so we use td in a th, but it needs to be bold
2-
thead,
3-
tfoot {
4-
td {
5-
font-weight: bold;
6-
}
7-
}
1+
// PDF-specific styles

app/controllers/invoices_controller.rb

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ def index
1515

1616
def show
1717
@invoice = invoice
18+
token_based_access = !integer_id?(params[:id])
19+
20+
# Authorize for authenticated access (integer ID), skip for token-based access
21+
authorize @invoice, :show? unless token_based_access
1822

1923
respond_to do |format|
2024
format.html
21-
format.pdf do
22-
render pdf: "Factuur #{@invoice.human_id}",
23-
template: 'invoices/show.html.erb',
24-
lowquality: true
25-
end
25+
format.pdf { render_invoice_pdf }
2626
end
2727
end
2828

@@ -42,6 +42,9 @@ def create
4242

4343
def pay # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
4444
@invoice = invoice
45+
token_based_access = !integer_id?(params[:id])
46+
47+
authorize @invoice, :pay? unless token_based_access
4548

4649
if @invoice.paid?
4750
redirect_to invoice_path params[:id]
@@ -73,14 +76,41 @@ def send_invoice
7376

7477
private
7578

79+
def integer_id?(id)
80+
Integer(id)
81+
true
82+
rescue ArgumentError
83+
false
84+
end
85+
7686
def invoice
7787
@invoice = Invoice.find(Integer(params[:id]))
78-
authorize @invoice
7988
rescue ArgumentError
8089
@invoice = Invoice.find_by!(token: params[:id])
8190
end
8291

8392
def permitted_attributes
8493
params.require(:invoice).permit(%i[user_id activity_id name_override email_override rows], rows_attributes: %i[name amount price])
8594
end
95+
96+
def render_invoice_pdf # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
97+
token_based_access = !integer_id?(params[:id])
98+
authorize @invoice, :download? unless token_based_access
99+
100+
html = render_to_string(
101+
template: 'invoices/show',
102+
formats: [:html],
103+
layout: 'pdf'
104+
)
105+
pdf = Grover.new(html).to_pdf
106+
send_data pdf, filename: "Factuur-#{@invoice.human_id}.pdf", type: 'application/pdf', disposition: 'attachment'
107+
rescue StandardError => e
108+
Rails.logger.error "Failed to generate PDF for invoice #{@invoice.id}: #{e.message}"
109+
if request.format.pdf?
110+
render plain: 'Er is een fout opgetreden bij het genereren van de PDF. Probeer het later opnieuw.', status: :internal_server_error
111+
else
112+
flash[:error] = 'Er is een fout opgetreden bij het genereren van de PDF. Probeer het later opnieuw.'
113+
redirect_to invoice_path(@invoice)
114+
end
115+
end
86116
end

app/controllers/zatladder_controller.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def current_year
2323
end
2424
end
2525

26+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
2627
def zatladder_spendings(from, to)
2728
users = User.in_amber.exists? ? User.in_amber : User.sofia_account
2829
@users_spendings = users.calculate_spendings(from:, to:)
@@ -33,6 +34,8 @@ def zatladder_spendings(from, to)
3334
spendings: @users_spendings.fetch(user.id, 0)
3435
}
3536
end
37+
zatladder.reject! { |user| user[:spendings].zero? }
3638
zatladder.sort_by { |id| id[:spendings] }.reverse!
3739
end
40+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
3841
end

app/mailers/invoice_mailer.rb

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,19 @@ def invoice_mail(invoice) # rubocop:disable Metrics/AbcSize, Metrics/MethodLengt
99
@cab_disabled = true
1010
end
1111

12-
attachments["#{invoice.human_id}.pdf"] = WickedPdf.new.pdf_from_string(
13-
render_to_string(pdf: invoice.human_id.to_s, template: 'invoices/show', layout: 'pdf')
14-
)
12+
begin
13+
html = render_to_string(
14+
template: 'invoices/show',
15+
formats: [:html],
16+
layout: 'pdf'
17+
)
18+
pdf = Grover.new(html).to_pdf
19+
attachments["#{invoice.human_id}.pdf"] = pdf
20+
rescue StandardError => e
21+
Rails.logger.error "Failed to generate PDF attachment for invoice #{invoice.id}: #{e.message}"
22+
Rails.logger.error e.backtrace.join("\n")
23+
# Continue sending email without PDF attachment
24+
end
1525

1626
mail to: @invoice.email, subject: "Factuur #{invoice.human_id} #{Rails.application.config.x.company_name}"
1727
end

app/policies/invoice_policy.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ def send_invoice?
77
user&.treasurer?
88
end
99

10+
def download?
11+
user&.treasurer?
12+
end
13+
1014
def pay?
1115
show?
1216
end

app/views/invoices/index.html.erb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
<% if policy(Invoice).send_invoice? %>
3636
<th scope="col">Verstuur</th>
3737
<% end %>
38+
<% if policy(Invoice).download? %>
39+
<th scope="col">Download</th>
40+
<% end %>
3841
</tr>
3942
</thead>
4043
<tbody class="table-group-divider">
@@ -77,6 +80,11 @@
7780
<% end %>
7881
</td>
7982
<% end %>
83+
<% if policy(Invoice).download? %>
84+
<td>
85+
<%= link_to 'Downloaden', invoice_path(invoice, format: :pdf), class: 'btn btn-primary', download: "Factuur-#{invoice.human_id}.pdf" %>
86+
</td>
87+
<% end %>
8088
</tr>
8189
<% end %>
8290
</tbody>

app/views/invoices/show.html.erb

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,9 @@
9494
<table class="table table-striped">
9595
<thead>
9696
<tr>
97-
<td scope="col" class="col-8">Consumpties</td>
98-
<td scope="col" class="col-2">Aantal</td>
99-
<td scope="col" class="col-2">Totaalbedrag</td>
97+
<th scope="col" class="col-8">Consumpties</th>
98+
<th scope="col" class="col-2">Aantal</th>
99+
<th scope="col" class="col-2">Totaalbedrag</th>
100100
</tr>
101101
</thead>
102102
<tbody class="table-group-divider">
@@ -110,9 +110,9 @@
110110
</tbody>
111111
<tfoot class="table-group-divider">
112112
<tr>
113-
<td class="border-bottom-0">Totaal</td>
113+
<th class="border-bottom-0">Totaal</th>
114114
<td class="border-bottom-0" />
115-
<td class="border-bottom-0"><%= number_to_currency(@invoice.activity.revenue_by_user(@invoice.user), unit: '€') %></td>
115+
<th class="border-bottom-0"><%= number_to_currency(@invoice.activity.revenue_by_user(@invoice.user), unit: '€') %></th>
116116
</tr>
117117
</tfoot>
118118
</table>
@@ -124,9 +124,9 @@
124124
<table class="table table-striped">
125125
<thead>
126126
<tr>
127-
<td scope="col" class="col-8"><% if @invoice.activity.count_per_product(user: @invoice.user).any? %>Overige kosten<% else %>Kosten<% end %></td>
128-
<td scope="col" class="col-2">Aantal</td>
129-
<td scope="col" class="col-2">Totaalbedrag</td>
127+
<th scope="col" class="col-8"><% if @invoice.activity.count_per_product(user: @invoice.user).any? %>Overige kosten<% else %>Kosten<% end %></th>
128+
<th scope="col" class="col-2">Aantal</th>
129+
<th scope="col" class="col-2">Totaalbedrag</th>
130130
</tr>
131131
</thead>
132132
<tbody class="table-group-divider">
@@ -140,9 +140,9 @@
140140
</tbody>
141141
<tfoot class="table-group-divider">
142142
<tr>
143-
<td class="border-bottom-0">Totaal</td>
143+
<th class="border-bottom-0">Totaal</th>
144144
<td class="border-bottom-0" />
145-
<td class="border-bottom-0"><%= number_to_currency(@invoice.row_amount, unit: '€') %></td>
145+
<th class="border-bottom-0"><%= number_to_currency(@invoice.row_amount, unit: '€') %></th>
146146
</tr>
147147
</tfoot>
148148
</table>

0 commit comments

Comments
 (0)