mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2024-11-29 10:24:20 +01:00
(wip) pay family and nominative event
This commit is contained in:
parent
07b47d7956
commit
e85e2d7b47
@ -7,7 +7,10 @@ class API::ChildrenController < API::APIController
|
||||
before_action :set_child, only: %i[show update destroy]
|
||||
|
||||
def index
|
||||
@children = policy_scope(Child)
|
||||
authorize Child
|
||||
user_id = current_user.id
|
||||
user_id = params[:user_id] if current_user.privileged? && params[:user_id]
|
||||
@children = Child.where(user_id: user_id)
|
||||
end
|
||||
|
||||
def show
|
||||
|
@ -54,6 +54,7 @@ export const EventForm: React.FC<EventFormProps> = ({ action, event, onError, on
|
||||
const [isOpenRecurrentModal, setIsOpenRecurrentModal] = useState<boolean>(false);
|
||||
const [updatingEvent, setUpdatingEvent] = useState<Event>(null);
|
||||
const [isActiveAccounting, setIsActiveAccounting] = useState<boolean>(false);
|
||||
const [isActiveFamilyAccount, setIsActiveFamilyAccount] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
EventCategoryAPI.index()
|
||||
@ -69,6 +70,7 @@ export const EventForm: React.FC<EventFormProps> = ({ action, event, onError, on
|
||||
.then(data => setPriceCategoriesOptions(data.map(c => decorationToOption(c))))
|
||||
.catch(onError);
|
||||
SettingAPI.get('advanced_accounting').then(res => setIsActiveAccounting(res.value === 'true')).catch(onError);
|
||||
SettingAPI.get('family_account').then(res => setIsActiveFamilyAccount(res.value === 'true')).catch(onError);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@ -172,11 +174,14 @@ export const EventForm: React.FC<EventFormProps> = ({ action, event, onError, on
|
||||
* This method provides event type options
|
||||
*/
|
||||
const buildEventTypeOptions = (): Array<SelectOption<EventType>> => {
|
||||
return [
|
||||
{ label: t('app.admin.event_form.event_types.standard'), value: 'standard' },
|
||||
{ label: t('app.admin.event_form.event_types.nominative'), value: 'nominative' },
|
||||
{ label: t('app.admin.event_form.event_types.family'), value: 'family' }
|
||||
const options = [
|
||||
{ label: t('app.admin.event_form.event_types.standard'), value: 'standard' as EventType },
|
||||
{ label: t('app.admin.event_form.event_types.nominative'), value: 'nominative' as EventType }
|
||||
];
|
||||
if (isActiveFamilyAccount) {
|
||||
options.push({ label: t('app.admin.event_form.event_types.family'), value: 'family' as EventType });
|
||||
}
|
||||
return options;
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -136,8 +136,8 @@ Application.Controllers.controller('EventsController', ['$scope', '$state', 'Eve
|
||||
}
|
||||
]);
|
||||
|
||||
Application.Controllers.controller('ShowEventController', ['$scope', '$state', '$rootScope', 'Event', '$uibModal', 'Member', 'Reservation', 'Price', 'CustomAsset', 'SlotsReservation', 'eventPromise', 'growl', '_t', 'Wallet', 'AuthService', 'helpers', 'dialogs', 'priceCategoriesPromise', 'settingsPromise', 'LocalPayment',
|
||||
function ($scope, $state,$rootScope, Event, $uibModal, Member, Reservation, Price, CustomAsset, SlotsReservation, eventPromise, growl, _t, Wallet, AuthService, helpers, dialogs, priceCategoriesPromise, settingsPromise, LocalPayment) {
|
||||
Application.Controllers.controller('ShowEventController', ['$scope', '$state', '$rootScope', 'Event', '$uibModal', 'Member', 'Reservation', 'Price', 'CustomAsset', 'SlotsReservation', 'eventPromise', 'growl', '_t', 'Wallet', 'AuthService', 'helpers', 'dialogs', 'priceCategoriesPromise', 'settingsPromise', 'LocalPayment', 'Child',
|
||||
function ($scope, $state,$rootScope, Event, $uibModal, Member, Reservation, Price, CustomAsset, SlotsReservation, eventPromise, growl, _t, Wallet, AuthService, helpers, dialogs, priceCategoriesPromise, settingsPromise, LocalPayment, Child) {
|
||||
/* PUBLIC SCOPE */
|
||||
|
||||
// reservations for the currently shown event
|
||||
@ -150,6 +150,9 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
$scope.ctrl =
|
||||
{ member: {} };
|
||||
|
||||
// children for the member
|
||||
$scope.children = [];
|
||||
|
||||
// parameters for a new reservation
|
||||
$scope.reserve = {
|
||||
nbPlaces: {
|
||||
@ -226,22 +229,12 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
});
|
||||
};
|
||||
|
||||
const hasMemberInBookingUsers = function () {
|
||||
const keys = Object.keys($scope.reserve.bookingUsers);
|
||||
for (const key of keys) {
|
||||
if ($scope.reserve.bookingUsers[key].find(u => u.booked_id === $scope.ctrl.member.id && u.booked_type === 'User')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback to call when the number of tickets to book changes in the current booking
|
||||
*/
|
||||
$scope.changeNbPlaces = function (priceType) {
|
||||
// compute the total remaining places
|
||||
let remain = $scope.event.nb_free_places - $scope.reserve.nbReservePlaces;
|
||||
let remain = ($scope.event.event_type === 'family' ? ($scope.children.length + 1) : $scope.event.nb_free_places) - $scope.reserve.nbReservePlaces;
|
||||
for (let ticket in $scope.reserve.tickets) {
|
||||
remain -= $scope.reserve.tickets[ticket];
|
||||
}
|
||||
@ -260,36 +253,41 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
}
|
||||
}
|
||||
|
||||
const nbBookingUsers = $scope.reserve.bookingUsers[priceType].length;
|
||||
const nbReservePlaces = priceType === 'normal' ? $scope.reserve.nbReservePlaces : $scope.reserve.tickets[priceType];
|
||||
if (nbReservePlaces > nbBookingUsers) {
|
||||
_.times(nbReservePlaces - nbBookingUsers, () => {
|
||||
/*
|
||||
if (!hasMemberInBookingUsers()) {
|
||||
$scope.reserve.bookingUsers[priceType].push({ event_price_category_id: priceType === 'normal' ? null : priceType, booked_id: $scope.ctrl.member.id, booked_type: 'User', name: $scope.ctrl.member.name });
|
||||
} else {
|
||||
$scope.reserve.bookingUsers[priceType].push({ event_price_category_id: priceType === 'normal' ? null : priceType });
|
||||
}
|
||||
*/
|
||||
$scope.reserve.bookingUsers[priceType].push({ event_price_category_id: priceType === 'normal' ? null : priceType });
|
||||
});
|
||||
} else {
|
||||
_.times(nbBookingUsers - nbReservePlaces, () => {
|
||||
$scope.reserve.bookingUsers[priceType].pop();
|
||||
});
|
||||
if ($scope.event.event_type === 'nominative' || $scope.event.event_type === 'family') {
|
||||
const nbBookingUsers = $scope.reserve.bookingUsers[priceType].length;
|
||||
const nbReservePlaces = priceType === 'normal' ? $scope.reserve.nbReservePlaces : $scope.reserve.tickets[priceType];
|
||||
if (nbReservePlaces > nbBookingUsers) {
|
||||
_.times(nbReservePlaces - nbBookingUsers, () => {
|
||||
$scope.reserve.bookingUsers[priceType].push({ event_price_category_id: priceType === 'normal' ? null : priceType, bookedUsers: buildBookedUsersOptions() });
|
||||
});
|
||||
} else {
|
||||
_.times(nbBookingUsers - nbReservePlaces, () => {
|
||||
$scope.reserve.bookingUsers[priceType].pop();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// recompute the total price
|
||||
return $scope.computeEventAmount();
|
||||
};
|
||||
|
||||
$scope.changeBookedUser = function () {
|
||||
for (const key of Object.keys($scope.reserve.bookingUsers)) {
|
||||
for (const user of $scope.reserve.bookingUsers[key]) {
|
||||
user.bookedUsers = buildBookedUsersOptions(user.booked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to reset the current reservation parameters
|
||||
* @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
|
||||
*/
|
||||
$scope.cancelReserve = function (e) {
|
||||
e.preventDefault();
|
||||
return resetEventReserve();
|
||||
resetEventReserve();
|
||||
updateNbReservePlaces();
|
||||
return;
|
||||
};
|
||||
|
||||
$scope.isUserValidatedByType = () => {
|
||||
@ -354,6 +352,9 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
Member.get({ id: $scope.ctrl.member.id }, function (member) {
|
||||
$scope.ctrl.member = member;
|
||||
getReservations($scope.event.id, 'Event', $scope.ctrl.member.id);
|
||||
getChildren($scope.ctrl.member.id).then(() => {
|
||||
updateNbReservePlaces();
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -615,6 +616,31 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
growl.error(message);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the reservation of current event is valid
|
||||
*/
|
||||
$scope.reservationIsValid = () => {
|
||||
if ($scope.event.event_type === 'nominative') {
|
||||
for (const key of Object.keys($scope.reserve.bookingUsers)) {
|
||||
for (const user of $scope.reserve.bookingUsers[key]) {
|
||||
if (!_.trim(user.name)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($scope.event.event_type === 'family') {
|
||||
for (const key of Object.keys($scope.reserve.bookingUsers)) {
|
||||
for (const user of $scope.reserve.bookingUsers[key]) {
|
||||
if (!user.booked) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* PRIVATE SCOPE */
|
||||
|
||||
/**
|
||||
@ -634,6 +660,9 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
// get the current user's reservations into $scope.reservations
|
||||
if ($scope.currentUser) {
|
||||
getReservations($scope.event.id, 'Event', $scope.currentUser.id);
|
||||
getChildren($scope.currentUser.id).then(function (children) {
|
||||
updateNbReservePlaces();
|
||||
});
|
||||
}
|
||||
|
||||
// watch when a coupon is applied to re-compute the total price
|
||||
@ -658,6 +687,72 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
}).$promise.then(function (reservations) { $scope.reservations = reservations; });
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the children for the user
|
||||
* @param user_id {number} the user's id (current or managed)
|
||||
*/
|
||||
const getChildren = function (user_id) {
|
||||
return Child.query({
|
||||
user_id
|
||||
}).$promise.then(function (children) {
|
||||
$scope.children = children;
|
||||
return $scope.children;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the number of places reserved by the current user
|
||||
*/
|
||||
const hasBookedUser = function (userKey) {
|
||||
for (const key of Object.keys($scope.reserve.bookingUsers)) {
|
||||
for (const user of $scope.reserve.bookingUsers[key]) {
|
||||
if (user.booked && user.booked.key === userKey) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Build the list of options for the select box of the booked users
|
||||
* @param booked {object} the booked user
|
||||
*/
|
||||
const buildBookedUsersOptions = function (booked) {
|
||||
const options = [];
|
||||
const userKey = `user_${$scope.ctrl.member.id}`;
|
||||
if ((booked && booked.key === userKey) || !hasBookedUser(userKey)) {
|
||||
options.push({ key: userKey, name: $scope.ctrl.member.name, type: 'User', id: $scope.ctrl.member.id });
|
||||
}
|
||||
for (const child of $scope.children) {
|
||||
const key = `child_${child.id}`;
|
||||
if ((booked && booked.key === key) || !hasBookedUser(key)) {
|
||||
options.push({
|
||||
key,
|
||||
name: child.first_name + ' ' + child.last_name,
|
||||
id: child.id,
|
||||
type: 'Child'
|
||||
});
|
||||
}
|
||||
}
|
||||
return options;
|
||||
};
|
||||
|
||||
/**
|
||||
* update number of places available for each price category for the family event
|
||||
*/
|
||||
const updateNbReservePlaces = function () {
|
||||
if ($scope.event.event_type === 'family') {
|
||||
const maxPlaces = $scope.children.length + 1;
|
||||
if ($scope.event.nb_free_places > maxPlaces) {
|
||||
$scope.reserve.nbPlaces.normal = __range__(0, maxPlaces, true);
|
||||
for (let evt_px_cat of Array.from($scope.event.event_price_categories_attributes)) {
|
||||
$scope.reserve.nbPlaces[evt_px_cat.id] = __range__(0, maxPlaces, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a hash map implementing the Reservation specs
|
||||
* @param reserve {Object} Reservation parameters (places...)
|
||||
@ -694,9 +789,9 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
for (const user of $scope.reserve.bookingUsers[key]) {
|
||||
reservation.booking_users_attributes.push({
|
||||
event_price_category_id: user.event_price_category_id,
|
||||
name: user.name,
|
||||
booked_id: user.booked_id,
|
||||
booked_type: user.booked_type
|
||||
name: user.booked ? user.booked.name : _.trim(user.name),
|
||||
booked_id: user.booked ? user.booked.id : undefined,
|
||||
booked_type: user.booked ? user.booked.type : undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -865,6 +960,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
$scope.reservations.push(reservation);
|
||||
});
|
||||
resetEventReserve();
|
||||
updateNbReservePlaces();
|
||||
$scope.reserveSuccess = true;
|
||||
$scope.coupon.applied = null;
|
||||
if ($scope.currentUser.role === 'admin') {
|
||||
|
11
app/frontend/src/javascript/services/child.js
Normal file
11
app/frontend/src/javascript/services/child.js
Normal file
@ -0,0 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
Application.Services.factory('Child', ['$resource', function ($resource) {
|
||||
return $resource('/api/children/:id',
|
||||
{ id: '@id' }, {
|
||||
update: {
|
||||
method: 'PUT'
|
||||
}
|
||||
}
|
||||
);
|
||||
}]);
|
@ -122,7 +122,20 @@
|
||||
<div class="col-sm-12 m-b" ng-if="event.event_type === 'nominative' && reserve.nbReservePlaces > 0">
|
||||
<div ng-repeat="user in reserve.bookingUsers.normal">
|
||||
<label class="" translate>{{ 'app.public.events_show.last_name_and_first_name '}}</label>
|
||||
<input type="text" class="form-control" ng-model="user.name">
|
||||
<input type="text" class="form-control" ng-model="user.name" ng-required="true">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 m-b" ng-if="event.event_type === 'family' && reserve.nbReservePlaces > 0">
|
||||
<div ng-repeat="user in reserve.bookingUsers.normal">
|
||||
<label class="" translate>{{ 'app.public.events_show.last_name_and_first_name '}}</label>
|
||||
<select ng-model="user.booked"
|
||||
ng-options="option.name for option in user.bookedUsers track by option.key"
|
||||
ng-change="changeBookedUser()"
|
||||
name="booked"
|
||||
ng-required="true"
|
||||
class="form-control">
|
||||
<option value=""></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -135,7 +148,20 @@
|
||||
<div class="col-sm-12 m-b" ng-if="event.event_type === 'nominative' && reserve.tickets[price.id] > 0">
|
||||
<div ng-repeat="user in reserve.bookingUsers[price.id]">
|
||||
<label class="" translate>{{ 'app.public.events_show.last_name_and_first_name '}}</label>
|
||||
<input type="text" class="form-control" ng-model="user.name">
|
||||
<input type="text" class="form-control" ng-model="user.name" ng-required="true">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 m-b" ng-if="event.event_type === 'family' && reserve.tickets[price.id] > 0">
|
||||
<div ng-repeat="user in reserve.bookingUsers[price.id]">
|
||||
<label class="" translate>{{ 'app.public.events_show.last_name_and_first_name '}}</label>
|
||||
<select ng-model="user.booked"
|
||||
ng-options="option.name for option in user.bookedUsers track by option.key"
|
||||
ng-change="changeBookedUser()"
|
||||
name="booked"
|
||||
ng-required="true"
|
||||
class="form-control">
|
||||
<option value=""></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -202,11 +228,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel-footer no-padder ng-scope" ng-if="event.amount">
|
||||
<div class="panel-footer no-padder ng-scope" ng-if="event.amount && reservationIsValid()">
|
||||
<button class="btn btn-valid btn-info btn-block p-l btn-lg text-u-c r-b text-base" ng-click="payEvent()" ng-if="reserve.totalSeats > 0">{{ 'app.public.events_show.confirm_and_pay' | translate }} {{reserve.amountTotal | currency}}</button>
|
||||
</div>
|
||||
|
||||
<div class="panel-footer no-padder ng-scope" ng-if="event.amount == 0">
|
||||
<div class="panel-footer no-padder ng-scope" ng-if="event.amount == 0 && reservationIsValid()">
|
||||
<button class="btn btn-valid btn-info btn-block p-l btn-lg text-u-c r-b text-base" ng-click="validReserveEvent()" ng-if="reserve.totalSeats > 0" ng-disabled="attempting">{{ 'app.shared.buttons.confirm' | translate }}</button>
|
||||
</div>
|
||||
|
||||
|
@ -2,13 +2,6 @@
|
||||
|
||||
# Check the access policies for API::ChildrenController
|
||||
class ChildPolicy < ApplicationPolicy
|
||||
# Defines the scope of the children index, depending on the current user
|
||||
class Scope < Scope
|
||||
def resolve
|
||||
scope.where(user_id: user.id)
|
||||
end
|
||||
end
|
||||
|
||||
def index?
|
||||
!user.organization?
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user