1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-03-21 12:29:03 +01:00

(feat) payment schedule due in invoices

This commit is contained in:
Sylvain 2023-01-05 12:09:16 +01:00
parent ac187a986f
commit 10f054256e
19 changed files with 233 additions and 154 deletions

View File

@ -23,6 +23,7 @@
- Display custom error message if the PDF invoice is not found
- Report subsription mismatch with user's group
- Added sentry for error reporting
- Report details of the due for invoices related to a payment schedule
- Fix a bug: unable to run test in negative timezones (#425)
- Fix a bug: providing an array of attributes to filter OpenApi data, results in error
- Fix a bug: unable to manage stocks on new products

View File

@ -54,7 +54,7 @@ class Invoice < PaymentDocument
# for debug & used by rake task "fablab:maintenance:regenerate_invoices"
def regenerate_invoice_pdf
pdf = ::PDF::Invoice.new(self, invoice_items.find_by(object_type: Subscription.name)&.object&.expiration_date).render
pdf = ::PDF::Invoice.new(self).render
File.binwrite(file, pdf)
end
@ -199,7 +199,7 @@ class Invoice < PaymentDocument
"main_item.object_id(#{main_item.object_id}), " \
"main_item.object_type(#{main_item.object_type}), user_id(#{invoicing_profile.user_id})"
end
InvoiceWorker.perform_async(id, user&.subscription&.expired_at)
InvoiceWorker.perform_async(id)
end
def log_changes

View File

@ -6,7 +6,8 @@ class PDF::Invoice < Prawn::Document
include ActionView::Helpers::NumberHelper
include ApplicationHelper
def initialize(invoice, subscription_expiration_date)
# @param invoice [Invoice]
def initialize(invoice)
super(margin: 70)
# fonts
@ -42,8 +43,8 @@ class PDF::Invoice < Prawn::Document
text I18n.t('invoices.invoice_reference', REF: invoice.reference), leading: 3
end
text I18n.t('invoices.code', CODE: Setting.get('invoice_code-value')), leading: 3 if Setting.get('invoice_code-active')
if invoice.main_item.object_type != WalletTransaction.name
order_number = invoice.main_item.object_type == OrderItem.name ? invoice.main_item.object.order.reference : invoice.order_number
if invoice.main_item&.object_type != WalletTransaction.name
order_number = invoice.main_item&.object_type == OrderItem.name ? invoice.main_item&.object&.order&.reference : invoice.order_number
text I18n.t('invoices.order_number', NUMBER: order_number), leading: 3
end
if invoice.is_a?(Avoir)
@ -80,60 +81,7 @@ class PDF::Invoice < Prawn::Document
invoice.invoice_items.each do |item|
price = item.amount.to_i / 100.00
details = invoice.is_a?(Avoir) ? "#{I18n.t('invoices.cancellation')} - " : ''
if item.object_type == Subscription.name
subscription = item.object
if invoice.main_item.object_type == 'OfferDay'
details += I18n.t('invoices.subscription_extended_for_free_from_START_to_END',
START: I18n.l(invoice.main_item.object.start_at.to_date),
END: I18n.l(invoice.main_item.object.end_at.to_date))
else
subscription_end_at = case subscription_expiration_date
when Time
subscription_expiration_date
when String
DateTime.parse(subscription_expiration_date)
else
subscription.expiration_date
end
subscription_start_at = subscription_end_at - subscription.plan.duration
details += I18n.t('invoices.subscription_NAME_from_START_to_END',
NAME: item.description,
START: I18n.l(subscription_start_at.to_date),
END: I18n.l(subscription_end_at.to_date))
end
elsif item.object_type == Reservation.name
case invoice.main_item.object.try(:reservable_type)
### Machine reservation
when 'Machine'
details += I18n.t('invoices.machine_reservation_DESCRIPTION', DESCRIPTION: item.description)
when 'Space'
details += I18n.t('invoices.space_reservation_DESCRIPTION', DESCRIPTION: item.description)
### Training reservation
when 'Training'
details += I18n.t('invoices.training_reservation_DESCRIPTION', DESCRIPTION: item.description)
### events reservation
when 'Event'
details += I18n.t('invoices.event_reservation_DESCRIPTION', DESCRIPTION: item.description)
# details of the number of tickets
if invoice.main_item.object.nb_reserve_places.positive?
details += "\n #{I18n.t('invoices.full_price_ticket', count: invoice.main_item.object.nb_reserve_places)}"
end
invoice.main_item.object.tickets.each do |t|
details += "\n #{I18n.t('invoices.other_rate_ticket',
count: t.booked,
NAME: t.event_price_category.price_category.name)}"
end
else
details += item.description
end
else
details += item.description
end
data += [[details, number_to_currency(price)]]
data += [[Invoices::ItemLabelService.build(invoice, item), number_to_currency(price)]]
total_calc += price
total_ht += item.net_amount
total_vat += item.vat
@ -221,71 +169,18 @@ class PDF::Invoice < Prawn::Document
# payment details
move_down 20
if invoice.is_a?(Avoir)
payment_verbose = "#{I18n.t('invoices.refund_on_DATE', DATE: I18n.l(invoice.avoir_date.to_date))} "
case invoice.payment_method
when 'stripe'
payment_verbose += I18n.t('invoices.by_card_online_payment')
when 'cheque'
payment_verbose += I18n.t('invoices.by_cheque')
when 'transfer'
payment_verbose += I18n.t('invoices.by_transfer')
when 'cash'
payment_verbose += I18n.t('invoices.by_cash')
when 'wallet'
payment_verbose += I18n.t('invoices.by_wallet')
when 'none'
payment_verbose = I18n.t('invoices.no_refund')
else
Rails.logger.error "specified refunding method (#{payment_verbose}) is unknown"
end
payment_verbose += " #{I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(total))}"
else
# subtract the wallet amount for this invoice from the total
if invoice.wallet_amount
wallet_amount = invoice.wallet_amount / 100.00
total -= wallet_amount
else
wallet_amount = nil
end
# payment method
payment_verbose = if invoice.paid_by_card?
I18n.t('invoices.settlement_by_debit_card')
else
I18n.t('invoices.settlement_done_at_the_reception')
end
# if the invoice was 100% payed with the wallet ...
payment_verbose = I18n.t('invoices.settlement_by_wallet') if total.zero? && wallet_amount
payment_verbose += " #{I18n.t('invoices.on_DATE_at_TIME',
DATE: I18n.l(invoice.created_at.to_date),
TIME: I18n.l(invoice.created_at, format: :hour_minute))}"
if total.positive? || !invoice.wallet_amount
payment_verbose += " #{I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(total))}"
end
if invoice.wallet_amount
payment_verbose += if total.positive?
" #{I18n.t('invoices.and')} #{I18n.t('invoices.by_wallet')} " \
"#{I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(wallet_amount))}"
else
" #{I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(wallet_amount))}"
end
end
end
text payment_verbose
text Invoices::PaymentDetailsService.build(invoice, total)
# important information
move_down 40
txt = parse_html(Setting.get('invoice_text'))
txt = parse_html(Setting.get('invoice_text').to_s)
txt.each_line do |line|
text line, style: :bold, inline_format: true
end
# address and legals information
move_down 40
txt = parse_html(Setting.get('invoice_legals'))
txt = parse_html(Setting.get('invoice_legals').to_s)
txt.each_line do |line|
text line, align: :right, leading: 4, inline_format: true
end
@ -303,21 +198,6 @@ class PDF::Invoice < Prawn::Document
private
def reservation_dates_verbose(slot)
if slot.start_at.to_date == slot.end_at.to_date
"- #{I18n.t('invoices.on_DATE_from_START_to_END',
DATE: I18n.l(slot.start_at.to_date),
START: I18n.l(slot.start_at, format: :hour_minute),
END: I18n.l(slot.end_at, format: :hour_minute))}\n"
else
"- #{I18n.t('invoices.from_STARTDATE_to_ENDDATE_from_STARTTIME_to_ENDTIME',
STARTDATE: I18n.l(slot.start_at.to_date),
ENDDATE: I18n.l(slot.start_at.to_date),
STARTTIME: I18n.l(slot.start_at, format: :hour_minute),
ENDTIME: I18n.l(slot.end_at, format: :hour_minute))}\n"
end
end
##
# Remove every unsupported html tag from the given html text (like <p>, <span>, ...).
# The supported tags are <b>, <u>, <i> and <br>.

