1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-18 07:52:23 +01:00

WIP: migration to object[]

TODO: fix running tests, fix front-end
This commit is contained in:
Sylvain 2021-05-28 17:34:20 +02:00
parent bf3dcd874b
commit d942d46632
32 changed files with 243 additions and 283 deletions

View File

@ -1,7 +1,7 @@
Metrics/LineLength:
Max: 140
Metrics/MethodLength:
Max: 35
Max: 36
Metrics/CyclomaticComplexity:
Max: 13
Metrics/PerceivedComplexity:

View File

@ -8,13 +8,7 @@ class API::LocalPaymentController < API::PaymentsController
authorize LocalPaymentContext.new(cart, price[:amount])
if cart.reservation
res = on_reservation_success(nil, nil, cart)
elsif cart.subscription
res = on_subscription_success(nil, nil, cart)
end
render res
render on_payment_success(nil, nil, cart)
end
protected

View File

@ -12,11 +12,7 @@ class API::PaymentsController < API::ApiController
protected
def post_save(_gateway_item_id, _gateway_item_type); end
def post_reservation_save(_gateway_item_id, _gateway_item_type); end
def post_subscription_save(_gateway_item_id, _gateway_item_type); end
def post_save(_gateway_item_id, _gateway_item_type, _payment_document); end
def get_wallet_debit(user, total_amount)
wallet_amount = (user.wallet.amount * 100).to_i
@ -37,44 +33,13 @@ class API::PaymentsController < API::ApiController
cs.from_hash(params[:cart_items])
end
# @param cart {ShoppingCart}
def check_coupon(cart)
return if cart.coupon.nil?
cart.coupon.coupon
end
# @param cart {ShoppingCart}
def check_plan(cart)
return unless cart.subscription
plan = cart.subscription.plan
raise InvalidGroupError if plan.group_id != current_user.group_id
end
def on_success(gateway_item_id, gateway_item_type, cart)
cart.pay_and_save(gateway_item_id, gateway_item_type)
end
def on_reservation_success(gateway_item_id, gateway_item_type, cart)
is_reserve = on_success(gateway_item_id, gateway_item_type, cart)
post_reservation_save(gateway_item_id, gateway_item_type)
if is_reserve
{ template: 'api/reservations/show', status: :created, location: @reservation }
def on_payment_success(gateway_item_id, gateway_item_type, cart)
res = cart.build_and_save(gateway_item_id, gateway_item_type)
if res[:success]
post_save(gateway_item_id, gateway_item_type, res[:payment])
res[:payment].render_resource.merge(status: :created)
else
{ json: @reservation.errors, status: :unprocessable_entity }
end
end
def on_subscription_success(gateway_item_id, gateway_item_type, cart)
is_subscribe = on_success(gateway_item_id, gateway_item_type, cart)
post_subscription_save(gateway_item_id, gateway_item_type)
if is_subscribe
{ template: 'api/subscriptions/show', status: :created, location: @subscription }
else
{ json: @subscription.errors, status: :unprocessable_entity }
{ json: res[:errors], status: :unprocessable_entity }
end
end
end

View File

@ -50,25 +50,17 @@ class API::PayzenController < API::PaymentsController
cart = shopping_cart
if order['answer']['transactions'].first['status'] == 'PAID'
if cart.reservation
res = on_reservation_success(params[:order_id], cart)
elsif cart.subscription
res = on_subscription_success(params[:order_id], cart)
end
render on_payment_success(params[:order_id], cart)
else
render json: order['answer'], status: :unprocessable_entity
end
render res
rescue StandardError => e
render json: e, status: :unprocessable_entity
end
private
def on_reservation_success(order_id, cart)
super(order_id, 'PayZen::Order', cart)
end
def on_subscription_success(order_id, cart)
def on_payment_success(order_id, cart)
super(order_id, 'PayZen::Order', cart)
end

View File

