mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-19 13:54:25 +01:00
buy prepaid-pack a hours from modal
This commit is contained in:
parent
91d2316280
commit
1aad4891c1
@ -13,13 +13,14 @@ interface MachineCardProps {
|
||||
onLoginRequested: () => Promise<User>,
|
||||
onEnrollRequested: (trainingId: number) => void,
|
||||
onError: (message: string) => void,
|
||||
onSuccess: (message: string) => void,
|
||||
}
|
||||
|
||||
/**
|
||||
* This component is a box showing the picture of the given machine and two buttons: one to start the reservation process
|
||||
* and another to redirect the user to the machine description page.
|
||||
*/
|
||||
const MachineCardComponent: React.FC<MachineCardProps> = ({ user, machine, onShowMachine, onReserveMachine, onError, onLoginRequested, onEnrollRequested }) => {
|
||||
const MachineCardComponent: React.FC<MachineCardProps> = ({ user, machine, onShowMachine, onReserveMachine, onError, onSuccess, onLoginRequested, onEnrollRequested }) => {
|
||||
const { t } = useTranslation('public');
|
||||
|
||||
// shall we display a loader to prevent double-clicking, while the machine details are loading?
|
||||
@ -60,6 +61,7 @@ const MachineCardComponent: React.FC<MachineCardProps> = ({ user, machine, onSho
|
||||
onLoadingStart={() => setLoading(true)}
|
||||
onLoadingEnd={() => setLoading(false)}
|
||||
onError={onError}
|
||||
onSuccess={onSuccess}
|
||||
onReserveMachine={handleReserveMachine}
|
||||
onLoginRequested={onLoginRequested}
|
||||
onEnrollRequested={onEnrollRequested}
|
||||
@ -79,10 +81,10 @@ const MachineCardComponent: React.FC<MachineCardProps> = ({ user, machine, onSho
|
||||
}
|
||||
|
||||
|
||||
export const MachineCard: React.FC<MachineCardProps> = ({ user, machine, onShowMachine, onReserveMachine, onError, onLoginRequested, onEnrollRequested }) => {
|
||||
export const MachineCard: React.FC<MachineCardProps> = ({ user, machine, onShowMachine, onReserveMachine, onError, onSuccess, onLoginRequested, onEnrollRequested }) => {
|
||||
return (
|
||||
<Loader>
|
||||
<MachineCardComponent user={user} machine={machine} onShowMachine={onShowMachine} onReserveMachine={onReserveMachine} onError={onError} onLoginRequested={onLoginRequested} onEnrollRequested={onEnrollRequested} />
|
||||
<MachineCardComponent user={user} machine={machine} onShowMachine={onShowMachine} onReserveMachine={onReserveMachine} onError={onError} onSuccess={onSuccess} onLoginRequested={onLoginRequested} onEnrollRequested={onEnrollRequested} />
|
||||
</Loader>
|
||||
);
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ declare var Application: IApplication;
|
||||
interface MachinesListProps {
|
||||
user?: User,
|
||||
onError: (message: string) => void,
|
||||
onSuccess: (message: string) => void,
|
||||
onShowMachine: (machine: Machine) => void,
|
||||
onReserveMachine: (machine: Machine) => void,
|
||||
onLoginRequested: () => Promise<User>,
|
||||
@ -22,7 +23,7 @@ interface MachinesListProps {
|
||||
/**
|
||||
* This component shows a list of all machines and allows filtering on that list.
|
||||
*/
|
||||
const MachinesList: React.FC<MachinesListProps> = ({ onError, onShowMachine, onReserveMachine, onLoginRequested, onEnrollRequested, user }) => {
|
||||
const MachinesList: React.FC<MachinesListProps> = ({ onError, onSuccess, onShowMachine, onReserveMachine, onLoginRequested, onEnrollRequested, user }) => {
|
||||
// shown machines
|
||||
const [machines, setMachines] = useState<Array<Machine>>(null);
|
||||
// we keep the full list of machines, for filtering
|
||||
@ -65,6 +66,7 @@ const MachinesList: React.FC<MachinesListProps> = ({ onError, onShowMachine, onR
|
||||
onShowMachine={onShowMachine}
|
||||
onReserveMachine={onReserveMachine}
|
||||
onError={onError}
|
||||
onSuccess={onSuccess}
|
||||
onLoginRequested={onLoginRequested}
|
||||
onEnrollRequested={onEnrollRequested} />
|
||||
})}
|
||||
@ -74,12 +76,12 @@ const MachinesList: React.FC<MachinesListProps> = ({ onError, onShowMachine, onR
|
||||
}
|
||||
|
||||
|
||||
const MachinesListWrapper: React.FC<MachinesListProps> = ({ user, onError, onShowMachine, onReserveMachine, onLoginRequested, onEnrollRequested }) => {
|
||||
const MachinesListWrapper: React.FC<MachinesListProps> = ({ user, onError, onSuccess, onShowMachine, onReserveMachine, onLoginRequested, onEnrollRequested }) => {
|
||||
return (
|
||||
<Loader>
|
||||
<MachinesList user={user} onError={onError} onShowMachine={onShowMachine} onReserveMachine={onReserveMachine} onLoginRequested={onLoginRequested} onEnrollRequested={onEnrollRequested} />
|
||||
<MachinesList user={user} onError={onError} onSuccess={onSuccess} onShowMachine={onShowMachine} onReserveMachine={onReserveMachine} onLoginRequested={onLoginRequested} onEnrollRequested={onEnrollRequested} />
|
||||
</Loader>
|
||||
);
|
||||
}
|
||||
|
||||
Application.Components.component('machinesList', react2angular(MachinesListWrapper, ['user', 'onError', 'onShowMachine', 'onReserveMachine', 'onLoginRequested', 'onEnrollRequested']));
|
||||
Application.Components.component('machinesList', react2angular(MachinesListWrapper, ['user', 'onError', 'onSuccess', 'onShowMachine', 'onReserveMachine', 'onLoginRequested', 'onEnrollRequested']));
|
||||
|
@ -9,6 +9,8 @@ import { IFablab } from '../../models/fablab';
|
||||
import { FabButton } from '../base/fab-button';
|
||||
import PriceAPI from '../../api/price';
|
||||
import { Price } from '../../models/price';
|
||||
import { PaymentMethod, ShoppingCart } from '../../models/payment';
|
||||
import { PaymentModal } from '../payment/payment-modal';
|
||||
|
||||
declare var Fablab: IFablab;
|
||||
|
||||
@ -17,18 +19,22 @@ interface ProposePacksModalProps {
|
||||
toggleModal: () => void,
|
||||
machine: Machine,
|
||||
customer: User,
|
||||
operator: User,
|
||||
onError: (message: string) => void,
|
||||
onDecline: (machine: Machine) => void,
|
||||
onSuccess: (message:string, machine: Machine) => void,
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal dialog shown to offer prepaid-packs for purchase, to the current user.
|
||||
*/
|
||||
export const ProposePacksModal: React.FC<ProposePacksModalProps> = ({ isOpen, toggleModal, machine, customer, onError, onDecline }) => {
|
||||
export const ProposePacksModal: React.FC<ProposePacksModalProps> = ({ isOpen, toggleModal, machine, customer, operator, onError, onDecline, onSuccess }) => {
|
||||
const { t } = useTranslation('logged');
|
||||
|
||||
const [price, setPrice] = useState<Price>(null);
|
||||
const [packs, setPacks] = useState<Array<PrepaidPack>>(null);
|
||||
const [cart, setCart] = useState<ShoppingCart>(null);
|
||||
const [paymentModal, setPaymentModal] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
PrepaidPackAPI.index({ priceable_id: machine.id, priceable_type: 'Machine', group_id: customer.group_id, disabled: false })
|
||||
@ -40,6 +46,13 @@ export const ProposePacksModal: React.FC<ProposePacksModalProps> = ({ isOpen, to
|
||||
}, [machine]);
|
||||
|
||||
|
||||
/**
|
||||
* Open/closes the payment modal
|
||||
*/
|
||||
const togglePaymentModal = (): void => {
|
||||
setPaymentModal(!paymentModal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the formatted localized amount for the given price (e.g. 20.5 => "20,50 €")
|
||||
*/
|
||||
@ -78,14 +91,28 @@ export const ProposePacksModal: React.FC<ProposePacksModalProps> = ({ isOpen, to
|
||||
}
|
||||
|
||||
/**
|
||||
* The user has accepted to buy the provided pack, process with teh payment
|
||||
* The user has accepted to buy the provided pack, process with the payment
|
||||
*/
|
||||
const handleBuyPack = (pack: PrepaidPack) => {
|
||||
return (event: BaseSyntheticEvent): void => {
|
||||
console.log(pack);
|
||||
return (): void => {
|
||||
setCart({
|
||||
customer_id: customer.id,
|
||||
payment_method: PaymentMethod.Card,
|
||||
items: [
|
||||
{ prepaid_pack: { id: pack.id }}
|
||||
]
|
||||
});
|
||||
togglePaymentModal();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback triggered when the user has bought the pack with a successful payment
|
||||
*/
|
||||
const handlePackBought = (): void => {
|
||||
onSuccess(t('app.logged.propose_packs_modal.pack_bought_success'), machine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the given prepaid-pack
|
||||
*/
|
||||
@ -118,6 +145,13 @@ export const ProposePacksModal: React.FC<ProposePacksModalProps> = ({ isOpen, to
|
||||
<div className="list-of-packs">
|
||||
{packs?.map(p => renderPack(p))}
|
||||
</div>
|
||||
{cart && <PaymentModal isOpen={paymentModal}
|
||||
toggleModal={togglePaymentModal}
|
||||
afterSuccess={handlePackBought}
|
||||
onError={onError}
|
||||
cart={cart}
|
||||
currentUser={operator}
|
||||
customer={customer} />}
|
||||
</FabModal>
|
||||
);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ interface ReserveButtonProps {
|
||||
onLoadingStart?: () => void,
|
||||
onLoadingEnd?: () => void,
|
||||
onError: (message: string) => void,
|
||||
onSuccess: (message: string) => void,
|
||||
onReserveMachine: (machine: Machine) => void,
|
||||
onLoginRequested: () => Promise<User>,
|
||||
onEnrollRequested: (trainingId: number) => void,
|
||||
@ -27,7 +28,7 @@ interface ReserveButtonProps {
|
||||
/**
|
||||
* Button component that makes the training verification before redirecting the user to the reservation calendar
|
||||
*/
|
||||
const ReserveButtonComponent: React.FC<ReserveButtonProps> = ({ currentUser, machineId, onLoginRequested, onLoadingStart, onLoadingEnd, onError, onReserveMachine, onEnrollRequested, className, children }) => {
|
||||
const ReserveButtonComponent: React.FC<ReserveButtonProps> = ({ currentUser, machineId, onLoginRequested, onLoadingStart, onLoadingEnd, onError, onSuccess, onReserveMachine, onEnrollRequested, className, children }) => {
|
||||
const { t } = useTranslation('shared');
|
||||
|
||||
const [machine, setMachine] = useState<Machine>(null);
|
||||
@ -87,6 +88,15 @@ const ReserveButtonComponent: React.FC<ReserveButtonProps> = ({ currentUser, mac
|
||||
setProposePacks(!proposePacks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback triggered when the user has successfully bought a pre-paid pack.
|
||||
* Display the success message and redirect him to the booking page.
|
||||
*/
|
||||
const handlePackBought = (message: string, machine: Machine): void => {
|
||||
onSuccess(message);
|
||||
onReserveMachine(machine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the current user has passed the required training before allowing him to book
|
||||
*/
|
||||
@ -154,20 +164,22 @@ const ReserveButtonComponent: React.FC<ReserveButtonProps> = ({ currentUser, mac
|
||||
machine={machine}
|
||||
onError={onError}
|
||||
customer={currentUser}
|
||||
onDecline={onReserveMachine} />}
|
||||
onDecline={onReserveMachine}
|
||||
operator={currentUser}
|
||||
onSuccess={handlePackBought} />}
|
||||
</span>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
export const ReserveButton: React.FC<ReserveButtonProps> = ({ currentUser, machineId, onLoginRequested, onLoadingStart, onLoadingEnd, onError, onReserveMachine, onEnrollRequested, className, children }) => {
|
||||
export const ReserveButton: React.FC<ReserveButtonProps> = ({ currentUser, machineId, onLoginRequested, onLoadingStart, onLoadingEnd, onError, onSuccess, onReserveMachine, onEnrollRequested, className, children }) => {
|
||||
return (
|
||||
<Loader>
|
||||
<ReserveButtonComponent currentUser={currentUser} machineId={machineId} onError={onError} onLoadingStart={onLoadingStart} onLoadingEnd={onLoadingEnd} onReserveMachine={onReserveMachine} onLoginRequested={onLoginRequested} onEnrollRequested={onEnrollRequested} className={className}>
|
||||
<ReserveButtonComponent currentUser={currentUser} machineId={machineId} onError={onError} onSuccess={onSuccess} onLoadingStart={onLoadingStart} onLoadingEnd={onLoadingEnd} onReserveMachine={onReserveMachine} onLoginRequested={onLoginRequested} onEnrollRequested={onEnrollRequested} className={className}>
|
||||
{children}
|
||||
</ReserveButtonComponent>
|
||||
</Loader>
|
||||
);
|
||||
}
|
||||
|
||||
Application.Components.component('reserveButton', react2angular(ReserveButton, ['currentUser', 'machineId', 'onLoadingStart', 'onLoadingEnd', 'onError', 'onReserveMachine', 'onLoginRequested', 'onEnrollRequested', 'className']));
|
||||
Application.Components.component('reserveButton', react2angular(ReserveButton, ['currentUser', 'machineId', 'onLoadingStart', 'onLoadingEnd', 'onError', 'onSuccess', 'onReserveMachine', 'onLoginRequested', 'onEnrollRequested', 'className']));
|
||||
|
@ -39,7 +39,7 @@ interface AbstractPaymentModalProps {
|
||||
afterSuccess: (result: Invoice|PaymentSchedule) => void,
|
||||
cart: ShoppingCart,
|
||||
currentUser: User,
|
||||
schedule: PaymentSchedule,
|
||||
schedule?: PaymentSchedule,
|
||||
customer: User,
|
||||
logoFooter: ReactNode,
|
||||
GatewayForm: FunctionComponent<GatewayFormProps>,
|
||||
|
@ -21,7 +21,7 @@ interface PaymentModalProps {
|
||||
onError: (message: string) => void,
|
||||
cart: ShoppingCart,
|
||||
currentUser: User,
|
||||
schedule: PaymentSchedule,
|
||||
schedule?: PaymentSchedule,
|
||||
customer: User
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ interface PaymentModalProps {
|
||||
* This component open a modal dialog for the configured payment gateway, allowing the user to input his card data
|
||||
* to process an online payment.
|
||||
*/
|
||||
const PaymentModal: React.FC<PaymentModalProps> = ({ isOpen, toggleModal, afterSuccess, onError, currentUser, schedule , cart, customer }) => {
|
||||
const PaymentModalComponent: React.FC<PaymentModalProps> = ({ isOpen, toggleModal, afterSuccess, onError, currentUser, schedule , cart, customer }) => {
|
||||
const { t } = useTranslation('shared');
|
||||
|
||||
const [gateway, setGateway] = useState<Setting>(null);
|
||||
@ -88,12 +88,12 @@ const PaymentModal: React.FC<PaymentModalProps> = ({ isOpen, toggleModal, afterS
|
||||
}
|
||||
|
||||
|
||||
const PaymentModalWrapper: React.FC<PaymentModalProps> = ({ isOpen, toggleModal, afterSuccess, onError, currentUser, schedule , cart, customer }) => {
|
||||
export const PaymentModal: React.FC<PaymentModalProps> = ({ isOpen, toggleModal, afterSuccess, onError, currentUser, schedule , cart, customer }) => {
|
||||
return (
|
||||
<Loader>
|
||||
<PaymentModal isOpen={isOpen} toggleModal={toggleModal} afterSuccess={afterSuccess} onError={onError} currentUser={currentUser} schedule={schedule} cart={cart} customer={customer} />
|
||||
<PaymentModalComponent isOpen={isOpen} toggleModal={toggleModal} afterSuccess={afterSuccess} onError={onError} currentUser={currentUser} schedule={schedule} cart={cart} customer={customer} />
|
||||
</Loader>
|
||||
);
|
||||
}
|
||||
|
||||
Application.Components.component('paymentModal', react2angular(PaymentModalWrapper, ['isOpen', 'toggleModal', 'afterSuccess', 'onError', 'currentUser', 'schedule', 'cart', 'customer']));
|
||||
Application.Components.component('paymentModal', react2angular(PaymentModal, ['isOpen', 'toggleModal', 'afterSuccess', 'onError', 'currentUser', 'schedule', 'cart', 'customer']));
|
||||
|
@ -17,7 +17,7 @@ import { Invoice } from '../../../models/invoice';
|
||||
// we use these two additional parameters to update the card, if provided
|
||||
interface PayzenFormProps extends GatewayFormProps {
|
||||
updateCard?: boolean,
|
||||
paymentScheduleId: number,
|
||||
paymentScheduleId?: number,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -17,7 +17,7 @@ interface PayZenModalProps {
|
||||
afterSuccess: (result: Invoice|PaymentSchedule) => void,
|
||||
cart: ShoppingCart,
|
||||
currentUser: User,
|
||||
schedule: PaymentSchedule,
|
||||
schedule?: PaymentSchedule,
|
||||
customer: User
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ interface StripeModalProps {
|
||||
afterSuccess: (result: Invoice|PaymentSchedule) => void,
|
||||
cart: ShoppingCart,
|
||||
currentUser: User,
|
||||
schedule: PaymentSchedule,
|
||||
schedule?: PaymentSchedule,
|
||||
customer: User
|
||||
}
|
||||
|
||||
|
@ -114,6 +114,13 @@ Application.Controllers.controller('MachinesController', ['$scope', '$state', '_
|
||||
growl.error(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a success message forwarded from a child react components
|
||||
*/
|
||||
$scope.onSuccess = function (message) {
|
||||
growl.success(message)
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the modal dialog to log the user and resolves the returned promise when the logging process
|
||||
* was successfully completed.
|
||||
@ -311,6 +318,14 @@ Application.Controllers.controller('ShowMachineController', ['$scope', '$state',
|
||||
growl.error(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a success message forwarded from a child react components
|
||||
*/
|
||||
$scope.onSuccess = function (message) {
|
||||
growl.success(message)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Open the modal dialog to log the user and resolves the returned promise when the logging process
|
||||
* was successfully completed.
|
||||
|
@ -19,7 +19,7 @@ export enum PaymentMethod {
|
||||
Other = ''
|
||||
}
|
||||
|
||||
export type CartItem = { reservation: Reservation }|{ subscription: SubscriptionRequest }|{ card_update: { date: Date } };
|
||||
export type CartItem = { reservation: Reservation }|{ subscription: SubscriptionRequest }|{ prepaid_pack: { id: number } };
|
||||
|
||||
export interface ShoppingCart {
|
||||
customer_id: number,
|
||||
|
@ -43,6 +43,7 @@
|
||||
|
||||
<machines-list user="currentUser"
|
||||
on-error="onError"
|
||||
on-success="onSuccess"
|
||||
on-show-machine="showMachine"
|
||||
on-reserve-machine="reserveMachine"
|
||||
on-login-requested="onLoginRequest"
|
||||
|
@ -20,6 +20,7 @@
|
||||
current-user="currentUser"
|
||||
machine-id="machine.id"
|
||||
on-error="onError"
|
||||
on-success="onSuccess"
|
||||
on-reserve-machine="reserveMachine"
|
||||
on-login-requested="onLoginRequest"
|
||||
on-enroll-requested="onEnrollRequest">
|
||||
|
36
app/models/cart_item/prepaid_pack.rb
Normal file
36
app/models/cart_item/prepaid_pack.rb
Normal file
@ -0,0 +1,36 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# A prepaid-pack added to the shopping cart
|
||||
class CartItem::PrepaidPack < CartItem::BaseItem
|
||||
def initialize(pack, customer)
|
||||
raise TypeError unless pack.is_a? PrepaidPack
|
||||
|
||||
@pack = pack
|
||||
@customer = customer
|
||||
super
|
||||
end
|
||||
|
||||
def pack
|
||||
raise InvalidGroupError if @pack.group_id != @customer.group_id
|
||||
|
||||
@pack
|
||||
end
|
||||
|
||||
def price
|
||||
amount = pack.amount
|
||||
elements = { pack: amount }
|
||||
|
||||
{ elements: elements, amount: amount }
|
||||
end
|
||||
|
||||
def name
|
||||
"#{@pack.minutes / 60} h"
|
||||
end
|
||||
|
||||
def to_object
|
||||
::StatisticProfilePrepaidPack.new(
|
||||
prepaid_pack_id: @pack.id,
|
||||
statistic_profile_id: StatisticProfile.find_by(user: @customer).id
|
||||
)
|
||||
end
|
||||
end
|
@ -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(&:subscription)&.expiration_date).render
|
||||
pdf = ::PDF::Invoice.new(self, invoice_items.find_by(object_type: Subscription.name)&.expiration_date).render
|
||||
File.binwrite(file, pdf)
|
||||
end
|
||||
|
||||
|
@ -12,6 +12,7 @@ class InvoiceItem < Footprintable
|
||||
belongs_to :subscription, foreign_type: 'Subscription', foreign_key: 'object_id'
|
||||
belongs_to :wallet_transaction, foreign_type: 'WalletTransaction', foreign_key: 'object_id'
|
||||
belongs_to :offer_day, foreign_type: 'OfferDay', foreign_key: 'object_id'
|
||||
belongs_to :statistic_profile_prepaid_pack, foreign_type: 'StatisticProfilePrepaidPack', foreign_key: 'object_id'
|
||||
|
||||
after_create :chain_record
|
||||
after_update :log_changes
|
||||
|
@ -7,6 +7,7 @@ class PaymentScheduleObject < Footprintable
|
||||
belongs_to :subscription, foreign_type: 'Subscription', foreign_key: 'object_id'
|
||||
belongs_to :wallet_transaction, foreign_type: 'WalletTransaction', foreign_key: 'object_id'
|
||||
belongs_to :offer_day, foreign_type: 'OfferDay', foreign_key: 'object_id'
|
||||
belongs_to :statistic_profile_prepaid_pack, foreign_type: 'StatisticProfilePrepaidPack', foreign_key: 'object_id'
|
||||
belongs_to :payment_schedule
|
||||
|
||||
after_create :chain_record
|
||||
|
@ -5,4 +5,15 @@
|
||||
class StatisticProfilePrepaidPack < ApplicationRecord
|
||||
belongs_to :prepaid_pack
|
||||
belongs_to :statistic_profile
|
||||
|
||||
has_many :invoice_items, as: :object, dependent: :destroy
|
||||
has_one :payment_schedule_object, as: :object, dependent: :destroy
|
||||
|
||||
before_create :set_expiration_date
|
||||
|
||||
private
|
||||
|
||||
def set_expiration_date
|
||||
self.expires_at = DateTime.current + prepaid_pack.validity
|
||||
end
|
||||
end
|
||||
|
@ -110,6 +110,8 @@ class PDF::Invoice < Prawn::Document
|
||||
object = offer_day_verbose(invoice.main_item.object, name)
|
||||
when 'Error'
|
||||
object = I18n.t('invoices.error_invoice')
|
||||
when 'StatisticProfilePrepaidPack'
|
||||
object = I18n.t('invoices.prepaid_pack')
|
||||
else
|
||||
puts "ERROR : specified main_item.object_type type (#{invoice.main_item.object_type}) is unknown"
|
||||
end
|
||||
@ -132,7 +134,7 @@ class PDF::Invoice < Prawn::Document
|
||||
|
||||
details = invoice.is_a?(Avoir) ? I18n.t('invoices.cancellation') + ' - ' : ''
|
||||
|
||||
if item.subscription ### Subscription
|
||||
if item.object_type == Subscription.name
|
||||
subscription = item.subscription
|
||||
if invoice.main_item.object_type == 'OfferDay'
|
||||
details += I18n.t('invoices.subscription_extended_for_free_from_START_to_END',
|
||||
@ -154,7 +156,7 @@ class PDF::Invoice < Prawn::Document
|
||||
end
|
||||
|
||||
|
||||
else ### Reservation
|
||||
elsif item.object_type == Reservation.name
|
||||
case invoice.main_item.object.try(:reservable_type)
|
||||
### Machine reservation
|
||||
when 'Machine'
|
||||
@ -179,6 +181,8 @@ class PDF::Invoice < Prawn::Document
|
||||
else
|
||||
details += item.description
|
||||
end
|
||||
else
|
||||
details += item.description
|
||||
end
|
||||
|
||||
data += [[details, number_to_currency(price)]]
|
||||
|
@ -20,6 +20,8 @@ class CartService
|
||||
items.push(CartItem::Subscription.new(plan_info[:plan], @customer)) if plan_info[:new_subscription]
|
||||
elsif ['reservation', :reservation].include?(item.keys.first)
|
||||
items.push(reservable_from_hash(item[:reservation], plan_info))
|
||||
elsif ['prepaid_pack', :prepaid_pack].include?(item.keys.first)
|
||||
items.push(CartItem::PrepaidPack.new(PrepaidPack.find(item[:prepaid_pack][:id]), @customer))
|
||||
end
|
||||
end
|
||||
|
||||
@ -45,10 +47,12 @@ class CartService
|
||||
|
||||
items = []
|
||||
payment_schedule.payment_schedule_objects.each do |object|
|
||||
if object.subscription
|
||||
if object.object_type == Subscription.name
|
||||
items.push(CartItem::Subscription.new(object.subscription.plan, @customer))
|
||||
elsif object.reservation
|
||||
elsif object.object_type == Reservation.name
|
||||
items.push(reservable_from_payment_schedule_object(object, plan))
|
||||
elsif object.object_type == PrepaidPack.name
|
||||
items.push(CartItem::PrepaidPack.new(object.statistic_profile_prepaid_pack.prepaid_pack_id, @customer))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -64,7 +64,7 @@ class InvoicesService
|
||||
# Create an Invoice with an associated array of InvoiceItem matching the given parameters
|
||||
# @param payment_details {Hash} as generated by ShoppingCart.total
|
||||
# @param operator_profile_id {Number} ID of the user that operates the invoice generation (may be an admin, a manager or the customer himself)
|
||||
# @param objects {Array<Reservation|Subscription>} the booking reservation and/or subscription
|
||||
# @param objects {Array<Reservation|Subscription|StatisticProfilePrepaidPack>} the booked reservation and/or subscription or pack
|
||||
# @param user {User} the customer
|
||||
# @param payment_id {String} ID of the payment, a returned by the gateway, if the current invoice is paid by card
|
||||
# @param payment_method {String} the payment method used
|
||||
@ -96,7 +96,7 @@ class InvoicesService
|
||||
# Generate an array of {InvoiceItem} with the elements in provided reservation, price included.
|
||||
# @param invoice {Invoice} the parent invoice
|
||||
# @param payment_details {Hash} as generated by ShoppingCart.total
|
||||
# @param objects {Array<Reservation|Subscription>}
|
||||
# @param objects {Array<Reservation|Subscription|StatisticProfilePrepaidPack>}
|
||||
##
|
||||
def self.generate_invoice_items(invoice, payment_details, objects)
|
||||
objects.each_with_index do |object, index|
|
||||
@ -106,6 +106,8 @@ class InvoicesService
|
||||
InvoicesService.generate_subscription_item(invoice, object, payment_details, index.zero?)
|
||||
elsif object.is_a?(Reservation)
|
||||
InvoicesService.generate_reservation_item(invoice, object, payment_details, index.zero?)
|
||||
elsif object.is_a?(StatisticProfilePrepaidPack)
|
||||
InvoicesService.generate_prepaid_pack_item(invoice, object, payment_details, index.zero?)
|
||||
else
|
||||
InvoicesService.generate_generic_item(invoice, object, payment_details, index.zero?)
|
||||
end
|
||||
@ -179,6 +181,21 @@ class InvoicesService
|
||||
)
|
||||
end
|
||||
|
||||
##
|
||||
# Generate an InvoiceItem for the given StatisticProfilePrepaidPack and save it in invoice.invoice_items.
|
||||
# This method must be called only with a valid pack-statistic_profile relation
|
||||
##
|
||||
def self.generate_prepaid_pack_item(invoice, pack, payment_details, main = false)
|
||||
raise TypeError unless pack
|
||||
|
||||
invoice.invoice_items.push InvoiceItem.new(
|
||||
amount: payment_details[:elements][:pack],
|
||||
description: I18n.t('invoices.pack_item', COUNT: pack.prepaid_pack.minutes / 60),
|
||||
object: pack,
|
||||
main: main
|
||||
)
|
||||
end
|
||||
|
||||
def self.generate_generic_item(invoice, item, payment_details, main = false)
|
||||
invoice.invoice_items.push InvoiceItem.new(
|
||||
amount: payment_details[:elements][item.class.name.to_sym],
|
||||
|
@ -184,6 +184,7 @@ en:
|
||||
no_thanks: "No, thanks"
|
||||
pack_DURATION: "{DURATION} hours"
|
||||
buy_this_pack: "Buy this pack"
|
||||
pack_bought_success: "You have successfully bought this pack of prepaid-hours. Your invoice will ba available soon from your dashboard."
|
||||
validity: "Usable for {COUNT} {PERIODS}"
|
||||
period:
|
||||
day: "{COUNT, plural, one{day} other{days}}"
|
||||
|
@ -113,6 +113,8 @@ en:
|
||||
and: 'and'
|
||||
invoice_text_example: "Our association is not subject to VAT"
|
||||
error_invoice: "Erroneous invoice. The items below ware not booked. Please contact the FabLab for a refund."
|
||||
prepaid_pack: "Prepaid pack of hours"
|
||||
pack_item: "Pack of %{COUNT} hours"
|
||||
#PDF payment schedule generation
|
||||
payment_schedules:
|
||||
schedule_reference: "Payment schedule reference: %{REF}"
|
||||
|
Loading…
x
Reference in New Issue
Block a user