mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-20 14:54:15 +01:00
fix wallet-info component to use cartItems
This commit is contained in:
parent
75e0fece13
commit
f4f1464826
@ -174,7 +174,7 @@ const StripeModal: React.FC<StripeModalProps> = ({ isOpen, toggleModal, afterSuc
|
||||
customFooter={logoFooter()}
|
||||
className="stripe-modal">
|
||||
{ready && <StripeElements>
|
||||
<WalletInfo reservation={cartItems?.reservation} currentUser={currentUser} wallet={wallet} price={price?.price} />
|
||||
<WalletInfo cartItems={cartItems} currentUser={currentUser} wallet={wallet} price={price?.price} />
|
||||
<StripeForm onSubmit={handleSubmit}
|
||||
onSuccess={handleFormSuccess}
|
||||
onError={handleFormError}
|
||||
|
@ -8,23 +8,25 @@ import { react2angular } from 'react2angular';
|
||||
import { IApplication } from '../models/application';
|
||||
import '../lib/i18n';
|
||||
import { Loader } from './loader';
|
||||
import { Reservation } from '../models/reservation';
|
||||
import { User } from '../models/user';
|
||||
import { Wallet } from '../models/wallet';
|
||||
import { IFablab } from '../models/fablab';
|
||||
import WalletLib from '../lib/wallet';
|
||||
import { CartItems } from '../models/payment';
|
||||
import { Reservation } from '../models/reservation';
|
||||
import { SubscriptionRequest } from '../models/subscription';
|
||||
|
||||
declare var Application: IApplication;
|
||||
declare var Fablab: IFablab;
|
||||
|
||||
interface WalletInfoProps {
|
||||
reservation: Reservation,
|
||||
cartItems: CartItems,
|
||||
currentUser: User,
|
||||
wallet: Wallet,
|
||||
price: number,
|
||||
}
|
||||
|
||||
export const WalletInfo: React.FC<WalletInfoProps> = ({reservation, currentUser, wallet, price}) => {
|
||||
export const WalletInfo: React.FC<WalletInfoProps> = ({ cartItems, currentUser, wallet, price }) => {
|
||||
const { t } = useTranslation('shared');
|
||||
const [remainingPrice, setRemainingPrice] = useState(0);
|
||||
|
||||
@ -34,20 +36,26 @@ export const WalletInfo: React.FC<WalletInfoProps> = ({reservation, currentUser,
|
||||
useEffect(() => {
|
||||
const wLib = new WalletLib(wallet);
|
||||
setRemainingPrice(wLib.computeRemainingPrice(price));
|
||||
})
|
||||
});
|
||||
|
||||
/**
|
||||
* Return the formatted localized amount for the given price (eg. 20.5 => "20,50 €")
|
||||
* Return the formatted localized amount for the given price (e.g. 20.5 => "20,50 €")
|
||||
*/
|
||||
const formatPrice = (price: number): string => {
|
||||
return new Intl.NumberFormat(Fablab.intl_locale, {style: 'currency', currency: Fablab.intl_currency}).format(price);
|
||||
}
|
||||
/**
|
||||
* Check if the currently connected used is also the person making the reservation.
|
||||
* If the currently connected user (ie. the operator), is an admin or a manager, he may book the reservation for someone else.
|
||||
* If the currently connected user (i.e. the operator), is an admin or a manager, he may book the reservation for someone else.
|
||||
*/
|
||||
const isOperatorAndClient = (): boolean => {
|
||||
return currentUser.id == reservation.user_id;
|
||||
return currentUser.id == buyingItem().user_id;
|
||||
}
|
||||
/**
|
||||
* Return the item currently bought (reservation or subscription)
|
||||
*/
|
||||
const buyingItem = (): Reservation|SubscriptionRequest => {
|
||||
return cartItems.reservation || cartItems.subscription;
|
||||
}
|
||||
/**
|
||||
* If the client has some money in his wallet & the price is not zero, then we should display this component.
|
||||
@ -66,17 +74,17 @@ export const WalletInfo: React.FC<WalletInfoProps> = ({reservation, currentUser,
|
||||
* Does the current cart contains a payment schedule?
|
||||
*/
|
||||
const isPaymentSchedule = (): boolean => {
|
||||
return reservation.plan_id && reservation.payment_schedule;
|
||||
return buyingItem().plan_id && buyingItem().payment_schedule;
|
||||
}
|
||||
/**
|
||||
* Return the human-readable name of the item currently bought with the wallet
|
||||
*/
|
||||
const getPriceItem = (): string => {
|
||||
let item = 'other';
|
||||
if (reservation.slots_attributes.length > 0) {
|
||||
if (cartItems.reservation) {
|
||||
item = 'reservation';
|
||||
} else if (reservation.plan_id) {
|
||||
if (reservation.payment_schedule) {
|
||||
} else if (cartItems.subscription) {
|
||||
if (cartItems.subscription.payment_schedule) {
|
||||
item = 'first_deadline';
|
||||
} else item = 'subscription';
|
||||
}
|
||||
@ -120,12 +128,12 @@ export const WalletInfo: React.FC<WalletInfoProps> = ({reservation, currentUser,
|
||||
);
|
||||
}
|
||||
|
||||
const WalletInfoWrapper: React.FC<WalletInfoProps> = ({currentUser, reservation, price, wallet}) => {
|
||||
const WalletInfoWrapper: React.FC<WalletInfoProps> = ({ currentUser, cartItems, price, wallet }) => {
|
||||
return (
|
||||
<Loader>
|
||||
<WalletInfo currentUser={currentUser} reservation={reservation} price={price} wallet={wallet}/>
|
||||
<WalletInfo currentUser={currentUser} cartItems={cartItems} price={price} wallet={wallet}/>
|
||||
</Loader>
|
||||
);
|
||||
}
|
||||
|
||||
Application.Components.component('walletInfo', react2angular(WalletInfoWrapper, ['currentUser', 'price', 'reservation', 'wallet']));
|
||||
Application.Components.component('walletInfo', react2angular(WalletInfoWrapper, ['currentUser', 'price', 'cartItems', 'wallet']));
|
||||
|
@ -704,9 +704,12 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'growl', 'wallet', 'helpers', '$filter', 'coupon', 'cartItems', 'stripeKey',
|
||||
function ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation, growl, wallet, helpers, $filter, coupon, cartItems, stripeKey) {
|
||||
// User's wallet amount
|
||||
$scope.walletAmount = wallet.amount;
|
||||
$scope.wallet = wallet;
|
||||
|
||||
// Price
|
||||
$scope.price = price.price;
|
||||
|
||||
// Amount to pay
|
||||
$scope.amount = helpers.getAmountToPay(price.price, wallet.amount);
|
||||
|
||||
// Cart items
|
||||
@ -753,16 +756,22 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
},
|
||||
coupon () {
|
||||
return $scope.coupon.applied;
|
||||
}
|
||||
},
|
||||
cartItems () {
|
||||
return mkRequestParams(reservation, $scope.coupon.applied);
|
||||
},
|
||||
},
|
||||
controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', 'wallet', 'helpers', '$filter', 'coupon',
|
||||
function ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation, wallet, helpers, $filter, coupon) {
|
||||
controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', 'wallet', 'helpers', '$filter', 'coupon', 'cartItems',
|
||||
function ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation, wallet, helpers, $filter, coupon, cartItems) {
|
||||
// User's wallet amount
|
||||
$scope.walletAmount = wallet.amount;
|
||||
$scope.wallet = wallet;
|
||||
|
||||
// Price
|
||||
$scope.price = price.price;
|
||||
|
||||
// Cart items
|
||||
$scope.cartItems = cartItems;
|
||||
|
||||
// price to pay
|
||||
$scope.amount = helpers.getAmountToPay(price.price, wallet.amount);
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Reservation } from './reservation';
|
||||
import { SubscriptionRequest } from './subscription';
|
||||
|
||||
export interface PaymentConfirmation {
|
||||
requires_action?: boolean,
|
||||
@ -20,11 +21,6 @@ export enum PaymentMethod {
|
||||
|
||||
export interface CartItems {
|
||||
reservation?: Reservation,
|
||||
subscription?: {
|
||||
plan_id: number,
|
||||
user_id: number,
|
||||
payment_schedule: boolean,
|
||||
payment_method: PaymentMethod
|
||||
},
|
||||
subscription?: SubscriptionRequest,
|
||||
coupon_code?: string
|
||||
}
|
||||
|
18
app/frontend/src/javascript/models/subscription.ts
Normal file
18
app/frontend/src/javascript/models/subscription.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { Plan } from './plan';
|
||||
import { PaymentMethod } from './payment';
|
||||
|
||||
export interface Subscription {
|
||||
id: number,
|
||||
plan_id: number,
|
||||
expired_at: Date,
|
||||
canceled_at?: Date,
|
||||
stripe: boolean,
|
||||
plan: Plan
|
||||
}
|
||||
|
||||
export interface SubscriptionRequest {
|
||||
plan_id: number,
|
||||
user_id: number,
|
||||
payment_schedule: boolean,
|
||||
payment_method: PaymentMethod
|
||||
}
|
@ -41,7 +41,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<wallet-info current-user="currentUser"
|
||||
reservation="reservation"
|
||||
cart-items="cartItems"
|
||||
price="price"
|
||||
wallet="wallet"/>
|
||||
</div>
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
<div class="row">
|
||||
<wallet-info current-user="currentUser"
|
||||
reservation="reservation"
|
||||
cart-items="cartItems"
|
||||
price="price"
|
||||
wallet="wallet"/>
|
||||
</div>
|
||||
|
383
app/pdfs/pdf/payment_schedule.rb
Normal file
383
app/pdfs/pdf/payment_schedule.rb
Normal file
@ -0,0 +1,383 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Generate a downloadable PDF file for the recorded payment schedule
|
||||
class PDF::PaymentSchedule < Prawn::Document
|
||||
require 'stringio'
|
||||
include ActionView::Helpers::NumberHelper
|
||||
include ApplicationHelper
|
||||
|
||||
def initialize(payment_schedule)
|
||||
super(margin: 70)
|
||||
|
||||
# fonts
|
||||
opensans = Rails.root.join('vendor/assets/fonts/OpenSans-Regular.ttf').to_s
|
||||
opensans_bold = Rails.root.join('vendor/assets/fonts/OpenSans-Bold.ttf').to_s
|
||||
opensans_bolditalic = Rails.root.join('vendor/assets/fonts/OpenSans-BoldItalic.ttf').to_s
|
||||
opensans_italic = Rails.root.join('vendor/assets/fonts/OpenSans-Italic.ttf').to_s
|
||||
|
||||
font_families.update(
|
||||
'Open-Sans' => {
|
||||
normal: { file: opensans, font: 'Open-Sans' },
|
||||
bold: { file: opensans_bold, font: 'Open-Sans-Bold' },
|
||||
italic: { file: opensans_italic, font: 'Open-Sans-Oblique' },
|
||||
bold_italic: { file: opensans_bolditalic, font: 'Open-Sans-BoldOblique' }
|
||||
}
|
||||
)
|
||||
|
||||
# logo
|
||||
img_b64 = Setting.find_by(name: 'invoice_logo')
|
||||
begin
|
||||
image StringIO.new(Base64.decode64(img_b64.value)), fit: [415, 40]
|
||||
rescue StandardError => e
|
||||
puts "Unable to decode invoice logo from base64: #{e}"
|
||||
end
|
||||
move_down 20
|
||||
font('Open-Sans', size: 10) do
|
||||
# general information
|
||||
text I18n.t('payment_schedules.schedule_reference', REF: payment_schedule.reference), leading: 3
|
||||
text I18n.t('payment_schedules.code', CODE: Setting.get('invoice_code-value')), leading: 3 if Setting.get('invoice_code-active')
|
||||
text I18n.t('payment_schedules.order_number', NUMBER: payment_schedule.order_number), leading: 3
|
||||
text I18n.t('payment_schedules.schedule_issued_on_DATE', DATE: I18n.l(payment_schedule.created_at.to_date))
|
||||
|
||||
# user/organization's information
|
||||
if invoice&.invoicing_profile&.organization
|
||||
name = invoice.invoicing_profile.organization.name
|
||||
full_name = "#{name} (#{invoice.invoicing_profile.full_name})"
|
||||
else
|
||||
name = invoice.invoicing_profile.full_name
|
||||
full_name = name
|
||||
end
|
||||
|
||||
address = if invoice&.invoicing_profile&.organization&.address
|
||||
invoice.invoicing_profile.organization.address.address
|
||||
elsif invoice&.invoicing_profile&.address
|
||||
invoice.invoicing_profile.address.address
|
||||
else
|
||||
''
|
||||
end
|
||||
|
||||
text_box "<b>#{name}</b>\n#{invoice.invoicing_profile.email}\n#{address}",
|
||||
at: [bounds.width - 130, bounds.top - 49],
|
||||
width: 130,
|
||||
align: :right,
|
||||
inline_format: true
|
||||
name = full_name
|
||||
|
||||
# object
|
||||
move_down 25
|
||||
if invoice.is_a?(Avoir)
|
||||
object = if invoice.invoiced_type == WalletTransaction.name
|
||||
I18n.t('invoices.wallet_credit')
|
||||
else
|
||||
I18n.t('invoices.cancellation_of_invoice_REF', REF: invoice.invoice.reference)
|
||||
end
|
||||
else
|
||||
case invoice.invoiced_type
|
||||
when 'Reservation'
|
||||
object = I18n.t('invoices.reservation_of_USER_on_DATE_at_TIME',
|
||||
USER: name,
|
||||
DATE: I18n.l(invoice.invoiced.slots[0].start_at.to_date),
|
||||
TIME: I18n.l(invoice.invoiced.slots[0].start_at, format: :hour_minute))
|
||||
invoice.invoice_items.each do |item|
|
||||
next unless item.subscription_id
|
||||
|
||||
subscription = Subscription.find item.subscription_id
|
||||
cancellation = invoice.is_a?(Avoir) ? I18n.t('invoices.cancellation') + ' - ' : ''
|
||||
object = "\n- #{object}\n- #{cancellation + subscription_verbose(subscription, name)}"
|
||||
break
|
||||
end
|
||||
when 'Subscription'
|
||||
object = subscription_verbose(invoice.invoiced, name)
|
||||
when 'OfferDay'
|
||||
object = offer_day_verbose(invoice.invoiced, name)
|
||||
else
|
||||
puts "ERROR : specified invoiced type (#{invoice.invoiced_type}) is unknown"
|
||||
end
|
||||
end
|
||||
text I18n.t('invoices.object') + ' ' + object
|
||||
|
||||
# details table of the invoice's elements
|
||||
move_down 20
|
||||
text I18n.t('invoices.order_summary'), leading: 4
|
||||
move_down 2
|
||||
data = [[I18n.t('invoices.details'), I18n.t('invoices.amount')]]
|
||||
|
||||
total_calc = 0
|
||||
total_ht = 0
|
||||
total_vat = 0
|
||||
# going through invoice_items
|
||||
invoice.invoice_items.each do |item|
|
||||
|
||||
price = item.amount.to_i / 100.00
|
||||
|
||||
details = invoice.is_a?(Avoir) ? I18n.t('invoices.cancellation') + ' - ' : ''
|
||||
|
||||
if item.subscription_id ### Subscription
|
||||
subscription = Subscription.find item.subscription_id
|
||||
if invoice.invoiced_type == 'OfferDay'
|
||||
details += I18n.t('invoices.subscription_extended_for_free_from_START_to_END',
|
||||
START: I18n.l(invoice.invoiced.start_at.to_date),
|
||||
END: I18n.l(invoice.invoiced.end_at.to_date))
|
||||
else
|
||||
subscription_end_at = if subscription_expiration_date.is_a?(Time)
|
||||
subscription_expiration_date
|
||||
elsif subscription_expiration_date.is_a?(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
|
||||
|
||||
|
||||
else ### Reservation
|
||||
case invoice.invoiced.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.invoiced.nb_reserve_places.positive?
|
||||
details += "\n " + I18n.t('invoices.full_price_ticket', count: invoice.invoiced.nb_reserve_places)
|
||||
end
|
||||
invoice.invoiced.tickets.each do |t|
|
||||
details += "\n " + I18n.t('invoices.other_rate_ticket',
|
||||
count: t.booked,
|
||||
NAME: t.event_price_category.price_category.name)
|
||||
end
|
||||
### wallet credit
|
||||
when nil
|
||||
details = item.description
|
||||
|
||||
### Other cases (not expected)
|
||||
else
|
||||
details += I18n.t('invoices.reservation_other')
|
||||
end
|
||||
end
|
||||
|
||||
data += [[details, number_to_currency(price)]]
|
||||
total_calc += price
|
||||
total_ht += item.net_amount
|
||||
total_vat += item.vat
|
||||
end
|
||||
|
||||
## subtract the coupon, if any
|
||||
unless invoice.coupon_id.nil?
|
||||
cp = invoice.coupon
|
||||
discount = 0
|
||||
if cp.type == 'percent_off'
|
||||
discount = total_calc * cp.percent_off / 100.00
|
||||
elsif cp.type == 'amount_off'
|
||||
# refunds of invoices with cash coupons: we need to ventilate coupons on paid items
|
||||
if invoice.is_a?(Avoir)
|
||||
paid_items = invoice.invoice.invoice_items.select { |ii| ii.amount.positive? }.length
|
||||
refund_items = invoice.invoice_items.select { |ii| ii.amount.positive? }.length
|
||||
|
||||
discount = ((invoice.coupon.amount_off / paid_items) * refund_items) / 100.00
|
||||
else
|
||||
discount = cp.amount_off / 100.00
|
||||
end
|
||||
else
|
||||
raise InvalidCouponError
|
||||
end
|
||||
|
||||
total_calc -= discount
|
||||
|
||||
# discount textual description
|
||||
literal_discount = cp.percent_off
|
||||
literal_discount = number_to_currency(cp.amount_off / 100.00) if cp.type == 'amount_off'
|
||||
|
||||
# add a row for the coupon
|
||||
data += [[_t('invoices.coupon_CODE_discount_of_DISCOUNT',
|
||||
CODE: cp.code,
|
||||
DISCOUNT: literal_discount,
|
||||
TYPE: cp.type), number_to_currency(-discount)]]
|
||||
end
|
||||
|
||||
# total verification
|
||||
total = invoice.total / 100.00
|
||||
puts "ERROR: totals are NOT equals => expected: #{total}, computed: #{total_calc}" if total_calc != total
|
||||
|
||||
# TVA
|
||||
vat_service = VatHistoryService.new
|
||||
vat_rate = vat_service.invoice_vat(invoice)
|
||||
if vat_rate != 0
|
||||
data += [[I18n.t('invoices.total_including_all_taxes'), number_to_currency(total)]]
|
||||
data += [[I18n.t('invoices.including_VAT_RATE', RATE: vat_rate), number_to_currency(total_vat / 100.00)]]
|
||||
data += [[I18n.t('invoices.including_total_excluding_taxes'), number_to_currency(total_ht / 100.00)]]
|
||||
data += [[I18n.t('invoices.including_amount_payed_on_ordering'), number_to_currency(total)]]
|
||||
|
||||
# checking the round number
|
||||
rounded = sprintf('%.2f', total_vat / 100.00).to_f + sprintf('%.2f', total_ht / 100.00).to_f
|
||||
if rounded != sprintf('%.2f', total_calc).to_f
|
||||
puts 'ERROR: rounding the numbers cause an invoice inconsistency. ' \
|
||||
"Total expected: #{sprintf('%.2f', total_calc)}, total computed: #{rounded}"
|
||||
end
|
||||
else
|
||||
data += [[I18n.t('invoices.total_amount'), number_to_currency(total)]]
|
||||
end
|
||||
|
||||
# display table
|
||||
table(data, header: true, column_widths: [400, 72], cell_style: { inline_format: true }) do
|
||||
row(0).font_style = :bold
|
||||
column(1).style align: :right
|
||||
|
||||
if Setting.get('invoice_VAT-active')
|
||||
# Total incl. taxes
|
||||
row(-1).style align: :right
|
||||
row(-1).background_color = 'E4E4E4'
|
||||
row(-1).font_style = :bold
|
||||
# including VAT xx%
|
||||
row(-2).style align: :right
|
||||
row(-2).background_color = 'E4E4E4'
|
||||
row(-2).font_style = :italic
|
||||
# including total excl. taxes
|
||||
row(-3).style align: :right
|
||||
row(-3).background_color = 'E4E4E4'
|
||||
row(-3).font_style = :italic
|
||||
# including amount payed on ordering
|
||||
row(-4).style align: :right
|
||||
row(-4).background_color = 'E4E4E4'
|
||||
row(-4).font_style = :bold
|
||||
end
|
||||
end
|
||||
|
||||
# optional description for refunds
|
||||
move_down 20
|
||||
text invoice.description if invoice.is_a?(Avoir) && invoice.description
|
||||
|
||||
# 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_stripe_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
|
||||
puts "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_with_stripe?
|
||||
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
|
||||
|
||||
# important information
|
||||
move_down 40
|
||||
txt = parse_html(Setting.get('invoice_text'))
|
||||
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.each_line do |line|
|
||||
text line, align: :right, leading: 4, inline_format: true
|
||||
end
|
||||
end
|
||||
|
||||
# factice watermark
|
||||
return unless %w[staging test development].include?(invoice.environment)
|
||||
|
||||
transparent(0.1) do
|
||||
rotate(45, origin: [0, 0]) do
|
||||
image "#{Rails.root}/app/pdfs/data/watermark-#{I18n.locale}.png", at: [90, 150]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
def subscription_verbose(subscription, username)
|
||||
subscription_start_at = subscription.expired_at - subscription.plan.duration
|
||||
duration_verbose = I18n.t("duration.#{subscription.plan.interval}", count: subscription.plan.interval_count)
|
||||
I18n.t('invoices.subscription_of_NAME_for_DURATION_starting_from_DATE',
|
||||
NAME: username,
|
||||
DURATION: duration_verbose,
|
||||
DATE: I18n.l(subscription_start_at.to_date))
|
||||
end
|
||||
|
||||
def offer_day_verbose(offer_day, username)
|
||||
I18n.t('invoices.subscription_of_NAME_extended_starting_from_STARTDATE_until_ENDDATE',
|
||||
NAME: username,
|
||||
STARTDATE: I18n.l(offer_day.start_at.to_date),
|
||||
ENDDATE: I18n.l(offer_day.end_at.to_date))
|
||||
end
|
||||
|
||||
##
|
||||
# Remove every unsupported html tag from the given html text (like <p>, <span>, ...).
|
||||
# The supported tags are <b>, <u>, <i> and <br>.
|
||||
# @param html [String] single line html text
|
||||
# @return [String] multi line simplified html text
|
||||
##
|
||||
def parse_html(html)
|
||||
ActionController::Base.helpers.sanitize(html, tags: %w[b u i br])
|
||||
end
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user