Skip to content

Conversation

@lodewiges
Copy link
Contributor

@lodewiges lodewiges commented Jan 4, 2026

Moves to grover for pdf generation because wkhtmltopdf is EOL and doesnot support ruby 3.4.8

Fixes #843
Fix authorisation issues and download button not working

Summary by CodeRabbit

  • New Features

    • Added a "Download" action on the invoice list for authorized users.
    • Public token-based invoice links now return PDFs without requiring login.
  • Chores

    • Switched PDF generation to a new renderer and added its default configuration; removed the old global PDF engine setup.
    • Emailed invoices now use the new PDF renderer.
  • Bug Fixes

    • PDF generation failures are logged and handled; emails continue sending without attachments.

✏️ Tip: You can customize this high-level summary in your review settings.

Copilot AI review requested due to automatic review settings January 4, 2026 22:05
@coderabbitai
Copy link

coderabbitai bot commented Jan 4, 2026

Warning

Rate limit exceeded

@lodewiges has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 minutes and 33 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 0cd8da2 and 647f448.

📒 Files selected for processing (1)
  • app/controllers/invoices_controller.rb
📝 Walkthrough

Walkthrough

Replaces WickedPdf/wkhtmltopdf with Grover/Chromium, updates Dockerfile and Gemfile, adds Grover initializer and controller/mailer PDF generation via Grover, adds download? policy and download UI, and removes a PDF-specific bold CSS rule. (50 words)

Changes

Cohort / File(s) Summary
Build & Gems
Dockerfile, Gemfile
Replaced wkhtmltopdf package in Dockerfile with chromium; removed wicked_pdf and wkhtmltopdf-binary gems and added the grover gem.
Grover config & WickedPdf removal
config/initializers/grover.rb, config/initializers/wicked_pdf.rb
Added grover.rb initializer with default Grover options (A4, print_background, display_url); removed the global wicked_pdf.rb initializer.
Controller PDF flow
app/controllers/invoices_controller.rb
Added integer_id? to distinguish token vs integer IDs, skip authorization for token-based access, and centralized PDF rendering in render_invoice_pdf using Grover with error handling and redirect on failure.
Mailer PDF flow
app/mailers/invoice_mailer.rb
Replaced WickedPdf usage with rendering HTML (layout: 'pdf') and generating PDF via Grover.from_html, attaching the PDF and logging/continuing if PDF generation fails.
Authorization / UI gating
app/policies/invoice_policy.rb, app/views/invoices/index.html.erb
Added download? policy (returns true for treasurers) and conditionally added a "Download" column/link (invoice_path(invoice, format: :pdf)) in the invoices index, gated by the new policy.
Stylesheet
app/assets/stylesheets/pdf.scss
Removed CSS rule that forced td in thead/tfoot to be bold; replaced with a PDF-specific comment.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Browser
  participant Rails as InvoicesController
  participant Grover
  participant ServerResp as Server Response

  Note over User,Browser: User clicks "Download" (GET /invoices/:id.pdf)
  User->>Browser: click download link
  Browser->>Rails: GET /invoices/:id.pdf
  Rails->>Rails: integer_id? -> token_based_access?
  alt integer id (authenticated)
    Rails->>Rails: authorize :show?
  end
  Rails->>Rails: render invoice HTML (layout: 'pdf')
  Rails->>Grover: Grover.from_html(rendered_html, options)
  Grover-->>Rails: PDF binary
  Rails->>ServerResp: send_data (attachment, filename: Factuur-<human_id>.pdf)
  ServerResp-->>Browser: HTTP response (PDF)
  Browser-->>User: prompt/save PDF
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I swapped a gem and tuned a spring,
Chromium hums — Grover takes wing.
Click "Download", the paper hops free,
A tiny puff, a PDF for thee.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning Description identifies the core motivation (wkhtmltopdf EOL, Ruby 3.4.8 incompatibility) and links to issue #843, but lacks required checklist items and detailed summary. Add completed checklist items for database migrations and testing steps; expand summary with UI changes and confirmation that issue #843 is fully resolved.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly and concisely summarizes the two main changes: migrating to Grover for PDF generation and adding a dedicated download button feature.
Linked Issues check ✅ Passed All requirements from issue #843 are met: download button added [app/views/invoices/index.html.erb], PDF generation via Grover implemented [multiple files], and direct download enabled instead of email-only flow.
Out of Scope Changes check ✅ Passed All changes directly support the stated objectives: Grover migration, authorization fixes, and download button implementation. No extraneous modifications detected.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates the PDF generation system from WickedPDF (using wkhtmltopdf) to Grover (using Chrome/Chromium) and adds a dedicated download button for invoices in the invoice list view.

  • Replaced WickedPDF with Grover for PDF generation throughout the application
  • Added a dedicated "Download" button column to the invoice index table with proper authorization
  • Updated the Dockerfile to use Chromium instead of wkhtmltopdf

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
config/initializers/wicked_pdf.rb Removed WickedPDF configuration (entire file deleted)
config/initializers/grover.rb Added Grover configuration with A4 format, background printing, and display URL settings
app/views/invoices/index.html.erb Added download button column with authorization check using the new download policy
app/policies/invoice_policy.rb Added download? policy method restricting downloads to treasurers
app/mailers/invoice_mailer.rb Updated to use Grover for PDF attachment generation instead of WickedPDF
app/controllers/invoices_controller.rb Refactored PDF rendering to use Grover with new render_invoice_pdf and integer_id? helper methods
app/assets/stylesheets/pdf.scss Removed Bootstrap table header workaround styles (previously needed for wkhtmltopdf)
Gemfile.lock Replaced wicked_pdf and wkhtmltopdf-binary dependencies with grover and its dependencies
Gemfile Updated gem dependencies: removed wicked_pdf/wkhtmltopdf-binary, added grover
Dockerfile Replaced wkhtmltopdf with chromium and chromium-driver system packages

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