View File

@ -0,0 +1,92 @@
# frozen_string_literal: true
# module definition
module Invoices; end
# Build a label for the given invoice item
class Invoices::ItemLabelService
class << self
# @param invoice [Invoice]
# @param item [InvoiceItem]
# @return [String]
def build(invoice, item)
details = invoice.is_a?(Avoir) ? "#{I18n.t('invoices.cancellation')} - " : ''
if item.object_type == Subscription.name
"#{details}#{build_subscription_label(invoice, item)}"
elsif item.object_type == Reservation.name
"#{details}#{build_reservation_label(invoice, item)}"
else
"#{details}#{item.description}"
end
end
private
# @param invoice [Invoice]
# @param item [InvoiceItem]
# @return [String]
def build_subscription_label(invoice, item)
subscription = item.object
label = if invoice.main_item&.object_type == 'OfferDay'
I18n.t('invoices.subscription_extended_for_free_from_START_to_END',
START: I18n.l(invoice.main_item&.object&.start_at&.to_date),
END: I18n.l(invoice.main_item&.object&.end_at&.to_date))
else
subscription_end_at = subscription.expiration_date
subscription_start_at = subscription_end_at - subscription.plan.duration
I18n.t('invoices.subscription_NAME_from_START_to_END',
NAME: item.description,
START: I18n.l(subscription_start_at.to_date),
END: I18n.l(subscription_end_at.to_date))
end
unless invoice.payment_schedule_item.nil?
dues = invoice.payment_schedule_item.payment_schedule.payment_schedule_items.order(:due_date)
label += "\n #{I18n.t('invoices.from_payment_schedule',
NUMBER: dues.index(invoice.payment_schedule_item) + 1,
TOTAL: dues.count,
DATE: I18n.l(invoice.payment_schedule_item.due_date.to_date),
SCHEDULE: invoice.payment_schedule_item.payment_schedule.reference)}"
end
label
end
# @param invoice [Invoice]
# @param item [InvoiceItem]
# @return [String]
def build_reservation_label(invoice, item)
case invoice.main_item&.object.try(:reservable_type)
### Machine reservation
when 'Machine'
I18n.t('invoices.machine_reservation_DESCRIPTION', DESCRIPTION: item.description)
when 'Space'
I18n.t('invoices.space_reservation_DESCRIPTION', DESCRIPTION: item.description)
### Training reservation
when 'Training'
I18n.t('invoices.training_reservation_DESCRIPTION', DESCRIPTION: item.description)
### events reservation
when 'Event'
build_event_reservation_label(invoice, item)
else
item.description
end
end
# @param invoice [Invoice]
# @param item [InvoiceItem]
# @return [String]
def build_event_reservation_label(invoice, item)
label = I18n.t('invoices.event_reservation_DESCRIPTION', DESCRIPTION: item.description)
# details of the number of tickets
if invoice.main_item&.object&.nb_reserve_places&.positive?
label += "\n #{I18n.t('invoices.full_price_ticket', count: invoice.main_item&.object&.nb_reserve_places)}"
end
invoice.main_item&.object&.tickets&.each do |t|
label += "\n #{I18n.t('invoices.other_rate_ticket',
count: t.booked,
NAME: t.event_price_category.price_category.name)}"
end
label
end
end
end

