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:
parent
ac187a986f
commit
10f054256e
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>.
|
||||
|
92
app/services/invoices/item_label_service.rb
Normal file
92
app/services/invoices/item_label_service.rb
Normal 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
|
@ -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,
|
||||
|
79
app/services/invoices/payment_details_service.rb
Normal file
79
app/services/invoices/payment_details_service.rb
Normal 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
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user