Skip to content

Commit

Permalink
feat(export-invoice-fees): Export invoice fees to CSV (#2268)
Browse files Browse the repository at this point in the history
## Roadmap Task

👉
https://getlago.canny.io/feature-requests/p/ability-to-export-data-from-the-user-interface

## Context

In order to effortlessly download export of invoice fees by
non-technical users, it is required to add a new CSV exporter and extend
the export resource service and mailer.

## Description

This change adds a new CSV exporter for invoice fees;

also extends the `ExportResourcesService` to handle unknown resource
types and invoice_fees.

and improve the mailer and its specs to support different kind of
exported resources like invoice_fees.
  • Loading branch information
ancorcruz authored Jul 11, 2024
1 parent a79ea39 commit d10f3cd
Show file tree
Hide file tree
Showing 8 changed files with 380 additions and 3 deletions.
3 changes: 2 additions & 1 deletion app/mailers/data_export_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
class DataExportMailer < ApplicationMailer
def completed
@data_export = params[:data_export]
@resource_type = @data_export.resource_type.humanize.downcase
user = @data_export.user

return if @data_export.file.blank?
Expand All @@ -15,7 +16,7 @@ def completed
from: ENV['LAGO_FROM_EMAIL'],
subject: I18n.t(
'email.data_export.completed.subject',
resource_type: @data_export.resource_type
resource_type: @resource_type
)
)
end
Expand Down
81 changes: 81 additions & 0 deletions app/services/data_exports/csv/invoice_fees.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# frozen_string_literal: true

require 'csv'
require 'forwardable'

module DataExports
module Csv
class InvoiceFees < Invoices
extend Forwardable

def call
::CSV.generate(headers: true) do |csv|
csv << headers

query.invoices.find_each do |invoice|
serialized_invoice = serializer_klass
.new(invoice, includes: %i[fees subscriptions])
.serialize

subscriptions = serialized_invoice[:subscriptions].index_by do |sub|
sub[:lago_id]
end

serialized_invoice[:fees].each do |serialized_fee|
subscription_id = serialized_fee[:lago_subscription_id]
serialized_subscription = subscriptions[subscription_id]

csv << [
serialized_invoice[:lago_id],
serialized_invoice[:number],
serialized_invoice[:issuing_date],
serialized_fee[:lago_id],
serialized_fee[:item][:type],
serialized_fee[:item][:code],
serialized_fee[:item][:name],
serialized_fee[:item][:invoice_display_name],
serialized_fee[:item][:filter_invoice_display_name],
serialized_fee[:item][:grouped_by],
serialized_subscription&.dig(:external_id),
serialized_subscription&.dig(:plan_code),
serialized_fee[:from_date],
serialized_fee[:to_date],
serialized_fee[:total_amount_cents],
serialized_fee[:total_amount_currency],
serialized_fee[:units],
serialized_fee[:precise_unit_amount],
serialized_fee[:taxes_amount_cents]
]
end
end
end
end

private

def headers
%w[
invoice_lago_id
invoice_number
invoice_issuing_date
fee_lago_id
fee_item_type
fee_item_code
fee_item_name
fee_item_invoice_display_name
fee_item_filter_invoice_display_name
fee_item_grouped_by
subscription_external_id
subscription_plan_code
fee_from_date
fee_to_date
fee_total_amount_cents
fee_amount_currency
fee_units
fee_precise_unit_amount
fee_taxes_amount_cents
]
end
end
end
end
7 changes: 7 additions & 0 deletions app/services/data_exports/export_resources_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ class ExportResourcesService < BaseService
EXPIRED_FAILURE_MESSAGE = 'Data Export already expired'
PROCESSED_FAILURE_MESSAGE = 'Data Export already processed'

ResourceTypeNotSupportedError = Class.new(StandardError)

def initialize(data_export:)
@data_export = data_export

Expand Down Expand Up @@ -42,6 +44,11 @@ def call
def file_data
case data_export.resource_type
when "invoices" then Csv::Invoices.call(data_export:)
when "invoice_fees" then Csv::InvoiceFees.call(data_export:)
else
raise ResourceTypeNotSupportedError.new(
"'#{data_export.resource_type}' resource not supported"
)
end
end

Expand Down
2 changes: 1 addition & 1 deletion app/views/data_export_mailer/completed.slim
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ div style='margin-bottom: 32px;width: 80px;height: 24px;'
div style='margin-bottom: 24px;font-style: normal;font-weight: 400;font-size: 16px;line-height: 24px;color: #19212E;'
= I18n.t('email.data_export.completed.greetings')
div style='margin-bottom: 32px;font-style: normal;font-weight: 400;font-size: 16px;line-height: 24px;color: #19212E;'
= I18n.t('email.data_export.completed.intro')
= I18n.t('email.data_export.completed.intro', resource_type: @resource_type)
table style='margin-bottom: 32px' width="100%" cellspacing="0" cellpadding="0"
tr
td
Expand Down
2 changes: 1 addition & 1 deletion config/locales/en/email.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ en:
completed:
fallback_text: If the link has expired, please generate a new one from your Dashboard.
greetings: Hello
intro: Your invoices export is ready! You can download it using the link below, which will be available for 7 days.
intro: Your %{resource_type} export is ready! You can download it using the link below, which will be available for 7 days.
lago_team: The Lago Team
main_cta_label: Download export
subject: Your Lago %{resource_type} export in ready!
Expand Down
18 changes: 18 additions & 0 deletions spec/mailers/data_export_mailer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,27 @@

describe '#completed' do
let(:mailer) { data_export_mailer.with(data_export:).completed }
let(:file_url) { "https://api.lago.dev/rails/active_storage/blobs/redirect/eyJf" }

before do
allow(data_export).to receive(:file_url).and_return(file_url)
end

specify do
expect(mailer.to).to eq([data_export.user.email])
expect(mailer.subject).to eq("Your Lago invoices export in ready!")
expect(mailer.body.encoded).to match("Your invoices export is ready!")
expect(mailer.body.encoded).to match("will be available for 7 days")
expect(mailer.body.encoded).to match(data_export.file_url)
end

context "when the resource type is invoice_fees" do
let(:data_export) { create(:data_export, :completed, resource_type: 'invoice_fees') }

specify do
expect(mailer.subject).to eq("Your Lago invoice fees export in ready!")
expect(mailer.body.encoded).to match("Your invoice fees export is ready!")
end
end

context 'when data export is expired' do
Expand Down
Loading

0 comments on commit d10f3cd

Please sign in to comment.