1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-22 11:52:21 +01:00

Merge branch 'pre_inscription' into staging

This commit is contained in:
Du Peng 2023-07-31 10:19:39 +02:00
commit 2025409741
15 changed files with 82 additions and 36 deletions

View File

@ -30,7 +30,7 @@ class API::ChildrenController < API::APIController
def update
authorize @child
if @child.update(child_params)
if ChildService.update(@child, child_params)
render status: :ok
else
render json: @child.errors.full_messages, status: :unprocessable_entity

View File

@ -47,15 +47,15 @@ export const EventReservationItem: React.FC<EventReservationItemProps> = ({ rese
const preRegistrationStatus = () => {
if (!_.isBoolean(reservation.is_valid) && !reservation.canceled_at && !reservation.is_paid) {
return t('app.logged.event_reservation_item.in_the_process_of_validation');
} else if (reservation.is_valid && !reservation.canceled_at && !reservation.is_paid && reservation.reservable.amount !== 0) {
} else if (reservation.is_valid && !reservation.canceled_at && !reservation.is_paid && reservation.amount !== 0) {
return t('app.logged.event_reservation_item.settle_your_payment');
} else if (reservation.is_valid && !reservation.canceled_at && !reservation.is_paid && reservation.reservable.amount === 0) {
} else if (reservation.is_valid && !reservation.canceled_at && !reservation.is_paid && reservation.amount === 0) {
return t('app.logged.event_reservation_item.registered');
} else if (!reservation.is_valid && !reservation.canceled_at) {
return t('app.logged.event_reservation_item.not_validated');
} else if (reservation.is_paid && !reservation.canceled_at && reservation.reservable.amount !== 0) {
} else if (reservation.is_paid && !reservation.canceled_at && reservation.amount !== 0) {
return t('app.logged.event_reservation_item.paid');
} else if (reservation.is_paid && !reservation.canceled_at && reservation.reservable.amount === 0) {
} else if (reservation.is_paid && !reservation.canceled_at && reservation.amount === 0) {
return t('app.logged.event_reservation_item.present');
} else if (reservation.canceled_at) {
return t('app.logged.event_reservation_item.canceled');

View File

@ -108,7 +108,7 @@ export const ChildForm: React.FC<ChildFormProps> = ({ child, onSubmit, supportin
label={t('app.public.child_form.email')}
/>
{!isPrivileged() && <>
{!isPrivileged() && supportingDocumentsTypes?.length > 0 && <>
<h3 className="missing-file">{t('app.public.child_form.supporting_documents')}</h3>
{output.supporting_document_files_attributes.map((sf, index) => {
return (
@ -116,7 +116,7 @@ export const ChildForm: React.FC<ChildFormProps> = ({ child, onSubmit, supportin
defaultFile={sf as FileType}
id={`supporting_document_files_attributes.${index}`}
accept="application/pdf"
rules={{ required: true }}
rules={{ required: !sf.attachment }}
setValue={setValue}
label={getSupportingDocumentsTypeName(sf.supporting_document_type_id)}
showRemoveButton={false}
@ -132,7 +132,7 @@ export const ChildForm: React.FC<ChildFormProps> = ({ child, onSubmit, supportin
</FabButton>
</div>
{isPrivileged() && <>
{isPrivileged() && supportingDocumentsTypes?.length > 0 && <>
<h3 className="missing-file">{t('app.public.child_form.supporting_documents')}</h3>
<div className="document-list">
{output.supporting_document_files_attributes.map((sf, index) => {
@ -158,7 +158,7 @@ export const ChildForm: React.FC<ChildFormProps> = ({ child, onSubmit, supportin
</div>
</>}
{isPrivileged() && <>
{isPrivileged() && supportingDocumentsTypes?.length > 0 && <>
<FabAlert level='info'>
<p>{t('app.public.child_form.refuse_documents_info')}</p>
</FabAlert>

View File

@ -461,6 +461,34 @@ Application.Controllers.controller('ShowEventReservationsController', ['$scope',
return reservation.slots_reservations_attributes[0].is_valid === true || reservation.slots_reservations_attributes[0].is_valid === 'true';
};
/**
* Test if the provided reservation has been invalidated
* @param reservation {Reservation}
* @returns {boolean}
*/
$scope.isInvalidated = function (reservation) {
return reservation.slots_reservations_attributes[0].is_valid === false || reservation.slots_reservations_attributes[0].is_valid === 'false';
};
/**
* Get the price of a reservation
* @param reservation {Reservation}
*/
$scope.reservationAmount = function (reservation) {
let amount = 0;
for (const user of reservation.booking_users_attributes) {
if (user.event_price_category_id) {
const price_category = _.find($scope.event.event_price_categories_attributes, { id: user.event_price_category_id });
if (price_category) {
amount += price_category.amount;
}
} else {
amount += $scope.event.amount;
}
}
return amount;
};
/**
* Callback to validate a reservation
* @param reservation {Reservation}
@ -625,7 +653,7 @@ Application.Controllers.controller('ShowEventReservationsController', ['$scope',
if (r.id === reservation.id) {
return reservation;
}
if ($scope.event.amount === 0) {
if ($scope.reservationAmount(reservation) === 0) {
growl.success(_t('app.admin.event_reservations.reservation_was_successfully_present'));
} else {
growl.success(_t('app.admin.event_reservations.reservation_was_successfully_paid'));

View File

@ -236,8 +236,19 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
* Callback to call when the number of tickets to book changes in the current booking
*/
$scope.changeNbPlaces = function (priceType) {
let reservedPlaces = 0;
if ($scope.event.event_type === 'family') {
reservedPlaces = $scope.reservations.reduce((sum, reservation) => {
return sum + reservation.booking_users_attributes.length;
}, 0);
}
let nb_free_places = $scope.event.nb_free_places;
if ($scope.event.event_type === 'family') {
const maxPlaces = $scope.children.length + 1 - reservedPlaces;
nb_free_places = Math.min(maxPlaces, $scope.event.nb_free_places);
}
// compute the total remaining places
let remain = ($scope.event.event_type === 'family' ? ($scope.children.length + 1) : $scope.event.nb_free_places) - $scope.reserve.nbReservePlaces;
let remain = nb_free_places - $scope.reserve.nbReservePlaces;
for (let ticket in $scope.reserve.tickets) {
remain -= $scope.reserve.tickets[ticket];
}
@ -360,8 +371,8 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
if ($scope.ctrl.member) {
Member.get({ id: $scope.ctrl.member.id }, function (member) {
$scope.ctrl.member = member;
getReservations($scope.event.id, 'Event', $scope.ctrl.member.id).then(function (reservations) {
getChildren($scope.currentUser.id).then(function (children) {
getReservations($scope.event.id, 'Event', $scope.ctrl.member.id).then(function () {
getChildren($scope.ctrl.member.id).then(function (children) {
updateNbReservePlaces();
});
});
@ -678,7 +689,7 @@ 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).then(function (reservations) {
getReservations($scope.event.id, 'Event', $scope.currentUser.id).then(function () {
getChildren($scope.currentUser.id).then(function (children) {
updateNbReservePlaces();
});
@ -772,7 +783,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
* update number of places available for each price category for the family event
*/
const updateNbReservePlaces = function () {
if ($scope.event.event_type === 'family') {
if ($scope.event.event_type === 'family' && $scope.ctrl.member.id) {
const reservedPlaces = $scope.reservations.reduce((sum, reservation) => {
return sum + reservation.booking_users_attributes.length;
}, 0);
@ -990,7 +1001,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
const afterPayment = function (invoice) {
Event.get({ id: $scope.event.id }).$promise.then(function (event) {
$scope.event = event;
getReservations($scope.event.id, 'Event', $scope.ctrl.member.id).then(function (reservations) {
getReservations($scope.event.id, 'Event', $scope.ctrl.member.id).then(function () {
updateNbReservePlaces();
$scope.reserveSuccess = true;
$scope.coupon.applied = null;

View File

@ -70,10 +70,11 @@ export interface Reservation {
end_at: TDateISO,
event_type?: string,
event_title?: string,
event_pre_registration?: boolean
event_pre_registration?: boolean,
canceled_at?: TDateISO,
is_valid?: boolean,
is_paid?: boolean,
amount?: number
}
export interface ReservationIndexFilter extends ApiFilter {

View File

@ -1,10 +1,10 @@
<div class="modal-header">
<img ng-src="{{logoBlack.custom_asset_file_attributes.attachment_url}}" alt="{{logo.custom_asset_file_attributes.attachment}}" class="modal-logo"/>
<h1 translate ng-show="reservation && event.amount !== 0">{{ 'app.admin.event_reservations.confirm_payment' }}</h1>
<h1 translate ng-show="reservation && event.amount === 0">{{ 'app.admin.event_reservations.confirm_present' }}</h1>
<h1 translate ng-show="reservation && amount !== 0">{{ 'app.admin.event_reservations.confirm_payment' }}</h1>
<h1 translate ng-show="reservation && amount === 0">{{ 'app.admin.event_reservations.confirm_present' }}</h1>
</div>
<div class="modal-body">
<div ng-show="event.amount !== 0">
<div ng-show="amount !== 0">
<div class="row" ng-show="!offered">
<wallet-info current-user="currentUser"
cart="cartItems"
@ -41,12 +41,12 @@
</div>
</div>
<div ng-if="event.amount === 0">
<div ng-if="amount === 0">
<p translate>{{ 'app.admin.event_reservations.confirm_present_info' }}</p>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-info" ng-if="event.amount !== 0" ng-click="ok()" ng-disabled="attempting || !payment" ng-bind-html="validButtonName"></button>
<button class="btn btn-info" ng-if="event.amount === 0" ng-click="ok()" ng-disabled="attempting" ng-bind-html="validButtonName"></button>
<button class="btn btn-info" ng-if="amount !== 0" ng-click="ok()" ng-disabled="attempting || !payment" ng-bind-html="validButtonName"></button>
<button class="btn btn-info" ng-if="amount === 0" ng-click="ok()" ng-disabled="attempting" ng-bind-html="validButtonName"></button>
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
</div>

View File

@ -47,11 +47,12 @@
<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) && !reservation.is_paid" class="v-middle badge text-sm bg-info" translate="">{{ 'app.admin.event_reservations.event_status.pre_registered' }}</span>
<span ng-if="event.amount !== 0 && isValidated(reservation) && !isCancelled(reservation) && !reservation.is_paid" class="v-middle badge text-sm bg-stage" translate="">{{ 'app.admin.event_reservations.event_status.to_pay' }}</span>
<span ng-if="event.amount === 0 && isValidated(reservation) && !isCancelled(reservation) && !reservation.is_paid" class="v-middle badge text-sm bg-stage" translate="">{{ 'app.admin.event_reservations.event_status.registered' }}</span>
<span ng-if="event.amount !== 0 && reservation.is_paid && !isCancelled(reservation)" class="v-middle badge text-sm bg-success" translate="">{{ 'app.admin.event_reservations.event_status.paid' }}</span>
<span ng-if="event.amount === 0 && reservation.is_paid && !isCancelled(reservation)" class="v-middle badge text-sm bg-success" translate="">{{ 'app.admin.event_reservations.event_status.present' }}</span>
<span ng-if="!isValidated(reservation) && !isInvalidated(reservation) && !isCancelled(reservation) && !reservation.is_paid" class="v-middle badge text-sm bg-info" translate="">{{ 'app.admin.event_reservations.event_status.pre_registered' }}</span>
<span ng-if="reservationAmount(reservation) !== 0 && isValidated(reservation) && !isCancelled(reservation) && !reservation.is_paid" class="v-middle badge text-sm bg-stage" translate="">{{ 'app.admin.event_reservations.event_status.to_pay' }}</span>
<span ng-if="reservationAmount(reservation) === 0 && isValidated(reservation) && !isCancelled(reservation) && !reservation.is_paid" class="v-middle badge text-sm bg-stage" translate="">{{ 'app.admin.event_reservations.event_status.registered' }}</span>
<span ng-if="isInvalidated(reservation) && !isCancelled(reservation) && !reservation.is_paid" class="v-middle badge text-sm bg-event" translate="">{{ 'app.admin.event_reservations.event_status.not_validated' }}</span>
<span ng-if="reservationAmount(reservation) !== 0 && reservation.is_paid && !isCancelled(reservation)" class="v-middle badge text-sm bg-success" translate="">{{ 'app.admin.event_reservations.event_status.paid' }}</span>
<span ng-if="reservationAmount(reservation) === 0 && reservation.is_paid && !isCancelled(reservation)" class="v-middle badge text-sm bg-success" translate="">{{ 'app.admin.event_reservations.event_status.present' }}</span>
<span ng-if="isCancelled(reservation)" class="v-middle badge text-sm bg-event" translate="">{{ 'app.admin.event_reservations.event_status.canceled' }}</span>
</td>
<td ng-if="event.pre_registration">
@ -67,8 +68,8 @@
</label>
</div>
<button class="btn btn-default" ng-click="payReservation(reservation)" ng-if="isValidated(reservation) && !isCancelled(reservation) && !reservation.is_paid">
<span ng-if="event.amount !== 0" translate>{{ 'app.admin.event_reservations.pay' }}</span>
<span ng-if="event.amount === 0" translate>{{ 'app.admin.event_reservations.present' }}</span>
<span ng-if="reservationAmount(reservation) !== 0" translate>{{ 'app.admin.event_reservations.pay' }}</span>
<span ng-if="reservationAmount(reservation) === 0" translate>{{ 'app.admin.event_reservations.present' }}</span>
</button>
</div>
</td>

View File

@ -180,7 +180,7 @@
class="form-control">
<option value=""></option>
</select>
<uib-alert type="danger" ng-if="enableChildValidationRequired && user.booked && user.booked.type === 'Child' && !user.booked.is_valid">
<uib-alert type="danger" ng-if="enableChildValidationRequired && user.booked && user.booked.type === 'Child' && !user.booked.validated_at">
<p class="text-sm">
<i class="fa fa-warning"></i>
<span translate>{{ 'app.shared.cart.child_validation_required_alert' }}</span>

View File

@ -2,8 +2,9 @@
# An admin can mark an uploaded document as refused, this will notify the member
class SupportingDocumentRefusal < ApplicationRecord
include NotificationAttachedObject
belongs_to :supportable, polymorphic: true
belongs_to :operator, class_name: 'User', inverse_of: :supporting_document_refusals
has_many :supporting_document_refusals_types, dependent: :destroy
has_many :supporting_document_types, through: :supporting_document_refusals_types
has_and_belongs_to_many :supporting_document_types
end

View File

@ -6,8 +6,7 @@ class SupportingDocumentType < ApplicationRecord
has_many :supporting_document_files, dependent: :destroy
has_many :supporting_document_refusals_types, dependent: :destroy
has_many :supporting_document_refusals, through: :supporting_document_refusals_types
has_and_belongs_to_many :supporting_document_refusals, dependent: :destroy
validates :document_type, presence: true, inclusion: { in: %w[User Child] }
end

View File

@ -28,7 +28,9 @@ class ChildService
all_files_are_upload = true
SupportingDocumentType.where(document_type: 'Child').each do |sdt|
file = sdt.supporting_document_files.find_by(supportable: child)
all_files_are_upload = false if file.nil? || file.attachment_identifier.nil?
if file.nil? || file.attachment_identifier.nil? || child_params['supporting_document_files_attributes']['0']['attachment'].blank?
all_files_are_upload = false
end
end
if all_files_are_upload
NotificationCenter.call type: 'notify_admin_user_child_supporting_document_files_updated',

View File

@ -89,6 +89,7 @@ json.events_reservations @member.reservations.where(reservable_type: 'Event').jo
json.event_pre_registration sr.reservation.reservable.pre_registration
json.is_valid sr.is_valid
json.is_paid sr.reservation.invoice_items.count.positive?
json.amount sr.reservation.invoice_items.sum(:amount)
json.canceled_at sr.canceled_at
json.booking_users_attributes sr.reservation.booking_users.order(booked_type: :desc) do |bu|
json.id bu.id

View File

@ -657,6 +657,7 @@ en:
canceled: "Canceled"
present: "Present"
registered: "Registered"
not_validated: "Not validated"
affirmative: "yes"
negative: "no"
validate: "Validate"

View File

@ -657,6 +657,7 @@ fr:
canceled: "Annulée"
present: "Présent"
registered: "Inscrit"
not_validated: "Non validée"
affirmative: "Oui"
negative: "Non"
validate: "Valider"