View File

@ -6,12 +6,14 @@ module Invoices; end
# Build a label for the given invoice
class Invoices::LabelService
class << self
# @param invoice [Invoice]
# @return [String, nil]
def build(invoice)
username = Invoices::RecipientService.name(invoice)
if invoice.is_a?(Avoir)
avoir_label(invoice)
else
case invoice.main_item.object_type
case invoice.main_item&.object_type
when 'Reservation'
reservation_invoice_label(invoice, username)
when 'Subscription'
@ -25,7 +27,7 @@ class Invoices::LabelService
when 'OrderItem'
I18n.t('invoices.order')
else
Rails.logger.error "specified main_item.object_type type (#{invoice.main_item.object_type}) is unknown"
Rails.logger.error "specified main_item.object_type type (#{invoice.main_item&.object_type}) is unknown"
nil
end
end
@ -33,12 +35,17 @@ class Invoices::LabelService
private
# @param invoice [Invoice]
# @return [String]
def avoir_label(invoice)
return I18n.t('invoices.wallet_credit') if invoice.main_item.object_type == WalletTransaction.name
return I18n.t('invoices.wallet_credit') if invoice.main_item&.object_type == WalletTransaction.name
I18n.t('invoices.cancellation_of_invoice_REF', REF: invoice.invoice.reference)
end
# @param invoice [Invoice]
# @param username [String]
# @return [String]
def reservation_invoice_label(invoice, username)
label = I18n.t('invoices.reservation_of_USER_on_DATE_at_TIME',
USER: username,
@ -55,6 +62,9 @@ class Invoices::LabelService
label
end
# @param subscription [Subscription]
# @param username [String]
# @return [String]
def subscription_label(subscription, username)
subscription_start_at = subscription.expired_at - subscription.plan.duration
duration_verbose = I18n.t("duration.#{subscription.plan.interval}", count: subscription.plan.interval_count)
@ -64,6 +74,9 @@ class Invoices::LabelService
DATE: I18n.l(subscription_start_at.to_date))
end
# @param offer_day [OfferDay]
# @param username [String]
# @return [String]
def offer_day_label(offer_day, username)
I18n.t('invoices.subscription_of_NAME_extended_starting_from_STARTDATE_until_ENDDATE',
NAME: username,

View File

@ -0,0 +1,79 @@
# frozen_string_literal: true
# module definition
module Invoices; end
# Build a localized string detailing the payment mean for the given invoice
class Invoices::PaymentDetailsService
class << self
include ActionView::Helpers::NumberHelper
# @param invoice [Invoice]
# @param total [Float]
# @return [String]
def build(invoice, total)
if invoice.is_a?(Avoir)
build_avoir_details(invoice)
else
# subtract the wallet amount for this invoice from the total
if invoice.wallet_amount
wallet_amount = invoice.wallet_amount / 100.00
total -= wallet_amount
else
wallet_amount = nil
end
# payment method
payment_verbose = if invoice.paid_by_card?
I18n.t('invoices.settlement_by_debit_card')
else
I18n.t('invoices.settlement_done_at_the_reception')
end
# if the invoice was 100% payed with the wallet ...
payment_verbose = I18n.t('invoices.settlement_by_wallet') if total.zero? && wallet_amount
payment_verbose += " #{I18n.t('invoices.on_DATE_at_TIME',
DATE: I18n.l(invoice.created_at.to_date),
TIME: I18n.l(invoice.created_at, format: :hour_minute))}"
if total.positive? || !invoice.wallet_amount
payment_verbose += " #{I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(total))}"
end
if invoice.wallet_amount
payment_verbose += if total.positive?
" #{I18n.t('invoices.and')} #{I18n.t('invoices.by_wallet')} " \
"#{I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(wallet_amount))}"
else
" #{I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(wallet_amount))}"
end
end
payment_verbose
end
end
private
# @param invoice [Invoice]
# @return [String]
def build_avoir_details(invoice)
details = "#{I18n.t('invoices.refund_on_DATE', DATE: I18n.l(invoice.avoir_date.to_date))} "
case invoice.payment_method
when 'stripe'
details += I18n.t('invoices.by_card_online_payment')
when 'cheque'
details += I18n.t('invoices.by_cheque')
when 'transfer'
details += I18n.t('invoices.by_transfer')
when 'cash'
details += I18n.t('invoices.by_cash')
when 'wallet'
details += I18n.t('invoices.by_wallet')
when 'none'
details = I18n.t('invoices.no_refund')
else
Rails.logger.error "specified refunding method (#{details}) is unknown"
end
"#{details} #{I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(total))}"
end
end
end