font-weight: bold;
}
}
// PDF-specific styles
Copy link

Copilot AI Jan 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removed CSS styles addressed a specific issue where <td> elements inside <thead> needed to be bold due to a Bootstrap tables bug in PDF generation. The invoice show template (lines 97, 127) still uses <td> inside <thead>. While this might work correctly with Grover/Chromium (unlike wkhtmltopdf), please verify that the PDF output still displays table headers with appropriate styling. If the headers appear non-bold, this CSS may need to be restored or the template markup should be updated to use <th> instead of <td>.

Suggested change
// PDF-specific styles
// PDF-specific styles
// Ensure table headers render bold in PDFs even when using <td> inside <thead>,
// which can be affected by Bootstrap/PDF generator quirks.
thead td {
font-weight: bold;
}

Copilot uses AI. Check for mistakes.
coderabbitai[bot]

This comment was marked as outdated.

@codecov
Copy link

codecov bot commented Jan 4, 2026

Codecov Report

❌ Patch coverage is 47.82609% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.20%. Comparing base (656f5a2) to head (647f448).

Files with missing lines Patch % Lines
app/controllers/invoices_controller.rb 62.50% 6 Missing ⚠️
app/mailers/invoice_mailer.rb 0.00% 5 Missing ⚠️
app/policies/invoice_policy.rb 50.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           staging    #1194      +/-   ##
===========================================
- Coverage    77.62%   77.20%   -0.43%     
===========================================
  Files           54       54              
  Lines         1341     1360      +19     
===========================================
+ Hits          1041     1050       +9     
- Misses         300      310      +10     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

