mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2024-11-28 09:24:24 +01:00
(feat) admin can confirm the payment of pre-registration reservation
This commit is contained in:
parent
172f09661f
commit
a60e587187
@ -4,7 +4,7 @@
|
||||
# Reservations are used for Training, Machine, Space and Event
|
||||
class API::ReservationsController < API::APIController
|
||||
before_action :authenticate_user!
|
||||
before_action :set_reservation, only: %i[show update]
|
||||
before_action :set_reservation, only: %i[show update confirm_payment]
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
@ -34,6 +34,16 @@ class API::ReservationsController < API::APIController
|
||||
end
|
||||
end
|
||||
|
||||
def confirm_payment
|
||||
authorize @reservation
|
||||
invoice = ReservationConfirmPaymentService.new(@reservation, current_user, params[:coupon_code], params[:offered]).call
|
||||
if invoice
|
||||
render :show, status: :ok, location: @reservation
|
||||
else
|
||||
render json: @reservation.errors, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_reservation
|
||||
|
@ -503,7 +503,7 @@ Application.Controllers.controller('ShowEventReservationsController', ['$scope',
|
||||
};
|
||||
|
||||
$scope.payReservation = function (reservation) {
|
||||
$uibModal.open({
|
||||
const modalInstance = $uibModal.open({
|
||||
templateUrl: '/admin/events/pay_reservation_modal.html',
|
||||
size: 'sm',
|
||||
resolve: {
|
||||
@ -520,13 +520,13 @@ Application.Controllers.controller('ShowEventReservationsController', ['$scope',
|
||||
return mkCartItems(reservation);
|
||||
}
|
||||
},
|
||||
controller: ['$scope', '$uibModalInstance', 'reservation', 'price', 'wallet', 'cartItems', 'helpers', '$filter', '_t',
|
||||
function ($scope, $uibModalInstance, reservation, price, wallet, cartItems, helpers, $filter, _t) {
|
||||
controller: ['$scope', '$uibModalInstance', 'reservation', 'price', 'wallet', 'cartItems', 'helpers', '$filter', '_t', 'Reservation',
|
||||
function ($scope, $uibModalInstance, reservation, price, wallet, cartItems, helpers, $filter, _t, Reservation) {
|
||||
// User's wallet amount
|
||||
$scope.wallet = wallet;
|
||||
|
||||
// Price
|
||||
$scope.price = price.price;
|
||||
$scope.price = price;
|
||||
|
||||
// Cart items
|
||||
$scope.cartItems = cartItems;
|
||||
@ -539,18 +539,77 @@ Application.Controllers.controller('ShowEventReservationsController', ['$scope',
|
||||
|
||||
$scope.coupon = { applied: null };
|
||||
|
||||
$scope.offered = false;
|
||||
|
||||
$scope.payment = false;
|
||||
|
||||
// Button label
|
||||
if ($scope.amount > 0) {
|
||||
$scope.validButtonName = _t('app.admin.event_reservations.confirm_payment_of_html', { ROLE: $scope.currentUser.role, AMOUNT: $filter('currency')($scope.amount) });
|
||||
} else {
|
||||
if ((price.price > 0) && ($scope.walletAmount === 0)) {
|
||||
$scope.validButtonName = _t('app.admin.event_reservations.confirm_payment_of_html', { ROLE: $scope.currentUser.role, AMOUNT: $filter('currency')(price.price) });
|
||||
$scope.setValidButtonName = function () {
|
||||
if ($scope.amount > 0 && !$scope.offered) {
|
||||
$scope.validButtonName = _t('app.admin.event_reservations.confirm_payment_of_html', { ROLE: $scope.currentUser.role, AMOUNT: $filter('currency')($scope.amount) });
|
||||
} else {
|
||||
$scope.validButtonName = _t('app.shared.buttons.confirm');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute the total amount for the current reservation according to the previously set parameters
|
||||
*/
|
||||
$scope.computeEventAmount = function () {
|
||||
Price.compute(mkCartItems(reservation, $scope.coupon.applied), function (res) {
|
||||
$scope.price = res;
|
||||
$scope.amount = helpers.getAmountToPay($scope.price.price, wallet.amount);
|
||||
$scope.setValidButtonName();
|
||||
});
|
||||
};
|
||||
|
||||
// Callback to validate the payment
|
||||
$scope.ok = function () {
|
||||
$scope.attempting = true;
|
||||
return Reservation.confirm_payment({
|
||||
id: reservation.id,
|
||||
coupon_code: $scope.coupon.applied ? $scope.coupon.applied.code : null,
|
||||
offered: $scope.offered
|
||||
}, function (res) {
|
||||
$uibModalInstance.close(res);
|
||||
return $scope.attempting = true;
|
||||
}
|
||||
, function (response) {
|
||||
$scope.alerts = [];
|
||||
angular.forEach(response, function (v, k) {
|
||||
angular.forEach(v, function (err) {
|
||||
$scope.alerts.push({
|
||||
msg: k + ': ' + err,
|
||||
type: 'danger'
|
||||
});
|
||||
});
|
||||
});
|
||||
return $scope.attempting = false;
|
||||
});
|
||||
};
|
||||
|
||||
// Callback to cancel the payment
|
||||
$scope.cancel = function () { $uibModalInstance.dismiss('cancel'); };
|
||||
|
||||
$scope.$watch('coupon.applied', function (newValue, oldValue) {
|
||||
if ((newValue !== null) || (oldValue !== null)) {
|
||||
return $scope.computeEventAmount();
|
||||
}
|
||||
});
|
||||
|
||||
$scope.setValidButtonName();
|
||||
}]
|
||||
});
|
||||
modalInstance.result.then(function (reservation) {
|
||||
$scope.reservations = $scope.reservations.map((r) => {
|
||||
if (r.id === reservation.id) {
|
||||
return reservation;
|
||||
}
|
||||
return r;
|
||||
});
|
||||
}, function () {
|
||||
$log.info('Pay reservation modal dismissed at: ' + new Date());
|
||||
});
|
||||
};
|
||||
}]);
|
||||
|
||||
|
@ -5,6 +5,11 @@ Application.Services.factory('Reservation', ['$resource', function ($resource) {
|
||||
{ id: '@id' }, {
|
||||
update: {
|
||||
method: 'PUT'
|
||||
},
|
||||
confirm_payment: {
|
||||
method: 'POST',
|
||||
url: '/api/reservations/confirm_payment',
|
||||
isArray: false
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -4,15 +4,42 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<uib-alert ng-repeat="alert in alerts" type="{{alert.type}}" close="closeAlert($index)">{{alert.msg}}</uib-alert>
|
||||
<div class="row">
|
||||
<div class="row" ng-show="!offered">
|
||||
<wallet-info current-user="currentUser"
|
||||
cart="cartItems"
|
||||
price="price"
|
||||
price="price.price"
|
||||
wallet="wallet"/>
|
||||
</div>
|
||||
<div class="row m-b">
|
||||
<div class="col-md-12">
|
||||
<label for="offerSlot" class="control-label m-r" translate>{{ 'app.admin.event_reservations.offer_this_reservation' }}</label>
|
||||
<input bs-switch
|
||||
ng-model="offered"
|
||||
id="offerSlot"
|
||||
type="checkbox"
|
||||
class="form-control"
|
||||
switch-on-text="{{ 'app.shared.buttons.yes' | translate }}"
|
||||
switch-off-text="{{ 'app.shared.buttons.no' | translate }}"
|
||||
switch-animate="true"
|
||||
ng-change="computeEventAmount()"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<coupon show="true" coupon="coupon.applied" total="price.price_without_coupon" user-id="{{reservation.user_id}}"></coupon>
|
||||
|
||||
<div class="row">
|
||||
<div class="form-group col-sm-12">
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox"
|
||||
name="paymentReceived"
|
||||
id="paymentReceived"
|
||||
ng-model="payment" />
|
||||
<label for="paymentReceived" translate>{{ 'app.admin.event_reservations.i_have_received_the_payment' }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-info" ng-click="ok()" ng-disabled="attempting" ng-bind-html="validButtonName"></button>
|
||||
<button class="btn btn-info" ng-click="ok()" ng-disabled="attempting || !payment" ng-bind-html="validButtonName"></button>
|
||||
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
|
||||
</div>
|
||||
|
@ -46,16 +46,16 @@
|
||||
<span ng-repeat="ticket in reservation.tickets_attributes">{{ticket.event_price_category.price_category.name}} : {{ticket.booked}}</span>
|
||||
</td>
|
||||
<td ng-if="event.pre_registration">
|
||||
<span ng-if="!isValidated(reservation) && !isCancelled(reservation)" class="v-middle badge text-base bg-info" translate="">{{ 'app.admin.event_reservations.event_status.pre_registered' }}</span>
|
||||
<span ng-if="isValidated(reservation) && !isCancelled(reservation)" class="v-middle badge text-base bg-stage" translate="">{{ 'app.admin.event_reservations.event_status.to_pay' }}</span>
|
||||
<span ng-if="!isValidated(reservation) && !isCancelled(reservation) && !reservation.is_paid" class="v-middle badge text-base bg-info" translate="">{{ 'app.admin.event_reservations.event_status.pre_registered' }}</span>
|
||||
<span ng-if="isValidated(reservation) && !isCancelled(reservation) && !reservation.is_paid" class="v-middle badge text-base bg-stage" translate="">{{ 'app.admin.event_reservations.event_status.to_pay' }}</span>
|
||||
<span ng-if="reservation.is_paid && !isCancelled(reservation)" class="v-middle badge text-base bg-success" translate="">{{ 'app.admin.event_reservations.event_status.paid' }}</span>
|
||||
<span ng-if="isCancelled(reservation)" class="v-middle badge text-base bg-event" translate="">{{ 'app.admin.event_reservations.event_status.canceled' }}</span>
|
||||
</td>
|
||||
<td ng-if="event.pre_registration">
|
||||
<button class="btn btn-default" ng-click="validateReservation(reservation)" ng-if="!isValidated(reservation) && !isCancelled(reservation)" translate>
|
||||
<button class="btn btn-default" ng-click="validateReservation(reservation)" ng-if="!isValidated(reservation) && !isCancelled(reservation) && !reservation.is_paid" translate>
|
||||
{{ 'app.admin.event_reservations.validate' }}
|
||||
</button>
|
||||
<button class="btn btn-default" ng-click="payReservation(reservation)" ng-if="isValidated(reservation) && !isCancelled(reservation)" translate>
|
||||
<button class="btn btn-default" ng-click="payReservation(reservation)" ng-if="isValidated(reservation) && !isCancelled(reservation) && !reservation.is_paid" translate>
|
||||
{{ 'app.admin.event_reservations.pay' }}
|
||||
</button>
|
||||
</td>
|
||||
|
@ -9,4 +9,8 @@ class ReservationPolicy < ApplicationPolicy
|
||||
def update?
|
||||
user.admin? || user.manager? || record.user == user
|
||||
end
|
||||
|
||||
def confirm_payment?
|
||||
user.admin? || user.manager?
|
||||
end
|
||||
end
|
||||
|
68
app/services/reservation_confirm_payment_service.rb
Normal file
68
app/services/reservation_confirm_payment_service.rb
Normal file
@ -0,0 +1,68 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# confirm payment of a pre-registration reservation
|
||||
class ReservationConfirmPaymentService
|
||||
def initialize(reservation, operator, coupon, offered)
|
||||
@reservation = reservation
|
||||
@operator = operator
|
||||
@offered = offered
|
||||
@coupon = CartItem::Coupon.new(
|
||||
customer_profile: @reservation.user.invoicing_profile,
|
||||
operator_profile: @operator.invoicing_profile,
|
||||
coupon: Coupon.find_by(code: coupon)
|
||||
)
|
||||
end
|
||||
|
||||
def total
|
||||
slots_reservations = @reservation.slots_reservations.map do |sr|
|
||||
{
|
||||
slot_id: sr.slot_id,
|
||||
offered: @offered
|
||||
}
|
||||
end
|
||||
event_reservation = CartItem::EventReservation.new(customer_profile: @reservation.user.invoicing_profile,
|
||||
operator_profile: @operator.invoicing_profile,
|
||||
event: @reservation.reservable,
|
||||
cart_item_reservation_slots_attributes: slots_reservations,
|
||||
normal_tickets: @reservation.nb_reserve_places,
|
||||
cart_item_event_reservation_tickets_attributes: @reservation.tickets.to_a,
|
||||
cart_item_event_reservation_booking_users_attributes: @reservation.booking_users.to_a)
|
||||
|
||||
all_elements = {
|
||||
slots: @reservation.slots_reservations.map do |sr|
|
||||
{ start_at: sr.slot.start_at, end_at: sr.slot.end_at, price: event_reservation.price[:amount] }
|
||||
end
|
||||
}
|
||||
|
||||
total_amount = event_reservation.price[:amount]
|
||||
|
||||
coupon_info = @coupon.price(total_amount)
|
||||
|
||||
# return result
|
||||
{
|
||||
elements: all_elements,
|
||||
total: coupon_info[:total_with_coupon].to_i,
|
||||
before_coupon: coupon_info[:total_without_coupon].to_i,
|
||||
coupon: @coupon.coupon
|
||||
}
|
||||
end
|
||||
|
||||
def call
|
||||
price = total
|
||||
invoice = InvoicesService.create(
|
||||
price,
|
||||
@operator.invoicing_profile.id,
|
||||
[@reservation],
|
||||
@reservation.user
|
||||
)
|
||||
return invoice if Setting.get('prevent_invoices_zero') && price[:total].zero?
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
WalletService.debit_user_wallet(invoice, @reservation.user)
|
||||
|
||||
invoice.save
|
||||
invoice.post_save
|
||||
end
|
||||
invoice
|
||||
end
|
||||
end
|
@ -659,6 +659,8 @@ en:
|
||||
validation_failed: "Validation failed."
|
||||
confirm_payment: "Confirm payment"
|
||||
confirm_payment_of_html: "{ROLE, select, admin{Cash} other{Pay}}: {AMOUNT}" #(contexte : validate a payment of $20,00)
|
||||
offer_this_reservation: "I offer this reservation"
|
||||
i_have_received_the_payment: "I have received the payment"
|
||||
events_settings:
|
||||
title: "Settings"
|
||||
generic_text_block: "Editorial text block"
|
||||
|
@ -659,6 +659,8 @@ fr:
|
||||
validation_failed: "La validation a échoué."
|
||||
confirm_payment: "Confirmer le paiement"
|
||||
confirm_payment_of_html: "{ROLE, select, admin{Encaisser} other{Payer}} : {AMOUNT}" #(contexte : validate a payment of $20,00)
|
||||
offer_this_reservation: "J'offre cette réservation"
|
||||
i_have_received_the_payment: "J'ai reçu le paiement"
|
||||
events_settings:
|
||||
title: "Paramètres"
|
||||
generic_text_block: "Bloc de texte rédactionnel"
|
||||
|
@ -68,7 +68,9 @@ Rails.application.routes.draw do
|
||||
patch ':id/update_role', action: 'update_role', on: :collection
|
||||
patch ':id/validate', action: 'validate', on: :collection
|
||||
end
|
||||
resources :reservations, only: %i[show index update]
|
||||
resources :reservations, only: %i[show index update] do
|
||||
post :confirm_payment, on: :collection
|
||||
end
|
||||
resources :notifications, only: %i[index show update] do
|
||||
match :update_all, path: '/', via: %i[put patch], on: :collection
|
||||
get 'polling', action: 'polling', on: :collection
|
||||
|
Loading…
Reference in New Issue
Block a user