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

(wip) event reservation naminative

This commit is contained in:
Du Peng 2023-05-09 18:54:16 +02:00
parent dc4151cfb0
commit 8c4602e535
15 changed files with 291 additions and 41 deletions

View File

@ -269,6 +269,8 @@ GEM
net-smtp (0.3.3)
net-protocol
nio4r (2.5.8)
nokogiri (1.14.3-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.14.3-x86_64-linux)
racc (~> 1.4)
oauth2 (1.4.4)
@ -524,6 +526,7 @@ GEM
zeitwerk (2.6.7)
PLATFORMS
x86_64-darwin-20
x86_64-linux
DEPENDENCIES

View File

@ -96,7 +96,7 @@ class API::EventsController < API::APIController
# handle general properties
event_preparams = params.required(:event).permit(:title, :description, :start_date, :start_time, :end_date, :end_time,
:amount, :nb_total_places, :availability_id, :all_day, :recurrence,
:recurrence_end_at, :category_id, :event_theme_ids, :age_range_id,
:recurrence_end_at, :category_id, :event_theme_ids, :age_range_id, :booking_nominative,
event_theme_ids: [],
event_image_attributes: %i[id attachment],
event_files_attributes: %i[id attachment _destroy],

View File

@ -290,6 +290,11 @@ export const EventForm: React.FC<EventFormProps> = ({ action, event, onError, on
label={t('app.admin.event_form.seats_available')}
type="number"
tooltip={t('app.admin.event_form.seats_help')} />
<FormSwitch control={control}
id="booking_nominative"
label={t('app.admin.event_form.booking_nominative')}
formState={formState}
tooltip={t('app.admin.event_form.booking_nominative_help')} />
<FormInput register={register}
type="number"
id="amount"

View File

@ -160,7 +160,10 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
toReserve: false,
amountTotal: 0,
totalNoCoupon: 0,
totalSeats: 0
totalSeats: 0,
bookingUsers: {
normal: []
},
};
// Discount coupon to apply to the basket, if any
@ -223,10 +226,20 @@ 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 () {
$scope.changeNbPlaces = function (priceType) {
// compute the total remaining places
let remain = $scope.event.nb_free_places - $scope.reserve.nbReservePlaces;
for (let ticket in $scope.reserve.tickets) {
@ -247,6 +260,22 @@ 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 });
}
});
} else {
_.times(nbBookingUsers - nbReservePlaces, () => {
$scope.reserve.bookingUsers[priceType].pop();
});
}
// recompute the total price
return $scope.computeEventAmount();
};
@ -638,7 +667,8 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
reservable_type: 'Event',
slots_reservations_attributes: [],
nb_reserve_places: reserve.nbReservePlaces,
tickets_attributes: []
tickets_attributes: [],
booking_users_attributes: []
};
reservation.slots_reservations_attributes.push({
@ -656,6 +686,15 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
}
}
if (event.booking_nominative) {
for (const key of Object.keys($scope.reserve.bookingUsers)) {
for (const user of $scope.reserve.bookingUsers[key]) {
reservation.booking_users_attributes.push(user);
}
}
console.log(reservation);
}
return { reservation };
};
@ -688,11 +727,15 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
tickets: {},
toReserve: false,
amountTotal: 0,
totalSeats: 0
totalSeats: 0,
bookingUsers: {
normal: [],
},
};
for (let evt_px_cat of Array.from($scope.event.event_price_categories_attributes)) {
$scope.reserve.nbPlaces[evt_px_cat.id] = __range__(0, $scope.event.nb_free_places, true);
$scope.reserve.bookingUsers[evt_px_cat.id] = [];
$scope.reserve.tickets[evt_px_cat.id] = 0;
}

View File