coderabbitai[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
app/controllers/invoices_controller.rb (1)

93-105: DRY violation: PDF generation duplicated in mailer.

As noted in past reviews, PDF generation logic (rendering template with Grover) is duplicated between this controller method and InvoiceMailer. Consider extracting to a shared service or helper to ensure consistency and maintainability.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5b9fe02 and 0cd8da2.

📒 Files selected for processing (1)
  • app/controllers/invoices_controller.rb
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Lint
🔇 Additional comments (4)
app/controllers/invoices_controller.rb (4)

5-5: LGTM! Exception list correctly includes show.

The verify_authorized exception list now includes show, which correctly allows token-based access in the show action without triggering authorization errors.


18-21: LGTM! Token-based access detection and conditional authorization implemented correctly.

The logic properly distinguishes between token-based access (which skips authorization) and integer ID access (which requires authorization). The implementation correctly handles both access patterns.


25-25: LGTM! Parameter removal addressed past review feedback.

The call to render_invoice_pdf correctly omits the unused parameter, as addressed in the previous review.


76-81: LGTM! Clean implementation of integer ID detection.

The helper method uses idiomatic Ruby with Integer() and exception handling to distinguish between integer IDs and token-based access. The implementation is correct and readable.

Comment on lines +93 to +105
def render_invoice_pdf # rubocop:disable Metrics/MethodLength
html = render_to_string(
template: 'invoices/show',
formats: [:html],
layout: 'pdf'
)
pdf = Grover.new(html).to_pdf
send_data pdf, filename: "Factuur-#{@invoice.human_id}.pdf", type: 'application/pdf', disposition: 'attachment'
rescue StandardError => e
Rails.logger.error "Failed to generate PDF for invoice #{@invoice.id}: #{e.message}"
flash[:error] = 'Er is een fout opgetreden bij het genereren van de PDF. Probeer het later opnieuw.'
redirect_to invoice_path(@invoice)
end
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, find the InvoicePolicy file
fd -t f "invoice_policy" app/

Repository: csvalpha/sofia

Length of output: 88


🏁 Script executed:

#!/bin/bash
# Read the InvoicePolicy file to understand download? and show? implementations
cat app/policies/invoice_policy.rb

Repository: csvalpha/sofia

Length of output: 287


🏁 Script executed:

#!/bin/bash
# Search for token_based_access usage in the controller
rg -n "token_based_access|render_invoice_pdf" app/controllers/invoices_controller.rb -B 5 -A 5

Repository: csvalpha/sofia

Length of output: 921


🏁 Script executed:

#!/bin/bash
# Verify the parent ApplicationPolicy to understand default show? behavior
rg -n "class ApplicationPolicy|def show\?" app/policies/application_policy.rb -A 3

Repository: csvalpha/sofia

Length of output: 227


🏁 Script executed:

#!/bin/bash
# Check if there are any other calls to render_invoice_pdf outside the show action
rg -n "render_invoice_pdf" app/

Repository: csvalpha/sofia

Length of output: 245


Add authorization check for download permission in render_invoice_pdf.

The InvoicePolicy defines separate show? and download? policies: show? authorizes treasurers and renting managers, while download? restricts access to treasurers only. Currently, render_invoice_pdf is called within the show action's response handler without verifying the download? permission. This allows renting managers to download invoices they shouldn't have access to.

Add the authorization check, respecting token-based access the same way the show action does (line 21):

def render_invoice_pdf
  token_based_access = !integer_id?(@invoice.id)
  authorize @invoice, :download? unless token_based_access
  # ... rest of method
end
🤖 Prompt for AI Agents
In app/controllers/invoices_controller.rb around lines 93 to 105, add the
missing authorization check so only users allowed to download can generate the
PDF: determine token-based access the same way as the show action (e.g.,
token_based_access = !integer_id?(@invoice.id)) and call authorize @invoice,
:download? unless token_based_access before rendering/generating the PDF; leave
the rest of the method (render_to_string, Grover, send_data, and the rescue)
unchanged.

Comment on lines +101 to +104
rescue StandardError => e
Rails.logger.error "Failed to generate PDF for invoice #{@invoice.id}: #{e.message}"
flash[:error] = 'Er is een fout opgetreden bij het genereren van de PDF. Probeer het later opnieuw.'
redirect_to invoice_path(@invoice)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Error handling may not work well for PDF requests.

The rescue block sets flash[:error] and redirects, but this occurs within a PDF format response. When a user requests a PDF download, the browser expects a PDF response, and a redirect may not display the flash message or provide clear feedback to the user. Consider rendering an error page or returning a 500 status with an error message instead.

🔎 Alternative error handling approach
  rescue StandardError => e
    Rails.logger.error "Failed to generate PDF for invoice #{@invoice.id}: #{e.message}"
-   flash[:error] = 'Er is een fout opgetreden bij het genereren van de PDF. Probeer het later opnieuw.'
-   redirect_to invoice_path(@invoice)
+   render plain: 'Er is een fout opgetreden bij het genereren van de PDF. Probeer het later opnieuw.', status: :internal_server_error
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
rescue StandardError => e
Rails.logger.error "Failed to generate PDF for invoice #{@invoice.id}: #{e.message}"
flash[:error] = 'Er is een fout opgetreden bij het genereren van de PDF. Probeer het later opnieuw.'
redirect_to invoice_path(@invoice)
rescue StandardError => e
Rails.logger.error "Failed to generate PDF for invoice #{@invoice.id}: #{e.message}"
render plain: 'Er is een fout opgetreden bij het genereren van de PDF. Probeer het later opnieuw.', status: :internal_server_error
🤖 Prompt for AI Agents
In app/controllers/invoices_controller.rb around lines 101-104, the rescue block
always sets a flash and redirects which fails for PDF requests; change the
rescue to branch on request.format.pdf? — for non-PDF keep the flash+redirect,
but for PDF return an appropriate error response (e.g. render an error template
with status 500, or send a simple error PDF or plain text body with content-type
and status 500) so the browser/client receives a valid response instead of a
redirect; preserve the Rails.logger.error call and ensure the response
content-type and HTTP status reflect the failure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add "factuur downloaden" button to generate a PDF and let the user download it

2 participants