View File

@ -7,6 +7,8 @@ module Invoices; end
class Invoices::RecipientService
class << self
# Get the full name of the recipient for the given invoice.
# @param invoice [Invoice]
# @return [String]
def name(invoice)
if invoice.invoicing_profile.organization
name = invoice.invoicing_profile.organization.name
@ -17,11 +19,15 @@ class Invoices::RecipientService
end
# Get the street address of the recipient for the given invoice.
# @param invoice [Invoice]
# @return [String]
def address(invoice)
invoice.invoicing_profile&.invoicing_address
end
# Get the optional data in profile_custom_fields, if the recipient is an organization
# @param invoice [Invoice]
# @return [Array<String>]
def organization_data(invoice)
return unless invoice.invoicing_profile.organization

View File

@ -4,10 +4,10 @@
class InvoiceWorker
include Sidekiq::Worker
def perform(invoice_id, subscription_expiration_date)
def perform(invoice_id)
# generate a invoice
invoice = Invoice.find invoice_id
pdf = ::PDF::Invoice.new(invoice, subscription_expiration_date).render
pdf = ::PDF::Invoice.new(invoice).render
# store invoice on drive
File.binwrite(invoice.file, pdf)
@ -23,5 +23,4 @@ class InvoiceWorker
attached_object: invoice
end
end
end