@ -20,14 +20,11 @@ class API::StripeController < API::PaymentsController
begin
amount = debit_amount(cart) # will contains the amount and the details of each invoice lines
if params[:payment_method_id].present?
check_coupon(cart)
check_plan(cart)
# Create the PaymentIntent
intent = Stripe::PaymentIntent.create(
{
payment_method: params[:payment_method_id],
amount: Stripe::Service.stripe_amount(amount[:amount]),
amount: Stripe::Service.new.stripe_amount(amount[:amount]),
currency: Setting.get('stripe_currency'),
confirmation_method: 'manual',
confirm: true,
@ -46,13 +43,7 @@ class API::StripeController < API::PaymentsController
res = { json: { plan_id: 'this plan is not compatible with your current group' }, status: :unprocessable_entity }
end
if intent&.status == 'succeeded'
if cart.reservation
res = on_reservation_success(intent, cart)
elsif cart.subscription
res = on_subscription_success(intent, cart)
end
end
res = on_payment_success(intent, cart) if intent&.status == 'succeeded'
render generate_payment_response(intent, res)
end
@ -82,14 +73,9 @@ class API::StripeController < API::PaymentsController
cart = shopping_cart
if intent&.status == 'succeeded'
if cart.reservation
res = on_reservation_success(intent, cart)
elsif cart.subscription
res = on_subscription_success(intent, cart)
end
res = on_payment_success(intent, cart)
render generate_payment_response(intent, res)
end
render generate_payment_response(intent, res)
rescue Stripe::InvalidRequestError => e
render json: e, status: :unprocessable_entity
end
@ -107,31 +93,17 @@ class API::StripeController < API::PaymentsController
private
def post_reservation_save(intent_id, intent_type)
def post_save(intent_id, intent_type, payment_document)
return unless intent_type == 'Stripe::PaymentIntent'
Stripe::PaymentIntent.update(
intent_id,
{ description: "Invoice reference: #{@reservation.invoice.reference}" },
{ description: "#{payment_document.class.name} reference: #{payment_document.reference}" },
{ api_key: Setting.get('stripe_secret_key') }
)
end
def post_subscription_save(intent_id, intent_type)
return unless intent_type == 'Stripe::PaymentIntent'
Stripe::PaymentIntent.update(
intent_id,
{ description: "Invoice reference: #{@subscription.invoices.first.reference}" },
{ api_key: Setting.get('stripe_secret_key') }
)
end
def on_reservation_success(intent, cart)
super(intent.id, intent.class.name, cart)
end
def on_subscription_success(intent, cart)
def on_payment_success(intent, cart)
super(intent.id, intent.class.name, cart)
end

View File

@ -613,12 +613,12 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
const updateCartPrice = function () {
if (Object.keys($scope.user).length > 0) {
const items = [];
if ($scope.selectedPlan) {
items.push(mkSubscription($scope.selectedPlan.id));
}
if ($scope.events.reserved && $scope.events.reserved.length > 0) {
items.push(mkReservation($scope.events.reserved));
}
if ($scope.selectedPlan) {
items.push(mkSubscription($scope.selectedPlan.id));
}
return Price.compute(mkCartItems(items), function (res) {
$scope.amountTotal = res.price;
@ -923,12 +923,12 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
*/
const paySlots = function () {
const items = [];
if ($scope.selectedPlan) {
items.push(mkSubscription($scope.selectedPlan.id));
}
if ($scope.events.reserved && $scope.events.reserved.length > 0) {
items.push(mkReservation($scope.events.reserved));
}
if ($scope.selectedPlan) {
items.push(mkSubscription($scope.selectedPlan.id));
}
return Wallet.getWalletByUser({ user_id: $scope.user.id }, function (wallet) {
const amountToPay = helpers.getAmountToPay($scope.amountTotal, wallet.amount);

View File

@ -23,6 +23,7 @@ export type CartItem = { reservation: Reservation }|{ subscription: Subscription
export interface ShoppingCart {
customer_id: number,
// WARNING: items ordering matters! The first item in the array will be considered as the main item
items: Array<CartItem>,
coupon_code?: string,
payment_schedule?: boolean,

View File

@ -2,8 +2,6 @@
# A subscription added to the shopping cart
class CartItem::Subscription < CartItem::BaseItem
attr_reader :plan
def initialize(plan, customer)
raise TypeError unless plan.is_a? Plan
@ -11,8 +9,14 @@ class CartItem::Subscription < CartItem::BaseItem
@customer = customer
end
def plan
raise InvalidGroupError if @plan.group_id != @customer.group_id
@plan
end
def price
amount = @plan.amount
amount = plan.amount
elements = { plan: amount }
{ elements: elements, amount: amount }

View File

@ -165,6 +165,10 @@ class Invoice < PaymentDocument
!payment_gateway_object.nil? && payment_method == 'card'
end
def render_resource
{ partial: 'api/invoices/invoice', locals: { invoice: self } }
end
private
def generate_and_send_invoice
@ -186,5 +190,4 @@ class Invoice < PaymentDocument
puts changes
puts '---------------------------------'
end
end

View File

@ -3,7 +3,6 @@
# A single line inside an invoice. Can be a subscription or a reservation
class InvoiceItem < Footprintable
belongs_to :invoice
belongs_to :subscription
has_one :invoice_item # associates invoice_items of an invoice to invoice_items of an Avoir
has_one :payment_gateway_object, as: :item

View File

@ -6,4 +6,13 @@ class OfferDay < ApplicationRecord
has_many :invoice_items, as: :object, dependent: :destroy
belongs_to :subscription
# buying invoice
def original_invoice
invoice_items.select(:invoice_id)
.group(:invoice_id)
.map(&:invoice_id)
.map { |id| Invoice.find_by(id: id, type: nil) }
.first
end
end

View File

@ -23,4 +23,6 @@ class PaymentDocument < Footprintable
end
def post_save(arg); end
def render_resource; end
end

View File

@ -77,6 +77,10 @@ class PaymentSchedule < PaymentDocument
PaymentGatewayService.new.create_subscription(self, gateway_method_id)
end
def render_resource
{ partial: 'api/payment_schedules/payment_schedule', locals: { payment_schedule: self } }
end
private
def generate_and_send_document

View File

@ -67,6 +67,18 @@ class Reservation < ApplicationRecord
reservable.save!
end
def original_payment_schedule
payment_schedule_object&.payment_schedule
end
def original_invoice
invoice_items.select(:invoice_id)
.group(:invoice_id)
.map(&:invoice_id)
.map { |id| Invoice.find_by(id: id, type: nil) }
.first
end
private
def machine_not_already_reserved

View File

@ -20,14 +20,6 @@ class ShoppingCart
@payment_schedule = payment_schedule
end
def subscription
@items.find { |item| item.is_a? CartItem::Subscription }
end
def reservation
@items.find { |item| item.is_a? CartItem::Reservation }
end
# compute the price details of the current shopping cart
def total
total_amount = 0
@ -53,26 +45,26 @@ class ShoppingCart
}
end
def pay_and_save(payment_id, payment_type)
def build_and_save(payment_id, payment_type)
price = total
objects = []
payment = nil
ActiveRecord::Base.transaction do
items.each do |item|
object = item.to_object
object.save
objects.push(object)
raise ActiveRecord::Rollback unless object.errors.count.zero?
raise ActiveRecord::Rollback unless object.errors.empty?
end
payment = if price[:schedule]
PaymentScheduleService.new.create(
subscription&.to_object,
objects,
price[:before_coupon],
coupon: @coupon,
coupon: @coupon.coupon,
operator: @operator,
payment_method: @payment_method,
user: @customer,
reservation: reservation&.to_object,
payment_id: payment_id,
payment_type: payment_type
)
@ -80,7 +72,8 @@ class ShoppingCart
InvoicesService.create(
price,
@operator.invoicing_profile.id,
reservation: reservation&.to_object,
objects,
@customer,
payment_id: payment_id,
payment_type: payment_type,
payment_method: @payment_method
@ -90,6 +83,6 @@ class ShoppingCart
payment.post_save(payment_id)
end
objects.map(&:errors).flatten.count.zero?
{ success: objects.map(&:errors).flatten.map(&:empty?).all?, payment: payment, errors: objects.map(&:errors).flatten }
end
end

View File

@ -58,7 +58,7 @@ class Subscription < ApplicationRecord
operator_profile_id: operator_profile_id,
total: 0
)
invoice.invoice_items.push InvoiceItem.new(amount: 0, description: plan.name, subscription_id: id, object: od)
invoice.invoice_items.push InvoiceItem.new(amount: 0, description: plan.name, object: od)
invoice.save
if save
@ -73,11 +73,16 @@ class Subscription < ApplicationRecord
end
def original_payment_schedule
# if the payment schedule was associated with this subscription, return it directly
return payment_schedule if payment_schedule
payment_schedule_object&.payment_schedule
end
# if it was associated with a reservation, query payment schedule from one of its items
PaymentScheduleItem.where("cast(details->>'subscription_id' AS int) = ?", id).first&.payment_schedule
# buying invoice
def original_invoice
invoice_items.select(:invoice_id)
.group(:invoice_id)
.map(&:invoice_id)
.map { |id| Invoice.find_by(id: id, type: nil) }
.first
end
private

View File

@ -19,4 +19,8 @@ class WalletTransaction < ApplicationRecord
def user
invoicing_profile.user
end
def original_invoice
invoice_item.invoice
end
end

View File

@ -97,9 +97,9 @@ class PDF::Invoice < Prawn::Document
DATE: I18n.l(invoice.main_item.object.slots[0].start_at.to_date),
TIME: I18n.l(invoice.main_item.object.slots[0].start_at, format: :hour_minute))
invoice.invoice_items.each do |item|
next unless item.subscription_id
next unless item.subscription
subscription = Subscription.find item.subscription_id
subscription = item.subscription
cancellation = invoice.is_a?(Avoir) ? I18n.t('invoices.cancellation') + ' - ' : ''
object = "\n- #{object}\n- #{cancellation + subscription_verbose(subscription, name)}"
break
@ -132,8 +132,8 @@ class PDF::Invoice < Prawn::Document
details = invoice.is_a?(Avoir) ? I18n.t('invoices.cancellation') + ' - ' : ''
if item.subscription_id ### Subscription
subscription = Subscription.find item.subscription_id
if item.subscription ### Subscription
subscription = item.subscription
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),

View File

@ -64,13 +64,12 @@ 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 reservation {Reservation} the booking reservation, if any
# @param subscription {Subscription} the booking subscription, if any
# @param objects {Array<Reservation|Subscription>} the booking reservation and/or subscription
# @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
##
def self.create(payment_details, operator_profile_id, reservation: nil, subscription: nil, payment_id: nil, payment_type: nil, payment_method: nil)
user = reservation&.user || subscription&.user
def self.create(payment_details, operator_profile_id, objects, user, payment_id: nil, payment_type: nil, payment_method: nil)
operator = InvoicingProfile.find(operator_profile_id)&.user
method = if payment_method
payment_method
@ -88,7 +87,7 @@ class InvoicesService
invoice.payment_gateway_object = PaymentGatewayObject.new(gateway_object_id: payment_id, gateway_object_type: payment_type)
end
InvoicesService.generate_invoice_items(invoice, payment_details, reservation: reservation, subscription: subscription)
InvoicesService.generate_invoice_items(invoice, payment_details, objects)
InvoicesService.set_total_and_coupon(invoice, user, payment_details[:coupon])
invoice
end
@ -97,30 +96,27 @@ 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>}
##
def self.generate_invoice_items(invoice, payment_details, reservation: nil, subscription: nil)
if reservation
case reservation.reservable
# === Event reservation ===
when Event
InvoicesService.generate_event_item(invoice, reservation, payment_details)
# === Space|Machine|Training reservation ===
def self.generate_invoice_items(invoice, payment_details, objects)
objects.each_with_index do |object, index|
if object.is_a?(Reservation) && object.reservable.is_a?(Event)
InvoicesService.generate_event_item(invoice, object, payment_details, index.zero?)
elsif object.is_a?(Subscription)
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?)
else
InvoicesService.generate_generic_item(invoice, reservation, payment_details)
InvoicesService.generate_generic_item(invoice, object, payment_details, index.zero?)
end
end
return unless subscription || reservation&.plan_id
subscription = reservation.generate_subscription if !subscription && reservation.plan_id
InvoicesService.generate_subscription_item(invoice, subscription, payment_details, reservation.nil?)
end
##
# Generate an InvoiceItem for each slot in the given reservation and save them in invoice.invoice_items.
# This method must be called if reservation.reservable is an Event
##
def self.generate_event_item(invoice, reservation, payment_details)
def self.generate_event_item(invoice, reservation, payment_details, main = false)
raise TypeError unless reservation.reservable.is_a? Event
reservation.slots.each do |slot|
@ -142,7 +138,7 @@ class InvoicesService
amount: price_slot[:price],
description: description,
object: reservation,
main: true
main: main
)
end
end
@ -151,7 +147,7 @@ class InvoicesService
# Generate an InvoiceItem for each slot in the given reservation and save them in invoice.invoice_items.
# This method must be called if reservation.reservable is a Space, a Machine or a Training
##
def self.generate_generic_item(invoice, reservation, payment_details)
def self.generate_reservation_item(invoice, reservation, payment_details, main = false)
raise TypeError unless [Space, Machine, Training].include? reservation.reservable.class
reservation.slots.each do |slot|
@ -163,7 +159,7 @@ class InvoicesService
amount: price_slot[:price],
description: description,
object: reservation,
main: true
main: main
)
end
end
@ -172,18 +168,26 @@ class InvoicesService
# Generate an InvoiceItem for the given subscription and save it in invoice.invoice_items.
# This method must be called only with a valid subscription
##
def self.generate_subscription_item(invoice, subscription, payment_details, main = true)
def self.generate_subscription_item(invoice, subscription, payment_details, main = false)
raise TypeError unless subscription
invoice.invoice_items.push InvoiceItem.new(
amount: payment_details[:elements][:plan],
description: subscription.plan.name,
subscription_id: subscription.id,
object: subscription,
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],
description: item.class.name,
object: item,
main: main
)
end
##
# Set the total price to the reservation's invoice, summing its whole items.

View File

@ -8,7 +8,7 @@ class PaymentScheduleService
# @param total {Number} Total amount of the current shopping cart (which includes this plan) - without coupon
# @param coupon {Coupon} apply this coupon, if any
##
def compute(plan, total, coupon: nil, subscription: nil)
def compute(plan, total, coupon: nil)
other_items = total - plan.amount
# base monthly price of the plan
price = plan.amount
@ -23,7 +23,7 @@ class PaymentScheduleService
items = []
(0..deadlines - 1).each do |i|
date = DateTime.current + i.months
details = { recurring: per_month, subscription_id: subscription&.id }
details = { recurring: per_month }
amount = if i.zero?
details[:adjustment] = adjustment.truncate
details[:other_items] = other_items.truncate
@ -49,21 +49,15 @@ class PaymentScheduleService
{ payment_schedule: ps, items: items }
end
def create(subscription, total, coupon: nil, operator: nil, payment_method: nil, reservation: nil, user: nil,
def create(objects, total, coupon: nil, operator: nil, payment_method: nil, user: nil,
payment_id: nil, payment_type: nil)
subscription = reservation.generate_subscription if !subscription && reservation&.plan_id
raise InvalidSubscriptionError unless subscription&.persisted?
subscription = objects.find { |item| item.class == Subscription }
schedule = compute(subscription.plan, total, coupon: coupon, subscription: subscription)
schedule = compute(subscription.plan, total, coupon: coupon)
ps = schedule[:payment_schedule]
items = schedule[:items]
if reservation
ps.payment_schedule_objects.push(PaymentScheduleObject.new(object: reservation, main: true))
ps.payment_schedule_objects.push(PaymentScheduleObject.new(object: subscription, main: false)) if subscription
else
ps.payment_schedule_objects.push(PaymentScheduleObject.new(object: subscription, main: true))
end
ps.payment_schedule_objects = build_objects(objects)
ps.payment_method = payment_method
if !payment_id.nil? && !payment_type.nil?
pgo = PaymentGatewayObject.new(
@ -77,12 +71,18 @@ class PaymentScheduleService
ps.invoicing_profile = user.invoicing_profile
ps.statistic_profile = user.statistic_profile
ps.payment_schedule_items = items
items.each do |item|
item.payment_schedule = ps
end
ps
end
def build_objects(objects)
res = []
res.push(PaymentScheduleObject.new(object: objects[0], main: true))
objects[1..-1].each do |object|
res.push(PaymentScheduleObject.new(object: object))
end
res
end
##
# Generate the invoice associated with the given PaymentScheduleItem, with the children elements (InvoiceItems).
# @param payment_method {String} the payment method or gateway in use

View File

@ -37,7 +37,7 @@ class Subscriptions::Subscribe
operator = InvoicingProfile.find(operator_profile_id)&.user
PaymentScheduleService.new.create(
new_sub,
[new_sub],
details[:before_coupon],
operator: operator,
payment_method: schedule.payment_method,
@ -49,7 +49,8 @@ class Subscriptions::Subscribe
InvoicesService.create(
details,
operator_profile_id,
subscription: new_sub
[new_sub],
new_sub.user
)
end
payment.save

View File

@ -31,7 +31,7 @@ class UsersExportService
# export reservations
def export_reservations(export)
@reservations = Reservation.all.includes(:slots, :reservable, :invoice, statistic_profile: [user: [:profile]])
@reservations = Reservation.all.includes(:slots, :reservable, statistic_profile: [user: [:profile]])
ActionController::Base.prepend_view_path './app/views/'
# place data in view_assigns

View File

@ -69,6 +69,7 @@ class WalletService
ii.description = I18n.t('invoices.wallet_credit')
ii.object = wallet_transaction
ii.invoice = avoir
ii.main = true
ii.save!
end

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
json.extract! invoice, :id, :created_at, :reference, :avoir_date, :description
json.user_id invoice.invoicing_profile&.user_id
json.total invoice.total / 100.00
json.name invoice.user.profile.full_name
json.has_avoir invoice.refunded?
json.is_avoir invoice.is_a?(Avoir)
json.is_subscription_invoice invoice.subscription_invoice?
json.stripe invoice.paid_by_card?
json.date invoice.is_a?(Avoir) ? invoice.avoir_date : invoice.created_at
json.chained_footprint invoice.check_footprint
json.main_object do
json.type invoice.main_item.object_type
json.id invoice.main_item.object_id
end
json.items invoice.invoice_items do |item|
json.id item.id
json.amount item.amount / 100.0
json.description item.description
json.avoir_item_id item.invoice_item.id if item.invoice_item
end

View File

@ -1,21 +1,3 @@
# frozen_string_literal: true
json.extract! @invoice, :id, :created_at, :reference, :avoir_date, :description
json.user_id @invoice.invoicing_profile&.user_id
json.total @invoice.total / 100.00
json.name @invoice.user.profile.full_name
json.has_avoir @invoice.refunded?
json.is_avoir @invoice.is_a?(Avoir)
json.is_subscription_invoice @invoice.subscription_invoice?
json.stripe @invoice.paid_by_card?
json.date @invoice.is_a?(Avoir) ? @invoice.avoir_date : @invoice.created_at
json.chained_footprint @invoice.check_footprint
json.main_object do
json.type @invoice.main_item.object_type
end
json.items @invoice.invoice_items do |item|
json.id item.id
json.amount item.amount / 100.0
json.description item.description
json.avoir_item_id item.invoice_item.id if item.invoice_item
end
json.partial! 'api/invoices/invoice', invoice: @invoice

View File

@ -13,6 +13,10 @@ if payment_schedule.operator_profile
json.extract! payment_schedule.operator_profile, :first_name, :last_name
end
end
json.main_object do
json.type payment_schedule.main_object.object_type
json.id payment_schedule.main_object.object_id
end
json.items payment_schedule.payment_schedule_items do |item|
json.extract! item, :id, :due_date, :state, :invoice_id, :payment_method
json.amount item.amount / 100.00

View File

@ -1,7 +1,9 @@
# frozen_string_literal: true
wb = xlsx_package.workbook
header = wb.styles.add_style b: true, :bg_color => Stylesheet.primary.upcase.gsub('#', 'FF'), :fg_color => 'FFFFFFFF'
date = wb.styles.add_style :format_code => Rails.application.secrets.excel_date_format
header = wb.styles.add_style b: true, bg_color: Stylesheet.primary.upcase.gsub('#', 'FF'), fg_color: 'FFFFFFFF'
date = wb.styles.add_style format_code: Rails.application.secrets.excel_date_format
wb.add_worksheet(name: t('export_reservations.reservations')) do |sheet|
@ -15,17 +17,17 @@ wb.add_worksheet(name: t('export_reservations.reservations')) do |sheet|
# data rows
@reservations.each do |resrv|
data = [
resrv.user&.id,
resrv.user&.profile&.full_name || t('export_reservations.deleted_user'),
resrv.user&.email,
resrv.created_at.to_date,
resrv.reservable_type,
(resrv.reservable.nil? ? '' : resrv.reservable.name),
(resrv.reservable_type == 'Event') ? resrv.total_booked_seats: resrv.slots.count,
(resrv.invoice&.paid_by_card?) ? t('export_reservations.online_payment') : t('export_reservations.local_payment')
resrv.user&.id,
resrv.user&.profile&.full_name || t('export_reservations.deleted_user'),
resrv.user&.email,
resrv.created_at.to_date,
resrv.reservable_type,
resrv.reservable.nil? ? '' : resrv.reservable.name,
resrv.reservable_type == 'Event' ? resrv.total_booked_seats : resrv.slots.count,
resrv.original_invoice&.paid_by_card? ? t('export_reservations.online_payment') : t('export_reservations.local_payment')
]
styles = [nil, nil, nil, date, nil, nil, nil, nil]
types = [:integer, :string, :string, :date, :string, :string, :integer, :string]
types = %i[integer string string date string string integer string]
sheet.add_row data, style: styles, types: types
end

View File

@ -5213,22 +5213,6 @@ CREATE INDEX profiles_lower_unaccent_last_name_trgm_idx ON public.profiles USING
CREATE INDEX projects_search_vector_idx ON public.projects USING gin (search_vector);
--
-- Name: accounting_periods accounting_periods_del_protect; Type: RULE; Schema: public; Owner: -
--
CREATE RULE accounting_periods_del_protect AS
ON DELETE TO public.accounting_periods DO INSTEAD NOTHING;
--
-- Name: accounting_periods accounting_periods_upd_protect; Type: RULE; Schema: public; Owner: -
--
CREATE RULE accounting_periods_upd_protect AS
ON UPDATE TO public.accounting_periods DO INSTEAD NOTHING;
--
-- Name: projects projects_search_content_trigger; Type: TRIGGER; Schema: public; Owner: -
--

View File

@ -17,7 +17,7 @@ class Stripe::Service < Payment::Service
subscription = payment_schedule.payment_schedule_objects.find(&:subscription)
reservable_stp_id = payment_schedule.main_object.object.reservable&.payment_gateway_object&.gateway_object_id
when Subscription.name
subscription = payment_schedule.main_object
subscription = payment_schedule.main_object.object
reservable_stp_id = nil
else
raise InvalidSubscriptionError

View File

@ -194,19 +194,19 @@ module Events
assert_equal Mime[:json], response.content_type
# Check the reservation match the required event
reservation = json_response(response.body)
r = Reservation.find(reservation[:id])
result = json_response(response.body)
i = Invoice.find(result[:id])
assert_equal e.id, r.reservable_id
assert_equal 'Event', r.reservable_type
assert_equal e.id, i.main_item.object_id
assert_equal 'Event', i.main_item.object_type
# Check the remaining places were updated successfully
e = Event.where(id: event[:id]).first
assert_equal 2, e.nb_free_places, 'Number of free places was not updated'
# Check the resulting invoice generation and it has right price
assert_invoice_pdf r.invoice
assert_equal (4 * 20) + (4 * 16), r.invoice.total / 100.0
assert_invoice_pdf i
assert_equal (4 * 20) + (4 * 16), i.total / 100.0
end
end
end

View File

@ -54,11 +54,11 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
# reservation assertions
reservation = Reservation.last
assert reservation.invoice
assert_equal 1, reservation.invoice.invoice_items.count
assert reservation.original_invoice
assert_equal 1, reservation.original_invoice.invoice_items.count
# invoice assertions
invoice = reservation.invoice
invoice = reservation.original_invoice
assert invoice.payment_gateway_object.blank?
refute invoice.total.blank?
@ -115,11 +115,11 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
# reservation assertions
reservation = Reservation.last
assert reservation.invoice
assert_equal 1, reservation.invoice.invoice_items.count
assert reservation.original_invoice
assert_equal 1, reservation.original_invoice.invoice_items.count
# invoice assertions
invoice = reservation.invoice
invoice = reservation.original_invoice
assert invoice.payment_gateway_object.blank?
refute invoice.total.blank?
@ -186,11 +186,11 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
# reservation assertions
reservation = Reservation.last
assert reservation.invoice
assert_equal 2, reservation.invoice.invoice_items.count
assert reservation.original_invoice
assert_equal 2, reservation.original_invoice.invoice_items.count
# invoice assertions
invoice = reservation.invoice
invoice = reservation.original_invoice
assert invoice.payment_gateway_object.blank?
refute invoice.total.blank?
@ -260,11 +260,11 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
# reservation assertions
reservation = Reservation.last
assert reservation.invoice
assert_equal 1, reservation.invoice.invoice_items.count
assert reservation.original_invoice
assert_equal 1, reservation.original_invoice.invoice_items.count
# invoice assertions
invoice = reservation.invoice
invoice = reservation.original_invoice
assert invoice.payment_gateway_object.blank?
refute invoice.total.blank?
@ -307,11 +307,6 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
post '/api/local_payment/confirm_payment', params: {
customer_id: @vlonchamp.id,
items: [
{
subscription: {
plan_id: plan.id,
}
},
{
reservation: {
reservable_id: machine.id,
@ -324,6 +319,11 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
}
]
}
},
{
subscription: {
plan_id: plan.id
}
}
]
}.to_json, headers: default_headers
@ -344,11 +344,11 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
# reservation assertions
reservation = Reservation.last
assert reservation.invoice
assert_equal 2, reservation.invoice.invoice_items.count
assert reservation.original_invoice
assert_equal 2, reservation.original_invoice.invoice_items.count
# invoice assertions
invoice = reservation.invoice
invoice = reservation.original_invoice
assert invoice.payment_gateway_object.blank?
refute invoice.total.blank?
@ -415,7 +415,7 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
# reservation assertions
reservation = Reservation.last
assert_not_nil reservation.invoice
assert_not_nil reservation.original_invoice
# notification
assert_not_empty Notification.where(attached_object: reservation)
@ -450,7 +450,7 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
},
{
subscription: {
plan_id: plan.id,
plan_id: plan.id
}
}
]
@ -475,8 +475,8 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
# reservation assertions
reservation = Reservation.find(result[:id])
assert reservation.invoice
assert_equal 2, reservation.invoice.invoice_items.count
assert reservation.original_invoice
assert_equal 2, reservation.original_invoice.invoice_items.count
# credits assertions
assert_equal 1, @user_without_subscription.credits.count
@ -484,7 +484,7 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
assert_equal training.id, @user_without_subscription.credits.last.creditable_id
# invoice assertions
invoice = reservation.invoice
invoice = reservation.original_invoice
assert invoice.payment_gateway_object.blank?
refute invoice.total.blank?
@ -567,16 +567,9 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
assert_not_nil @user_without_subscription.subscription, "user's subscription was not found"
assert_equal plan.id, @user_without_subscription.subscribed_plan.id, "user's plan does not match"
# Check the answer
reservation_res = json_response(response.body)
assert_equal plan.id, reservation_res[:user][:subscribed_plan][:id], 'subscribed plan does not match'
# reservation assertions
assert_equal reservation_res[:id], reservation.id
assert reservation.payment_schedule
assert_equal payment_schedule.main_object.object, reservation
# payment schedule assertions
assert reservation.original_payment_schedule
assert_equal payment_schedule.id, reservation.original_payment_schedule.id
assert_not_nil payment_schedule.reference
assert_equal 'check', payment_schedule.payment_method
assert_empty payment_schedule.payment_gateway_objects
@ -587,5 +580,13 @@ class Reservations::CreateAsAdminTest < ActionDispatch::IntegrationTest
assert payment_schedule.check_footprint
assert_equal @user_without_subscription.invoicing_profile.id, payment_schedule.invoicing_profile_id
assert_equal @admin.invoicing_profile.id, payment_schedule.operator_profile_id
# Check the answer
result = json_response(response.body)
assert_equal reservation.original_payment_schedule.id, result[:id], 'payment schedule id does not match'
# reservation assertions
assert_equal result[:main_object][:id], reservation.id
assert_equal payment_schedule.main_object.object, reservation
end
end

View File

@ -61,8 +61,8 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
# reservation assertions
reservation = Reservation.last
assert reservation.invoice
assert_equal 1, reservation.invoice.invoice_items.count
assert reservation.original_invoice
assert_equal 1, reservation.original_invoice.invoice_items.count
# invoice_items assertions
invoice_item = InvoiceItem.last
@ -183,8 +183,8 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
# reservation assertions
reservation = Reservation.last
assert reservation.invoice
assert_equal 1, reservation.invoice.invoice_items.count
assert reservation.original_invoice
assert_equal 1, reservation.original_invoice.invoice_items.count
# invoice_items
invoice_item = InvoiceItem.last
@ -261,8 +261,8 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
# reservation assertions
reservation = Reservation.last
assert reservation.invoice
assert_equal 2, reservation.invoice.invoice_items.count
assert reservation.original_invoice
assert_equal 2, reservation.original_invoice.invoice_items.count
# invoice_items assertions
invoice_items = InvoiceItem.last(2)
@ -338,8 +338,8 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
# reservation assertions
reservation = Reservation.last
assert reservation.invoice
assert_equal 1, reservation.invoice.invoice_items.count
assert reservation.original_invoice
assert_equal 1, reservation.original_invoice.invoice_items.count
# invoice_items
invoice_item = InvoiceItem.last
@ -418,8 +418,8 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
# reservation assertions
reservation = Reservation.last
assert reservation.invoice
assert_equal 1, reservation.invoice.invoice_items.count
assert reservation.original_invoice
assert_equal 1, reservation.original_invoice.invoice_items.count
# invoice_items assertions
invoice_item = InvoiceItem.last
@ -507,8 +507,8 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
# reservation assertions
reservation = Reservation.last
assert reservation.invoice
assert_equal 2, reservation.invoice.invoice_items.count
assert reservation.original_invoice
assert_equal 2, reservation.original_invoice.invoice_items.count
# invoice assertions
item = InvoiceItem.find_by(object: reservation)
@ -591,8 +591,8 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
# reservation assertions
reservation = Reservation.last
assert reservation.invoice_items.count == 1
assert_equal 2, reservation.invoice.invoice_items.count
assert reservation.original_invoice
assert_equal 2, reservation.original_invoice.invoice_items.count
# invoice assertions
item = InvoiceItem.find_by(object: reservation)
@ -605,17 +605,17 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
# invoice_items assertions
## reservation
reservation_item = invoice.invoice_items.where(subscription_id: nil).first
reservation_item = invoice.invoice_items.find_by(object: reservation)
assert_not_nil reservation_item
assert_equal reservation_item.amount, machine.prices.find_by(group_id: @user_without_subscription.group_id, plan_id: plan.id).amount
assert reservation_item.check_footprint
## subscription
subscription_item = invoice.invoice_items.where.not(subscription_id: nil).first
subscription_item = invoice.invoice_items.find_by(object_type: Subscription.name)
assert_not_nil subscription_item
subscription = Subscription.find(subscription_item.subscription_id)
subscription = subscription_item.subscription
assert_equal subscription_item.amount, plan.amount
assert_equal subscription.plan_id, plan.id
@ -832,11 +832,6 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
coupon_code: 'GIME3EUR',
payment_schedule: true,
items: [
{
subscription: {
plan_id: plan.id
}
},
{
reservation: {
reservable_id: machine.id,
@ -849,6 +844,11 @@ class Reservations::CreateTest < ActionDispatch::IntegrationTest
}
]
}
},
{
subscription: {
plan_id: plan.id
}
}
]
}