Allow to cancel bookings

This commit is contained in:
jordi fita mas 2024-05-03 17:21:20 +02:00
parent e425b88477
commit e5253f9adb
12 changed files with 216 additions and 33 deletions

23
deploy/cancel_booking.sql Normal file
View File

@ -0,0 +1,23 @@
-- Deploy camper:cancel_booking to pg
-- requires: roles
-- requires: schema_camper
-- requires: booking
-- requires: booking_campsite
begin;
set search_path to camper, public;
create or replace function cancel_booking(bid integer) returns void as
$$
delete from booking_campsite where booking_id = bid;
update booking set booking_status = 'cancelled' where booking_id = bid;
$$
language sql
;
revoke execute on function cancel_booking(integer) from public;
grant execute on function cancel_booking(integer) to employee;
grant execute on function cancel_booking(integer) to admin;
commit;

View File

@ -484,6 +484,21 @@ func updateBooking(w http.ResponseWriter, r *http.Request, user *auth.User, comp
}
panic(err)
}
if err := r.ParseForm(); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if len(r.Form["cancel"]) > 0 {
if err := conn.CancelBooking(r.Context(), bookingID); err != nil {
panic(err)
}
if bookingStatus == "created" {
httplib.Redirect(w, r, "/admin/prebookings", http.StatusSeeOther)
} else {
httplib.Redirect(w, r, "/admin/bookings", http.StatusSeeOther)
}
return
}
processAdminBookingForm(w, r, user, company, conn, bookingID, func(ctx context.Context, tx *database.Tx, f *adminBookingForm) error {
var err error
_, err = tx.EditBookingFromPayment(ctx, slug, f.PaymentSlug.Val)

View File

@ -366,6 +366,11 @@ func (tx *Tx) EditBooking(ctx context.Context, bookingID int, customerName strin
return err
}
func (c *Conn) CancelBooking(ctx context.Context, bookingID int) error {
_, err := c.Exec(ctx, "select cancel_booking($1)", bookingID)
return err
}
func (c *Conn) CheckInGuests(ctx context.Context, bookingSlug string, guests []*CheckedInGuest) error {
_, err := c.Exec(ctx, "select check_in_guests(booking_id, $2) from booking where slug = $1", bookingSlug, CheckedInGuestArray(guests))
return err

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: camper\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-05-03 00:56+0200\n"
"POT-Creation-Date: 2024-05-03 17:19+0200\n"
"PO-Revision-Date: 2024-02-06 10:04+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Catalan <ca@dodds.net>\n"
@ -2748,6 +2748,15 @@ msgctxt "action"
msgid "Invoice Booking"
msgstr "Factura la reserva"
#: web/templates/admin/booking/form.gohtml:58
msgid "Are you sure you wish to cancel this booking?"
msgstr "Esteu segur de voler cancel·lar aquesta reserva?"
#: web/templates/admin/booking/form.gohtml:59
msgctxt "action"
msgid "Cancel booking"
msgstr "Cancel·la la reserva"
#: web/templates/admin/booking/checkin.gohtml:6
msgctxt "title"
msgid "Check-in Booking"
@ -2933,7 +2942,7 @@ msgid "Email can not be empty."
msgstr "No podeu deixar el correu-e en blanc."
#: pkg/app/login.go:57 pkg/app/user.go:247 pkg/customer/admin.go:345
#: pkg/company/admin.go:225 pkg/booking/admin.go:582 pkg/booking/public.go:593
#: pkg/company/admin.go:225 pkg/booking/admin.go:597 pkg/booking/public.go:593
msgid "This email is not valid. It should be like name@domain.com."
msgstr "Aquest correu-e no és vàlid. Hauria de ser similar a nom@domini.com."
@ -3144,7 +3153,7 @@ msgctxt "header"
msgid "Children (aged 2 to 10)"
msgstr "Mainada (entre 2 i 10 anys)"
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:558 pkg/booking/public.go:177
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:573 pkg/booking/public.go:177
#: pkg/booking/public.go:232
msgid "Selected campsite type is not valid."
msgstr "El tipus dallotjament escollit no és vàlid."
@ -3196,12 +3205,12 @@ msgid "ID document number can not be empty."
msgstr "No podeu deixar el número document didentitat en blanc."
#: pkg/customer/admin.go:333 pkg/booking/checkin.go:291
#: pkg/booking/checkin.go:292 pkg/booking/admin.go:570
#: pkg/booking/checkin.go:292 pkg/booking/admin.go:585
#: pkg/booking/public.go:581
msgid "Full name can not be empty."
msgstr "No podeu deixar el nom i els cognoms en blanc."
#: pkg/customer/admin.go:334 pkg/booking/admin.go:571 pkg/booking/public.go:582
#: pkg/customer/admin.go:334 pkg/booking/admin.go:586 pkg/booking/public.go:582
msgid "Full name must have at least one letter."
msgstr "El nom i els cognoms han de tenir com a mínim una lletra."
@ -3217,13 +3226,13 @@ msgstr "No podeu deixar la població en blanc."
msgid "Postcode can not be empty."
msgstr "No podeu deixar el codi postal en blanc."
#: pkg/customer/admin.go:340 pkg/company/admin.go:234 pkg/booking/admin.go:577
#: pkg/customer/admin.go:340 pkg/company/admin.go:234 pkg/booking/admin.go:592
#: pkg/booking/public.go:588
msgid "This postcode is not valid."
msgstr "Aquest codi postal no és vàlid."
#: pkg/customer/admin.go:348 pkg/company/admin.go:220
#: pkg/booking/checkin.go:304 pkg/booking/admin.go:587
#: pkg/booking/checkin.go:304 pkg/booking/admin.go:602
#: pkg/booking/public.go:596
msgid "This phone number is not valid."
msgstr "Aquest número de telèfon no és vàlid."
@ -3492,19 +3501,19 @@ msgctxt "filename"
msgid "bookings.ods"
msgstr "reserves.ods"
#: pkg/booking/admin.go:576
#: pkg/booking/admin.go:591
msgid "Country can not be empty to validate the postcode."
msgstr "No podeu deixar el país en blanc per validar el codi postal."
#: pkg/booking/admin.go:586
#: pkg/booking/admin.go:601
msgid "Country can not be empty to validate the phone."
msgstr "No podeu deixar el país en blanc per validar el telèfon."
#: pkg/booking/admin.go:593
#: pkg/booking/admin.go:608
msgid "You must select at least one accommodation."
msgstr "Heu descollir com a mínim un allotjament."
#: pkg/booking/admin.go:599
#: pkg/booking/admin.go:614
msgid "The selected accommodations have no available openings in the requested dates."
msgstr "Els allotjaments escollits no estan disponibles a les dates demanades."

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: camper\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-05-03 00:56+0200\n"
"POT-Creation-Date: 2024-05-03 17:19+0200\n"
"PO-Revision-Date: 2024-02-06 10:04+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Spanish <es@tp.org.es>\n"
@ -2748,6 +2748,15 @@ msgctxt "action"
msgid "Invoice Booking"
msgstr "Facturar la reserva"
#: web/templates/admin/booking/form.gohtml:58
msgid "Are you sure you wish to cancel this booking?"
msgstr "¿Estáis seguro de querer cancelar esta reserva?"
#: web/templates/admin/booking/form.gohtml:59
msgctxt "action"
msgid "Cancel booking"
msgstr "Cancelar la reserva"
#: web/templates/admin/booking/checkin.gohtml:6
msgctxt "title"
msgid "Check-in Booking"
@ -2933,7 +2942,7 @@ msgid "Email can not be empty."
msgstr "No podéis dejar el correo-e en blanco."
#: pkg/app/login.go:57 pkg/app/user.go:247 pkg/customer/admin.go:345
#: pkg/company/admin.go:225 pkg/booking/admin.go:582 pkg/booking/public.go:593
#: pkg/company/admin.go:225 pkg/booking/admin.go:597 pkg/booking/public.go:593
msgid "This email is not valid. It should be like name@domain.com."
msgstr "Este correo-e no es válido. Tiene que ser parecido a nombre@dominio.com."
@ -3144,7 +3153,7 @@ msgctxt "header"
msgid "Children (aged 2 to 10)"
msgstr "Niños (de 2 a 10 años)"
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:558 pkg/booking/public.go:177
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:573 pkg/booking/public.go:177
#: pkg/booking/public.go:232
msgid "Selected campsite type is not valid."
msgstr "El tipo de alojamiento escogido no es válido."
@ -3196,12 +3205,12 @@ msgid "ID document number can not be empty."
msgstr "No podéis dejar el número del documento de identidad en blanco."
#: pkg/customer/admin.go:333 pkg/booking/checkin.go:291
#: pkg/booking/checkin.go:292 pkg/booking/admin.go:570
#: pkg/booking/checkin.go:292 pkg/booking/admin.go:585
#: pkg/booking/public.go:581
msgid "Full name can not be empty."
msgstr "No podéis dejar el nombre y los apellidos en blanco."
#: pkg/customer/admin.go:334 pkg/booking/admin.go:571 pkg/booking/public.go:582
#: pkg/customer/admin.go:334 pkg/booking/admin.go:586 pkg/booking/public.go:582
msgid "Full name must have at least one letter."
msgstr "El nombre y los apellidos tienen que tener como mínimo una letra."
@ -3217,13 +3226,13 @@ msgstr "No podéis dejar la población en blanco."
msgid "Postcode can not be empty."
msgstr "No podéis dejar el código postal en blanco."
#: pkg/customer/admin.go:340 pkg/company/admin.go:234 pkg/booking/admin.go:577
#: pkg/customer/admin.go:340 pkg/company/admin.go:234 pkg/booking/admin.go:592
#: pkg/booking/public.go:588
msgid "This postcode is not valid."
msgstr "Este código postal no es válido."
#: pkg/customer/admin.go:348 pkg/company/admin.go:220
#: pkg/booking/checkin.go:304 pkg/booking/admin.go:587
#: pkg/booking/checkin.go:304 pkg/booking/admin.go:602
#: pkg/booking/public.go:596
msgid "This phone number is not valid."
msgstr "Este teléfono no es válido."
@ -3492,19 +3501,19 @@ msgctxt "filename"
msgid "bookings.ods"
msgstr "reservas.ods"
#: pkg/booking/admin.go:576
#: pkg/booking/admin.go:591
msgid "Country can not be empty to validate the postcode."
msgstr "No podéis dejar el país en blanco para validar el código postal."
#: pkg/booking/admin.go:586
#: pkg/booking/admin.go:601
msgid "Country can not be empty to validate the phone."
msgstr "No podéis dejar el país en blanco para validar el teléfono."
#: pkg/booking/admin.go:593
#: pkg/booking/admin.go:608
msgid "You must select at least one accommodation."
msgstr "Tenéis que seleccionar como mínimo un alojamiento."
#: pkg/booking/admin.go:599
#: pkg/booking/admin.go:614
msgid "The selected accommodations have no available openings in the requested dates."
msgstr "Los alojamientos seleccionados no tienen disponibilidad en las fechas pedidas."

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: camper\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-05-03 00:56+0200\n"
"POT-Creation-Date: 2024-05-03 17:19+0200\n"
"PO-Revision-Date: 2024-02-06 10:05+0100\n"
"Last-Translator: Oriol Carbonell <info@oriolcarbonell.cat>\n"
"Language-Team: French <traduc@traduc.org>\n"
@ -2748,6 +2748,15 @@ msgctxt "action"
msgid "Invoice Booking"
msgstr "Facturer la réservation"
#: web/templates/admin/booking/form.gohtml:58
msgid "Are you sure you wish to cancel this booking?"
msgstr "Êtes-vous sûr de vouloir annuler cette réservation ?"
#: web/templates/admin/booking/form.gohtml:59
msgctxt "action"
msgid "Cancel booking"
msgstr "Annuler la réservation"
#: web/templates/admin/booking/checkin.gohtml:6
msgctxt "title"
msgid "Check-in Booking"
@ -2933,7 +2942,7 @@ msgid "Email can not be empty."
msgstr "Le-mail ne peut pas être vide."
#: pkg/app/login.go:57 pkg/app/user.go:247 pkg/customer/admin.go:345
#: pkg/company/admin.go:225 pkg/booking/admin.go:582 pkg/booking/public.go:593
#: pkg/company/admin.go:225 pkg/booking/admin.go:597 pkg/booking/public.go:593
msgid "This email is not valid. It should be like name@domain.com."
msgstr "Cette adresse e-mail nest pas valide. Il devrait en être name@domain.com."
@ -3144,7 +3153,7 @@ msgctxt "header"
msgid "Children (aged 2 to 10)"
msgstr "Enfants (de 2 à 10 anys)"
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:558 pkg/booking/public.go:177
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:573 pkg/booking/public.go:177
#: pkg/booking/public.go:232
msgid "Selected campsite type is not valid."
msgstr "Le type demplacement sélectionné nest pas valide."
@ -3196,12 +3205,12 @@ msgid "ID document number can not be empty."
msgstr "Le numéro de documento didentité ne peut pas être vide."
#: pkg/customer/admin.go:333 pkg/booking/checkin.go:291
#: pkg/booking/checkin.go:292 pkg/booking/admin.go:570
#: pkg/booking/checkin.go:292 pkg/booking/admin.go:585
#: pkg/booking/public.go:581
msgid "Full name can not be empty."
msgstr "Le nom complet ne peut pas être vide."
#: pkg/customer/admin.go:334 pkg/booking/admin.go:571 pkg/booking/public.go:582
#: pkg/customer/admin.go:334 pkg/booking/admin.go:586 pkg/booking/public.go:582
msgid "Full name must have at least one letter."
msgstr "Le nom complet doit comporter au moins une lettre."
@ -3217,13 +3226,13 @@ msgstr "La ville ne peut pas être vide."
msgid "Postcode can not be empty."
msgstr "Le code postal ne peut pas être vide."
#: pkg/customer/admin.go:340 pkg/company/admin.go:234 pkg/booking/admin.go:577
#: pkg/customer/admin.go:340 pkg/company/admin.go:234 pkg/booking/admin.go:592
#: pkg/booking/public.go:588
msgid "This postcode is not valid."
msgstr "Ce code postal nest pas valide."
#: pkg/customer/admin.go:348 pkg/company/admin.go:220
#: pkg/booking/checkin.go:304 pkg/booking/admin.go:587
#: pkg/booking/checkin.go:304 pkg/booking/admin.go:602
#: pkg/booking/public.go:596
msgid "This phone number is not valid."
msgstr "Ce numéro de téléphone nest pas valide."
@ -3492,19 +3501,19 @@ msgctxt "filename"
msgid "bookings.ods"
msgstr "reservations.ods"
#: pkg/booking/admin.go:576
#: pkg/booking/admin.go:591
msgid "Country can not be empty to validate the postcode."
msgstr "Le pays ne peut pas être vide pour valider le code postal."
#: pkg/booking/admin.go:586
#: pkg/booking/admin.go:601
msgid "Country can not be empty to validate the phone."
msgstr "Le pays ne peut pas être vide pour valider le téléphone."
#: pkg/booking/admin.go:593
#: pkg/booking/admin.go:608
msgid "You must select at least one accommodation."
msgstr "Vous devez sélectionner au moins un hébergement."
#: pkg/booking/admin.go:599
#: pkg/booking/admin.go:614
msgid "The selected accommodations have no available openings in the requested dates."
msgstr "Les hébergements sélectionnés nont pas de disponibilités aux dates demandées."

View File

@ -0,0 +1,7 @@
-- Revert camper:cancel_booking from pg
begin;
drop function if exists camper.cancel_booking(integer);
commit;

View File

@ -332,3 +332,4 @@ edit_contact [roles schema_camper email country_code contact extension_pg_libpho
booking_invoice [roles schema_camper booking invoice] 2024-04-28T19:45:05Z jordi fita mas <jordi@tandem.blog> # Add relation of booking invoices
marshal_payment [roles schema_camper payment payment_customer payment_option payment__acsi_card payment_customer__-acsi_card] 2024-04-29T17:11:59Z jordi fita mas <jordi@tandem.blog> # Add function to marshal a payment
unmarshal_booking [roles schema_camper booking booking_option extension_pg_libphonenumber] 2024-04-29T17:20:38Z jordi fita mas <jordi@tandem.blog> # Add function to unmarshal a booking
cancel_booking [roles schema_camper booking booking_campsite] 2024-05-03T14:27:31Z jordi fita mas <jordi@tandem.blog> # Add function to cancel a booking

89
test/cancel_booking.sql Normal file
View File

@ -0,0 +1,89 @@
-- Test cancel_booking
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(12);
set search_path to camper, public;
select has_function('camper', 'cancel_booking', array['integer']);
select function_lang_is('camper', 'cancel_booking', array['integer'], 'sql');
select function_returns('camper', 'cancel_booking', array['integer'], 'void');
select isnt_definer('camper', 'cancel_booking', array['integer']);
select volatility_is('camper', 'cancel_booking', array['integer'], 'volatile');
select function_privs_are('camper', 'cancel_booking', array ['integer'], 'guest', array[]::text[]);
select function_privs_are('camper', 'cancel_booking', array ['integer'], 'employee', array['EXECUTE']);
select function_privs_are('camper', 'cancel_booking', array ['integer'], 'admin', array['EXECUTE']);
select function_privs_are('camper', 'cancel_booking', array ['integer'], 'authenticator', array[]::text[]);
set client_min_messages to warning;
truncate booking_campsite cascade;
truncate booking cascade;
truncate campsite_type cascade;
truncate media cascade;
truncate media_content cascade;
truncate company cascade;
reset client_min_messages;
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, tourist_tax_max_days, country_code, currency_code, default_lang_tag)
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 7, 'ES', 'EUR', 'ca')
;
insert into media_content (media_type, bytes)
values ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffffff","a"};')
;
insert into media (media_id, company_id, original_filename, content_hash)
values (6, 2, 'cover2.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};'))
;
insert into campsite_type (campsite_type_id, company_id, name, media_id, max_campers, bookable_nights)
values (10, 2, 'Wooden lodge', 6, 7, '[1, 7]')
;
insert into campsite (campsite_id, company_id, label, campsite_type_id)
values (12, 2, 'A', 10)
, (14, 2, 'B', 10)
, (16, 2, 'C', 10)
;
insert into booking (booking_id, company_id, campsite_type_id, holder_name, stay, acsi_card, currency_code, zone_preferences, subtotal_nights, number_adults, subtotal_adults, number_teenagers, subtotal_teenagers, number_children, subtotal_children, number_dogs, subtotal_dogs, subtotal_tourist_tax, total)
values (18, 2, 10, 'Holder 2', daterange('2024-01-18', '2024-01-29'), false, 'EUR', '', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
, (20, 2, 10, 'Holder 4', daterange('2024-01-28', '2024-01-29'), true, 'USD', 'None', 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
;
insert into booking_campsite (booking_id, campsite_id, stay)
values (18, 12, daterange('2024-01-18', '2024-01-29'))
, (18, 14, daterange('2024-01-18', '2024-01-29'))
, (20, 16, daterange('2024-01-28', '2024-01-29'))
;
select lives_ok(
$$ select cancel_booking(18) $$,
'Should be able to cancel the first booking'
);
select bag_eq(
$$ select booking_id, company_id, campsite_type_id, stay, holder_name, address, postal_code, city, country_code::text, email::text, phone::text, lang_tag, zone_preferences, subtotal_nights::integer, number_adults::integer, subtotal_adults::integer, number_teenagers::integer, subtotal_teenagers::integer, number_children::integer, subtotal_children::integer, number_dogs::integer, subtotal_dogs::integer, subtotal_tourist_tax::integer, total::integer, acsi_card, currency_code::text, booking_status from booking $$,
$$ values (18, 2, 10, daterange('2024-01-18', '2024-01-29'), 'Holder 2', null, null, null, null, null, null, 'und', '', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, false, 'EUR', 'cancelled')
, (20, 2, 10, daterange('2024-01-28', '2024-01-29'), 'Holder 4', null, null, null, null, null, null, 'und', 'None', 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, true, 'USD', 'created')
$$,
'Should have updated the bookings'
);
select bag_eq (
$$ select booking_id, campsite_id, stay from booking_campsite $$,
$$ values (20, 16, daterange('2024-01-28', '2024-01-29'))
$$ ,
'Should have deleted the cancelled booking campsites'
);
select *
from finish();
rollback;

View File

@ -0,0 +1,7 @@
-- Verify camper:cancel_booking on pg
begin;
select has_function_privilege('camper.cancel_booking(integer)', 'execute');
rollback;

View File

@ -883,6 +883,11 @@ label[x-show] > span, label[x-show] > br {
grid-template-columns: repeat(4, 1fr);
}
#booking-form > footer {
display: flex;
justify-content: space-between;
}
#booking-form fieldset fieldset, #booking-form .colspan {
grid-column: span 2;
}

View File

@ -54,6 +54,10 @@
{{( pgettext "Add" "action" )}}
{{- end -}}
</button>
{{- if .ID -}}
{{ $confirm := ( gettext "Are you sure you wish to cancel this booking?" )}}
<button type="submit" onclick="return confirm('{{ $confirm }}')" name="cancel">{{( pgettext "Cancel booking" "action" )}}</button>
{{- end }}
</footer>
</form>
{{- end }}