View File

@ -10,12 +10,13 @@ class PaymentScheduleItemWorker
psi = PaymentScheduleItem.find(record_id)
check_item(psi)
else
PaymentScheduleItem.where.not(state: 'paid').where('due_date < ?', DateTime.current).each do |psi|
check_item(psi)
PaymentScheduleItem.where.not(state: 'paid').where('due_date < ?', DateTime.current).each do |item|
check_item(item)
end
end
end
# @param psi [PaymentScheduleItem]
def check_item(psi)
# the following depends on the payment method (card/check)
if psi.payment_schedule.payment_method == 'card'
@ -26,9 +27,10 @@ class PaymentScheduleItemWorker
NotificationCenter.call type: "notify_admin_payment_schedule_#{psi.payment_schedule.payment_method}_deadline",
receiver: User.admins_and_managers,
attached_object: psi
psi.update_attributes(state: 'pending')
psi.update(state: 'pending')
end
rescue StandardError
psi.update_attributes(state: 'error')
rescue StandardError => e
Rails.logger.debug(e.backtrace)
psi.update(state: 'error')
end
end

View File

@ -155,7 +155,7 @@ fr:
credits_panel:
title_Space: "Mes crédits espace"
title_Machine: "Mes crédits machine"
reamaining_credits_html: "<strong>{NAME}</strong> : Vous pouvez réserver gratuitement <em>{REMAINING} {REMAINING, plural, =0{créneau} one{créneau} other{créneaux}}</em>. Vous avez déjà utilisé {USED} {USED, plural, =0{crédit} one{crédit} other{crédits}} de votre abonnement actuel."
reamaining_credits_html: "<strong>{NAME}</strong> : Vous pouvez réserver gratuitement <em>{REMAINING} {REMAINING, plural, one{créneau} other{créneaux}}</em>. Vous avez déjà utilisé {USED} {USED, plural, one{crédit} other{crédits}} de votre abonnement actuel."
no_credits: "Vous n'avez pas encore de crédits. Certains abonnements peuvent vous permettre de réserver des créneaux gratuitement."
#public profil of a member
members_show:

View File

@ -145,18 +145,18 @@ zu:
edit: "crwdns27632:0crwdne27632:0"
reservations:
reservations_panel:
title_Space: "crwdns27634:0crwdne27634:0"
title_Machine: "crwdns27636:0crwdne27636:0"
upcoming: "crwdns27638:0crwdne27638:0"
past: "crwdns27640:0crwdne27640:0"
slots_details: "crwdns27642:0crwdne27642:0"
no_reservations: "crwdns27644:0crwdne27644:0"
show_more: "crwdns27646:0crwdne27646:0"
title_Space: "crwdns36333:0crwdne36333:0"
title_Machine: "crwdns36335:0crwdne36335:0"
upcoming: "crwdns36337:0crwdne36337:0"
past: "crwdns36339:0crwdne36339:0"
slots_details: "crwdns36341:0crwdne36341:0"
no_reservations: "crwdns36343:0crwdne36343:0"
show_more: "crwdns36345:0crwdne36345:0"
credits_panel:
title_Space: "crwdns27648:0crwdne27648:0"
title_Machine: "crwdns27650:0crwdne27650:0"
reamaining_credits_html: "crwdns27652:0NAME={NAME}crwdnd27652:0REMAINING={REMAINING}crwdnd27652:0REMAINING={REMAINING}crwdnd27652:0USED={USED}crwdnd27652:0USED={USED}crwdne27652:0"
no_credits: "crwdns27654:0crwdne27654:0"
title_Space: "crwdns36347:0crwdne36347:0"
title_Machine: "crwdns36349:0crwdne36349:0"
reamaining_credits_html: "crwdns36351:0NAME={NAME}crwdnd36351:0REMAINING={REMAINING}crwdnd36351:0REMAINING={REMAINING}crwdnd36351:0USED={USED}crwdnd36351:0USED={USED}crwdne36351:0"
no_credits: "crwdns36353:0crwdne36353:0"
#public profil of a member
members_show:
members_list: "crwdns27656:0crwdne27656:0"

View File