@ -63,7 +63,8 @@ export interface Event {
}>,
recurrence: RecurrenceOption,
recurrence_end_at: Date,
advanced_accounting_attributes?: AdvancedAccounting
advanced_accounting_attributes?: AdvancedAccounting,
booking_nominative: boolean,
}
export interface EventDecoration {

View File

@ -116,16 +116,28 @@
<div class="row">
<label class="col-sm-6 control-label">{{ 'app.public.events_show.full_price_' | translate }} <span class="text-blue">{{event.amount | currency}}</span></label>
<div class="col-sm-6">
<select ng-model="reserve.nbReservePlaces" ng-change="changeNbPlaces()" ng-options="i for i in reserve.nbPlaces.normal">
<select ng-model="reserve.nbReservePlaces" ng-change="changeNbPlaces('normal')" ng-options="i for i in reserve.nbPlaces.normal">
</select> {{ 'app.public.events_show.ticket' | translate:{NUMBER:reserve.nbReservePlaces} }}
</div>
<div class="col-sm-12" ng-if="event.booking_nominative && reserve.nbReservePlaces > 0">
<div ng-repeat="user in reserve.bookingUsers.normal">
<label class="">Name</label>
<input type="text" class="form-control" ng-model="user.name">
</div>
</div>
</div>
<div class="row" ng-repeat="price in event.event_price_categories_attributes">
<label class="col-sm-6 control-label">{{price.category.name}} : <span class="text-blue">{{price.amount | currency}}</span></label>
<div class="col-sm-6">
<select ng-model="reserve.tickets[price.id]" ng-change="changeNbPlaces()" ng-options="i for i in reserve.nbPlaces[price.id]">
<select ng-model="reserve.tickets[price.id]" ng-change="changeNbPlaces(price.id)" ng-options="i for i in reserve.nbPlaces[price.id]">
</select> {{ 'app.public.events_show.ticket' | translate:{NUMBER:reserve.tickets[price.id]} }}
</div>
<div class="col-sm-12" ng-if="event.booking_nominative && reserve.tickets[price.id] > 0">
<div ng-repeat="user in reserve.bookingUsers[price.id]">
<label class="">Name</label>
<input type="text" class="form-control" ng-model="user.name">
</div>
</div>
</div>
<div ng-show="currentUser.role == 'admin'" class="m-t">

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
# BookingUser is a class for save the booking info of reservation
# booked can be a User or a Child (polymorphic)
class BookingUser < ApplicationRecord
belongs_to :reservation
belongs_to :booked, polymorphic: true
belongs_to :event_price_category
end

View File

@ -23,6 +23,9 @@ class Reservation < ApplicationRecord
has_many :prepaid_pack_reservations, dependent: :destroy
has_many :booking_users, dependent: :destroy
accepts_nested_attributes_for :booking_users, allow_destroy: true
validates :reservable_id, :reservable_type, presence: true
validate :machine_not_already_reserved, if: -> { reservable.is_a?(Machine) }
validate :training_not_fully_reserved, if: -> { reservable.is_a?(Training) }

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
json.extract! event, :id, :title, :description
json.extract! event, :id, :title, :description, :booking_nominative
if event.event_image
json.event_image_attributes do
json.id event.event_image.id

View File

@ -139,6 +139,8 @@ en:
event_themes: "Event themes"
age_range: "Age range"
add_price: "Add a price"
booking_nominative: "Nominative booking"
booking_nominative_help: "If you check this option, the members will have to enter the names of the participants when booking."
save: "Save"
create_success: "The event was created successfully"
events_updated: "{COUNT, plural, =1{One event was} other{{COUNT} Events were}} successfully updated"

View File

@ -139,6 +139,8 @@ fr:
event_themes: "Thèmes de l'événement"
age_range: "Tranche d'âge"
add_price: "Ajouter un tarif"
booking_nominative: "Réservation nominative"
booking_nominative_help: "Si cette option est activée, les réservations seront nominatives. Les participants devront s'identifier pour réserver."
save: "Enregistrer"
create_success: "L'événement a bien été créé"
events_updated: "{COUNT, plural, one {}=1{Un événement à été} other{{COUNT} événements ont été}} mis à jour avec succès"

View File

@ -0,0 +1,8 @@
# frozen_string_literal: true
# add booking_nominative to event
class AddBookingNominativeToEvent < ActiveRecord::Migration[7.0]
def change
add_column :events, :booking_nominative, :boolean, default: false
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
# create booking_users table
class CreateBookingUsers < ActiveRecord::Migration[7.0]
def change
create_table :booking_users do |t|
t.string :name
t.belongs_to :reservation, foreign_key: true
t.references :booked, polymorphic: true
t.references :event_price_category, foreign_key: true
t.timestamps
end
end
end

View File

@ -115,8 +115,8 @@ SET default_tablespace = '';
CREATE TABLE public.abuses (
id integer NOT NULL,
signaled_id integer,
signaled_type character varying,
signaled_id integer,
first_name character varying,
last_name character varying,
email character varying,
@ -236,8 +236,8 @@ CREATE TABLE public.addresses (
locality character varying,
country character varying,
postal_code character varying,
placeable_id integer,
placeable_type character varying,
placeable_id integer,
created_at timestamp without time zone,
updated_at timestamp without time zone
);
@ -346,8 +346,8 @@ CREATE TABLE public.ar_internal_metadata (
CREATE TABLE public.assets (
id integer NOT NULL,
viewable_id integer,
viewable_type character varying,
viewable_id integer,
attachment character varying,
type character varying,
created_at timestamp without time zone,
@ -520,6 +520,41 @@ CREATE SEQUENCE public.availability_tags_id_seq
ALTER SEQUENCE public.availability_tags_id_seq OWNED BY public.availability_tags.id;
--
-- Name: booking_users; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.booking_users (
id bigint NOT NULL,
name character varying,
reservation_id bigint,
booked_type character varying,
booked_id bigint,
event_price_category_id bigint,
created_at timestamp(6) without time zone NOT NULL,
updated_at timestamp(6) without time zone NOT NULL
);
--
-- Name: booking_users_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public.booking_users_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: booking_users_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE public.booking_users_id_seq OWNED BY public.booking_users.id;
--
-- Name: cart_item_coupons; Type: TABLE; Schema: public; Owner: -
--
@ -892,6 +927,42 @@ CREATE SEQUENCE public.chained_elements_id_seq
ALTER SEQUENCE public.chained_elements_id_seq OWNED BY public.chained_elements.id;
--
-- Name: children; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.children (
id bigint NOT NULL,
user_id bigint,
first_name character varying,
last_name character varying,
birthday date,
phone character varying,
email character varying,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL
);
--
-- Name: children_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public.children_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: children_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE public.children_id_seq OWNED BY public.children.id;
--
-- Name: components; Type: TABLE; Schema: public; Owner: -
--
@ -965,8 +1036,8 @@ ALTER SEQUENCE public.coupons_id_seq OWNED BY public.coupons.id;
CREATE TABLE public.credits (
id integer NOT NULL,
creditable_id integer,
creditable_type character varying,
creditable_id integer,
plan_id integer,
hours integer,
created_at timestamp without time zone,
@ -1136,7 +1207,8 @@ CREATE TABLE public.events (
recurrence_id integer,
age_range_id integer,
category_id integer,
deleted_at timestamp without time zone
deleted_at timestamp without time zone,
booking_nominative boolean DEFAULT false
);
@ -1762,15 +1834,15 @@ ALTER SEQUENCE public.notification_types_id_seq OWNED BY public.notification_typ
CREATE TABLE public.notifications (
id integer NOT NULL,
receiver_id integer,
attached_object_id integer,
attached_object_type character varying,
attached_object_id integer,
notification_type_id integer,
is_read boolean DEFAULT false,
created_at timestamp without time zone,
updated_at timestamp without time zone,
receiver_type character varying,
is_send boolean DEFAULT false,
meta_data jsonb DEFAULT '{}'::jsonb
meta_data jsonb DEFAULT '"{}"'::jsonb
);
@ -2498,8 +2570,8 @@ CREATE TABLE public.prices (
id integer NOT NULL,
group_id integer,
plan_id integer,
priceable_id integer,
priceable_type character varying,
priceable_id integer,
amount integer,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL,
@ -2962,8 +3034,8 @@ CREATE TABLE public.reservations (
message text,
created_at timestamp without time zone,
updated_at timestamp without time zone,
reservable_id integer,
reservable_type character varying,
reservable_id integer,
nb_reserve_places integer,
statistic_profile_id integer
);
@ -2995,8 +3067,8 @@ ALTER SEQUENCE public.reservations_id_seq OWNED BY public.reservations.id;
CREATE TABLE public.roles (
id integer NOT NULL,
name character varying,
resource_id integer,
resource_type character varying,
resource_id integer,
created_at timestamp without time zone,
updated_at timestamp without time zone
);
@ -4102,8 +4174,8 @@ CREATE TABLE public.users (
is_allow_newsletter boolean,
current_sign_in_ip inet,
last_sign_in_ip inet,
mapped_from_sso character varying,
validated_at timestamp without time zone
validated_at timestamp without time zone,
mapped_from_sso character varying
);
@ -4312,6 +4384,13 @@ ALTER TABLE ONLY public.availabilities ALTER COLUMN id SET DEFAULT nextval('publ
ALTER TABLE ONLY public.availability_tags ALTER COLUMN id SET DEFAULT nextval('public.availability_tags_id_seq'::regclass);
--
-- Name: booking_users id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.booking_users ALTER COLUMN id SET DEFAULT nextval('public.booking_users_id_seq'::regclass);
--
-- Name: cart_item_coupons id; Type: DEFAULT; Schema: public; Owner: -
--
@ -4389,6 +4468,13 @@ ALTER TABLE ONLY public.categories ALTER COLUMN id SET DEFAULT nextval('public.c
ALTER TABLE ONLY public.chained_elements ALTER COLUMN id SET DEFAULT nextval('public.chained_elements_id_seq'::regclass);
--
-- Name: children id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.children ALTER COLUMN id SET DEFAULT nextval('public.children_id_seq'::regclass);
--
-- Name: components id; Type: DEFAULT; Schema: public; Owner: -
--
@ -5150,6 +5236,14 @@ ALTER TABLE ONLY public.availability_tags
ADD CONSTRAINT availability_tags_pkey PRIMARY KEY (id);
--
-- Name: booking_users booking_users_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.booking_users
ADD CONSTRAINT booking_users_pkey PRIMARY KEY (id);
--
-- Name: cart_item_coupons cart_item_coupons_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@ -5238,6 +5332,14 @@ ALTER TABLE ONLY public.chained_elements
ADD CONSTRAINT chained_elements_pkey PRIMARY KEY (id);
--
-- Name: children children_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.children
ADD CONSTRAINT children_pkey PRIMARY KEY (id);
--
-- Name: components components_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@ -5718,6 +5820,14 @@ ALTER TABLE ONLY public.roles
ADD CONSTRAINT roles_pkey PRIMARY KEY (id);
--
-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.schema_migrations
ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version);
--
-- Name: settings settings_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@ -6061,6 +6171,27 @@ CREATE INDEX index_availability_tags_on_availability_id ON public.availability_t
CREATE INDEX index_availability_tags_on_tag_id ON public.availability_tags USING btree (tag_id);
--
-- Name: index_booking_users_on_booked; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_booking_users_on_booked ON public.booking_users USING btree (booked_type, booked_id);
--
-- Name: index_booking_users_on_event_price_category_id; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_booking_users_on_event_price_category_id ON public.booking_users USING btree (event_price_category_id);
--
-- Name: index_booking_users_on_reservation_id; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_booking_users_on_reservation_id ON public.booking_users USING btree (reservation_id);
--
-- Name: index_cart_item_coupons_on_coupon_id; Type: INDEX; Schema: public; Owner: -
--
@ -6243,6 +6374,13 @@ CREATE UNIQUE INDEX index_categories_on_slug ON public.categories USING btree (s
CREATE INDEX index_chained_elements_on_element ON public.chained_elements USING btree (element_type, element_id);
--
-- Name: index_children_on_user_id; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_children_on_user_id ON public.children USING btree (user_id);
--
-- Name: index_coupons_on_code; Type: INDEX; Schema: public; Owner: -
--
@ -7377,21 +7515,6 @@ CREATE INDEX proof_of_identity_type_id_and_proof_of_identity_refusal_id ON publi
CREATE UNIQUE INDEX unique_not_null_external_id ON public.invoicing_profiles USING btree (external_id) WHERE (external_id IS NOT NULL);
--
-- Name: unique_schema_migrations; Type: INDEX; Schema: public; Owner: -
--
CREATE UNIQUE INDEX unique_schema_migrations ON public.schema_migrations USING btree (version);
--
-- 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: -
--
@ -7625,6 +7748,14 @@ ALTER TABLE ONLY public.subscriptions
ADD CONSTRAINT fk_rails_358a71f738 FOREIGN KEY (statistic_profile_id) REFERENCES public.statistic_profiles(id);
--
-- Name: booking_users fk_rails_38ad1ae7e8; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.booking_users
ADD CONSTRAINT fk_rails_38ad1ae7e8 FOREIGN KEY (reservation_id) REFERENCES public.reservations(id);
--
-- Name: invoices fk_rails_40d78f8cf6; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@ -8025,6 +8156,14 @@ ALTER TABLE ONLY public.cart_item_coupons
ADD CONSTRAINT fk_rails_a44bac1e45 FOREIGN KEY (operator_profile_id) REFERENCES public.invoicing_profiles(id);
--
-- Name: children fk_rails_a51d7cfb22; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.children
ADD CONSTRAINT fk_rails_a51d7cfb22 FOREIGN KEY (user_id) REFERENCES public.users(id);
--
-- Name: projects_themes fk_rails_b021a22658; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@ -8257,6 +8396,14 @@ ALTER TABLE ONLY public.projects
ADD CONSTRAINT fk_rails_e812590204 FOREIGN KEY (author_statistic_profile_id) REFERENCES public.statistic_profiles(id);
--
-- Name: booking_users fk_rails_e88263229e; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.booking_users
ADD CONSTRAINT fk_rails_e88263229e FOREIGN KEY (event_price_category_id) REFERENCES public.event_price_categories(id);
--
-- Name: user_tags fk_rails_ea0382482a; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@ -8368,7 +8515,6 @@ INSERT INTO "schema_migrations" (version) VALUES
('20140605125131'),
('20140605142133'),
('20140605151442'),
('20140606133116'),
('20140609092700'),
('20140609092827'),
('20140610153123'),
@ -8437,14 +8583,12 @@ INSERT INTO "schema_migrations" (version) VALUES
('20150507075620'),
('20150512123546'),
('20150520132030'),
('20150520133409'),
('20150526130729'),
('20150527153312'),
('20150529113555'),
('20150601125944'),
('20150603104502'),
('20150603104658'),
('20150603133050'),
('20150604081757'),
('20150604131525'),
('20150608142234'),
@ -8526,7 +8670,6 @@ INSERT INTO "schema_migrations" (version) VALUES
('20160905142700'),
('20160906094739'),
('20160906094847'),
('20160906145713'),
('20160915105234'),
('20161123104604'),
('20170109085345'),
@ -8693,6 +8836,9 @@ INSERT INTO "schema_migrations" (version) VALUES
('20230324095639'),
('20230328094807'),
('20230328094808'),
('20230328094809');
('20230328094809'),
('20230331132506'),
('20230509121907'),
('20230509161557');

View File

@ -27,6 +27,7 @@ describe('EventForm', () => {
expect(screen.getByLabelText(/app.admin.event_form._and_ends_on/)).toBeInTheDocument();
expect(screen.getByLabelText(/app.admin.event_form.seats_available/)).toBeInTheDocument();
expect(screen.getByLabelText(/app.admin.event_form.standard_rate/)).toBeInTheDocument();
expect(screen.getByLabelText(/app.admin.event_form.booking_nominative/)).toBeInTheDocument();
expect(screen.getByRole('button', { name: /app.admin.event_form.add_price/ })).toBeInTheDocument();
expect(screen.getByRole('button', { name: /app.admin.event_form.add_a_new_file/ })).toBeInTheDocument();
expect(screen.getByLabelText(/app.admin.advanced_accounting_form.code/)).toBeInTheDocument();