@ -98,6 +98,7 @@ de:
space_reservation_DESCRIPTION: "Raumreservierung - %{DESCRIPTION}"
training_reservation_DESCRIPTION: "Trainingsreservierung - %{DESCRIPTION}"
event_reservation_DESCRIPTION: "Veranstaltungs-Reservierung - %{DESCRIPTION}"
from_payment_schedule: "Due %{NUMBER} out of %{TOTAL}, from %{DATE}. Repayment schedule %{SCHEDULE}"
full_price_ticket:
one: "Ein Vollpreis-Ticket"
other: "%{count} Vollpreis-Tickets"

View File

@ -98,6 +98,7 @@ en:
space_reservation_DESCRIPTION: "Space reservation - %{DESCRIPTION}"
training_reservation_DESCRIPTION: "Training reservation - %{DESCRIPTION}"
event_reservation_DESCRIPTION: "Event reservation - %{DESCRIPTION}"
from_payment_schedule: "Due %{NUMBER} out of %{TOTAL}, from %{DATE}. Repayment schedule %{SCHEDULE}"
full_price_ticket:
one: "One full price ticket"
other: "%{count} full price tickets"

View File

@ -98,6 +98,7 @@ es:
space_reservation_DESCRIPTION: "Reserva de espacio - %{DESCRIPTION}"
training_reservation_DESCRIPTION: "Reserva de curso - %{DESCRIPTION}"
event_reservation_DESCRIPTION: "Reserva de evento - %{DESCRIPTION}"
from_payment_schedule: "Due %{NUMBER} out of %{TOTAL}, from %{DATE}. Repayment schedule %{SCHEDULE}"
full_price_ticket:
one: "Una entrada de precio completo"
other: "%{count} entradas de precio completo"

View File

@ -98,6 +98,7 @@ fr:
space_reservation_DESCRIPTION: "Réservation Espace - %{DESCRIPTION}"
training_reservation_DESCRIPTION: "Réservation Formation - %{DESCRIPTION}"
event_reservation_DESCRIPTION: "Réservation Événement - %{DESCRIPTION}"
from_payment_schedule: "Échéance %{NUMBER} sur %{TOTAL}, du %{DATE}. Échéancier de paiement %{SCHEDULE}"
full_price_ticket:
one: "Une place plein tarif"
other: "%{count} places plein tarif"

View File

@ -98,6 +98,7 @@
space_reservation_DESCRIPTION: "Reservasjon av plass/rom - %{DESCRIPTION}"
training_reservation_DESCRIPTION: "Bestilling, opplæring/kurs - %{DESCRIPTION}"
event_reservation_DESCRIPTION: "Arrangements-reservasjon - %{DESCRIPTION}"
from_payment_schedule: "Due %{NUMBER} out of %{TOTAL}, from %{DATE}. Repayment schedule %{SCHEDULE}"
full_price_ticket:
one: "En fullprisbillett"
other: "%{count} full price tickets"

View File

@ -98,6 +98,7 @@ pt:
space_reservation_DESCRIPTION: "Reserva de máquina - %{DESCRIPTION}"
training_reservation_DESCRIPTION: "Reserva de treinamneto - %{DESCRIPTION}"
event_reservation_DESCRIPTION: "Reserva de evento - %{DESCRIPTION}"
from_payment_schedule: "Due %{NUMBER} out of %{TOTAL}, from %{DATE}. Repayment schedule %{SCHEDULE}"
full_price_ticket:
one: "Um ticket de preço cheio"
other: "%{count} tickets de preço cheio"

View File

@ -98,6 +98,7 @@ zu:
space_reservation_DESCRIPTION: "crwdns3319:0%{DESCRIPTION}crwdne3319:0"
training_reservation_DESCRIPTION: "crwdns3321:0%{DESCRIPTION}crwdne3321:0"
event_reservation_DESCRIPTION: "crwdns3323:0%{DESCRIPTION}crwdne3323:0"
from_payment_schedule: "crwdns36355:0%{NUMBER}crwdnd36355:0%{TOTAL}crwdnd36355:0%{DATE}crwdnd36355:0%{SCHEDULE}crwdne36355:0"
full_price_ticket:
one: "crwdns3325:1crwdne3325:1"
other: "crwdns3325:5%{count}crwdne3325:5"

View File

@ -31,7 +31,7 @@ module InvoiceHelper
def generate_pdf(invoice)
invoice_worker = InvoiceWorker.new
invoice_worker.perform(invoice.id, invoice&.user&.subscription&.expired_at)
invoice_worker.perform(invoice.id)
end
# Parse a line of text read from a PDF file and return the price included inside