Show booking on booking grid

I need the campsite_id in booking to know what row to show the booking
at. Besides the need of knowing which actual campsite has been booked,
of course.

This field is nullable because we can not now it until an employee has
confirmed the booking; until that point we only know the campsite type
customer requested.  I do not care much if the campsite_id is from a
different campsite_type, because maybe the customer requested the change
by phone or what have you, therefore the database can not be that
strict.  It must have a value if the booking is confirmed.

It helps me if the arrival_date and departure_date is a single
daterange, because then i can use `&&` and other range operators to work
with these dates.  For instance, i have to intersect it with the range
displayed on the screen in order to know which day i have to put it.
But then i have to know whether the booking begins and ends in the
display range, because i only have to show arrival and departure (i.e.,
the box half-way within the first or last boxes) on these days only.
This commit is contained in:
jordi fita mas 2024-04-19 21:09:28 +02:00
parent bc5fd61d5d
commit cdd91c815e
15 changed files with 314 additions and 117 deletions

View File

@ -1421,12 +1421,12 @@ values (72, daterange(make_date(extract(year from current_date)::int, 2, 4), m
, (72, daterange(make_date(extract(year from current_date)::int, 9, 1), make_date(extract(year from current_date)::int, 10, 13))) , (72, daterange(make_date(extract(year from current_date)::int, 9, 1), make_date(extract(year from current_date)::int, 10, 13)))
; ;
insert into acsi_option (campsite_type_id, campsite_type_option_id, units) insert into acsi_option (campsite_type_id, campsite_type_option_id, units, option_group)
values (72, 102, 1) values (72, 102, 1, 1)
, (72, 103, 1) , (72, 103, 1, 2)
, (72, 104, 1) , (72, 104, 1, 3)
, (72, 107, 1) , (72, 107, 1, 4)
, (72, 109, 1) , (72, 109, 1, 5)
; ;
alter table surroundings_highlight alter column surroundings_highlight_id restart with 112; alter table surroundings_highlight alter column surroundings_highlight_id restart with 112;
@ -1488,16 +1488,20 @@ select translate_surroundings_ad(52, 'fr', 'Venez faire du canyoning à Sadernes
alter table booking alter column booking_id restart with 122; alter table booking alter column booking_id restart with 122;
insert into booking (company_id, campsite_type_id, holder_name, arrival_date, departure_date, number_dogs, acsi_card, booking_status) insert into booking (company_id, campsite_type_id, campsite_id, holder_name, stay, number_dogs, acsi_card, booking_status)
values (52, 72, 'Juli Verd', current_date + interval '23 days', current_date + interval '25 days', 0, false, 'created') values (52, 72, null, 'Juli Verd', daterange((current_date + interval '23 days')::date, (current_date + interval '25 days')::date), 0, false, 'created')
, (52, 72, 'Pere Gil', current_date + interval '24 days', current_date + interval '25 days', 1, true, 'cancelled') , (52, 72, null, 'Camèlia Vermella', daterange((current_date + interval '7 days')::date, (current_date + interval '8 days')::date), 0, false, 'created')
, (52, 73, 'Calèndula Groga', current_date + interval '24 days', current_date + interval '25 days', 0, false, 'confirmed') , (52, 72, 90, 'Margarita Blanca', daterange((current_date + interval '7 days')::date, (current_date + interval '8 days')::date), 0, false, 'invoiced')
, (52, 73, 'Rosa Blava', current_date + interval '15 days', current_date + interval '22 days', 0, false, 'checked-in') , (52, 72, 90, 'Rosa Blava', daterange((current_date + interval '8 days')::date, (current_date + interval '11 days')::date), 0, false, 'checked-in')
, (52, 74, 'Margarita Blanca', current_date + interval '7 days', current_date + interval '8 days', 0, false, 'invoiced') , (52, 72, 90, 'Calèndula Groga', daterange((current_date + interval '14 days')::date, (current_date + interval '21 days')::date), 0, false, 'confirmed')
, (52, 74, 'Camèlia Vermella', current_date + interval '7 days', current_date + interval '8 days', 0, false, 'created') , (52, 72, 91, 'Jacint Violeta', daterange((current_date + interval '9 days')::date, (current_date + interval '13 days')::date), 0, false, 'checked-in')
, (52, 74, 'Valeriana Rosa', current_date + interval '3 days', current_date + interval '8 days', 0, true, 'cancelled') , (52, 72, 92, 'Hortènsia Grisa', daterange((current_date + interval '4 days')::date, (current_date + interval '8 days')::date), 0, false, 'invoiced')
, (52, 75, 'Jacint Violeta', current_date + interval '30 days', current_date + interval '33 days', 0, false, 'checked-in') , (52, 72, 93, 'Pere Gil', daterange((current_date + interval '9 days')::date, (current_date + interval '19 days')::date), 1, true, 'confirmed')
, (52, 76, 'Hortènsia Grisa', current_date + interval '29 days', current_date + interval '34 days', 0, false, 'invoiced') , (52, 72, 94, 'Juli Verd', daterange((current_date + interval '11 days')::date, (current_date + interval '13 days')::date), 0, false, 'confirmed')
, (52, 72, 94, 'Camèlia Vermella', daterange((current_date + interval '13 days')::date, (current_date + interval '15 days')::date), 0, false, 'confirmed')
, (52, 72, 94, 'Valeriana Rosa', daterange((current_date + interval '15 days')::date, (current_date + interval '17 days')::date), 0, false, 'confirmed')
, (52, 72, null, 'Pere Gil', daterange((current_date + interval '24 days')::date, (current_date + interval '25 days')::date), 1, true, 'cancelled')
, (52, 72, 83, 'Valeriana Rosa', daterange((current_date + interval '3 days')::date, (current_date + interval '8 days')::date), 0, true, 'cancelled')
; ;

View File

@ -0,0 +1,13 @@
-- Deploy camper:booking__campsite_id to pg
-- requires: booking
begin;
set search_path to camper, public;
alter table booking
add column campsite_id integer references campsite
, add constraint booking_needs_campsite check ( booking_status in ('created', 'cancelled') or campsite_id is not null )
;
commit;

24
deploy/booking__stay.sql Normal file
View File

@ -0,0 +1,24 @@
-- Deploy camper:booking__stay to pg
-- requires: booking
begin;
set search_path to camper, public;
alter table booking
add column stay daterange constraint stay_not_empty check (not isempty(stay))
;
update booking
set stay = daterange(arrival_date, departure_date)
;
alter table booking
drop column if exists arrival_date
, drop column if exists departure_date
, alter column stay set not null
;
create index stay_idx on booking using gist (stay);
commit;

View File

@ -7,7 +7,6 @@ package campsite
import ( import (
"context" "context"
"fmt"
"net/http" "net/http"
"time" "time"
@ -91,20 +90,20 @@ func (h *AdminHandler) Handler(user *auth.User, company *auth.Company, conn *dat
} }
func serveCampsiteIndex(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) { func serveCampsiteIndex(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
campsites, err := collectCampsiteEntries(r.Context(), company, conn) page := newCampsiteIndex()
if err != nil {
panic(err)
}
page := newCampsiteIndex(campsites)
if err := page.Parse(r); err != nil { if err := page.Parse(r); err != nil {
panic(err) panic(err)
} }
fmt.Println(page.From, page.To) var err error
page.Campsites, err = collectCampsiteEntries(r.Context(), company, conn, page.From.Date(), page.To.Date())
if err != nil {
panic(err)
}
page.Months = collectMonths(page.From.Date(), page.To.Date()) page.Months = collectMonths(page.From.Date(), page.To.Date())
page.MustRender(w, r, user, company) page.MustRender(w, r, user, company)
} }
func collectCampsiteEntries(ctx context.Context, company *auth.Company, conn *database.Conn) ([]*campsiteEntry, error) { func collectCampsiteEntries(ctx context.Context, company *auth.Company, conn *database.Conn, from time.Time, to time.Time) ([]*campsiteEntry, error) {
rows, err := conn.Query(ctx, ` rows, err := conn.Query(ctx, `
select campsite.label select campsite.label
, campsite_type.name , campsite_type.name
@ -118,6 +117,7 @@ func collectCampsiteEntries(ctx context.Context, company *auth.Company, conn *da
} }
defer rows.Close() defer rows.Close()
byLabel := make(map[string]*campsiteEntry)
var campsites []*campsiteEntry var campsites []*campsiteEntry
for rows.Next() { for rows.Next() {
entry := &campsiteEntry{} entry := &campsiteEntry{}
@ -125,15 +125,69 @@ func collectCampsiteEntries(ctx context.Context, company *auth.Company, conn *da
return nil, err return nil, err
} }
campsites = append(campsites, entry) campsites = append(campsites, entry)
byLabel[entry.Label] = entry
}
if err := collectBookingEntries(ctx, company, conn, from, to, byLabel); err != nil {
return nil, err
} }
return campsites, nil return campsites, nil
} }
func collectBookingEntries(ctx context.Context, company *auth.Company, conn *database.Conn, from time.Time, to time.Time, campsites map[string]*campsiteEntry) error {
lastDay := to.AddDate(0, 1, 0)
rows, err := conn.Query(ctx, `
select campsite.label
, lower(stay * daterange($2::date, $3::date))
, holder_name
, booking_status
, upper(stay * daterange($2::date, $3::date)) - lower(stay * daterange($2::date, $3::date))
, stay &> daterange($2::date, $3::date)
, stay &< daterange($2::date, $3::date)
from booking
join campsite using (campsite_id)
where booking.company_id = $1
and stay && daterange($2::date, $3::date)
and booking_status <> 'cancelled'
order by label`, company.ID, from, lastDay)
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
entry := &bookingEntry{}
var label string
var date time.Time
if err = rows.Scan(&label, &date, &entry.Holder, &entry.Status, &entry.Nights, &entry.Begin, &entry.End); err != nil {
return err
}
campsite := campsites[label]
if campsite != nil {
if campsite.Bookings == nil {
campsite.Bookings = make(map[time.Time]*bookingEntry)
}
campsite.Bookings[date] = entry
}
}
return nil
}
type campsiteEntry struct { type campsiteEntry struct {
Label string Label string
Type string Type string
Active bool Active bool
Bookings map[time.Time]*bookingEntry
}
type bookingEntry struct {
Holder string
Status string
Nights int
Begin bool
End bool
} }
type campsiteIndex struct { type campsiteIndex struct {
@ -143,12 +197,11 @@ type campsiteIndex struct {
Months []*Month Months []*Month
} }
func newCampsiteIndex(campsites []*campsiteEntry) *campsiteIndex { func newCampsiteIndex() *campsiteIndex {
now := time.Now() now := time.Now()
from := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, time.UTC) from := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, time.UTC)
to := from.AddDate(0, 3, 0) to := from.AddDate(0, 3, 0)
return &campsiteIndex{ return &campsiteIndex{
Campsites: campsites,
From: &form.Month{ From: &form.Month{
Name: "from", Name: "from",
Year: from.Year(), Year: from.Year(),

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: camper\n" "Project-Id-Version: camper\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-04-19 11:18+0200\n" "POT-Creation-Date: 2024-04-19 20:53+0200\n"
"PO-Revision-Date: 2024-02-06 10:04+0100\n" "PO-Revision-Date: 2024-02-06 10:04+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n" "Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Catalan <ca@dodds.net>\n" "Language-Team: Catalan <ca@dodds.net>\n"
@ -910,7 +910,7 @@ msgstr "Menú"
#: web/templates/admin/campsite/carousel/index.gohtml:10 #: web/templates/admin/campsite/carousel/index.gohtml:10
#: web/templates/admin/campsite/form.gohtml:15 #: web/templates/admin/campsite/form.gohtml:15
#: web/templates/admin/campsite/index.gohtml:6 #: web/templates/admin/campsite/index.gohtml:6
#: web/templates/admin/campsite/index.gohtml:15 #: web/templates/admin/campsite/index.gohtml:18
#: web/templates/admin/campsite/type/feature/form.gohtml:16 #: web/templates/admin/campsite/type/feature/form.gohtml:16
#: web/templates/admin/campsite/type/feature/index.gohtml:10 #: web/templates/admin/campsite/type/feature/index.gohtml:10
#: web/templates/admin/campsite/type/carousel/form.gohtml:16 #: web/templates/admin/campsite/type/carousel/form.gohtml:16
@ -1462,31 +1462,31 @@ msgctxt "input"
msgid "Info (Second Column)" msgid "Info (Second Column)"
msgstr "Informació (segona columna)" msgstr "Informació (segona columna)"
#: web/templates/admin/campsite/index.gohtml:14 #: web/templates/admin/campsite/index.gohtml:15
msgctxt "action" msgctxt "action"
msgid "Add Campsite" msgid "Add Campsite"
msgstr "Afegeix allotjament" msgstr "Afegeix allotjament"
#: web/templates/admin/campsite/index.gohtml:21 #: web/templates/admin/campsite/index.gohtml:32
msgid "From Date"
msgstr "De la data"
#: web/templates/admin/campsite/index.gohtml:28
msgid "To Date"
msgstr "A la data"
#: web/templates/admin/campsite/index.gohtml:35
msgctxt "action"
msgid "Show"
msgstr "Mostra"
#: web/templates/admin/campsite/index.gohtml:50
#: web/templates/admin/amenity/index.gohtml:20 #: web/templates/admin/amenity/index.gohtml:20
msgctxt "header" msgctxt "header"
msgid "Label" msgid "Label"
msgstr "Etiqueta" msgstr "Etiqueta"
#: web/templates/admin/campsite/index.gohtml:78 #: web/templates/admin/campsite/index.gohtml:78
msgid "From Date"
msgstr "De la data"
#: web/templates/admin/campsite/index.gohtml:85
msgid "To Date"
msgstr "A la data"
#: web/templates/admin/campsite/index.gohtml:92
msgctxt "action"
msgid "Show"
msgstr "Mostra"
#: web/templates/admin/campsite/index.gohtml:96
msgid "No campsites added yet." msgid "No campsites added yet."
msgstr "No sha afegit cap allotjament encara." msgstr "No sha afegit cap allotjament encara."
@ -2637,12 +2637,12 @@ msgctxt "header"
msgid "Children (aged 2 to 10)" msgid "Children (aged 2 to 10)"
msgstr "Mainada (entre 2 i 10 anys)" msgstr "Mainada (entre 2 i 10 anys)"
#: pkg/campsite/admin.go:366 pkg/booking/public.go:172 #: pkg/campsite/admin.go:419 pkg/booking/public.go:172
#: pkg/booking/public.go:227 #: pkg/booking/public.go:227
msgid "Selected campsite type is not valid." msgid "Selected campsite type is not valid."
msgstr "El tipus dallotjament escollit no és vàlid." msgstr "El tipus dallotjament escollit no és vàlid."
#: pkg/campsite/admin.go:367 pkg/amenity/admin.go:282 #: pkg/campsite/admin.go:420 pkg/amenity/admin.go:282
msgid "Label can not be empty." msgid "Label can not be empty."
msgstr "No podeu deixar letiqueta en blanc." msgstr "No podeu deixar letiqueta en blanc."

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: camper\n" "Project-Id-Version: camper\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-04-19 11:18+0200\n" "POT-Creation-Date: 2024-04-19 20:53+0200\n"
"PO-Revision-Date: 2024-02-06 10:04+0100\n" "PO-Revision-Date: 2024-02-06 10:04+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n" "Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Spanish <es@tp.org.es>\n" "Language-Team: Spanish <es@tp.org.es>\n"
@ -910,7 +910,7 @@ msgstr "Menú"
#: web/templates/admin/campsite/carousel/index.gohtml:10 #: web/templates/admin/campsite/carousel/index.gohtml:10
#: web/templates/admin/campsite/form.gohtml:15 #: web/templates/admin/campsite/form.gohtml:15
#: web/templates/admin/campsite/index.gohtml:6 #: web/templates/admin/campsite/index.gohtml:6
#: web/templates/admin/campsite/index.gohtml:15 #: web/templates/admin/campsite/index.gohtml:18
#: web/templates/admin/campsite/type/feature/form.gohtml:16 #: web/templates/admin/campsite/type/feature/form.gohtml:16
#: web/templates/admin/campsite/type/feature/index.gohtml:10 #: web/templates/admin/campsite/type/feature/index.gohtml:10
#: web/templates/admin/campsite/type/carousel/form.gohtml:16 #: web/templates/admin/campsite/type/carousel/form.gohtml:16
@ -1462,31 +1462,31 @@ msgctxt "input"
msgid "Info (Second Column)" msgid "Info (Second Column)"
msgstr "Información (segunda columna)" msgstr "Información (segunda columna)"
#: web/templates/admin/campsite/index.gohtml:14 #: web/templates/admin/campsite/index.gohtml:15
msgctxt "action" msgctxt "action"
msgid "Add Campsite" msgid "Add Campsite"
msgstr "Añadir alojamiento" msgstr "Añadir alojamiento"
#: web/templates/admin/campsite/index.gohtml:21 #: web/templates/admin/campsite/index.gohtml:32
msgid "From Date"
msgstr "De la fecha"
#: web/templates/admin/campsite/index.gohtml:28
msgid "To Date"
msgstr "A la fecha"
#: web/templates/admin/campsite/index.gohtml:35
msgctxt "action"
msgid "Show"
msgstr "Mostrar"
#: web/templates/admin/campsite/index.gohtml:50
#: web/templates/admin/amenity/index.gohtml:20 #: web/templates/admin/amenity/index.gohtml:20
msgctxt "header" msgctxt "header"
msgid "Label" msgid "Label"
msgstr "Etiqueta" msgstr "Etiqueta"
#: web/templates/admin/campsite/index.gohtml:78 #: web/templates/admin/campsite/index.gohtml:78
msgid "From Date"
msgstr "De la fecha"
#: web/templates/admin/campsite/index.gohtml:85
msgid "To Date"
msgstr "A la fecha"
#: web/templates/admin/campsite/index.gohtml:92
msgctxt "action"
msgid "Show"
msgstr "Mostrar"
#: web/templates/admin/campsite/index.gohtml:96
msgid "No campsites added yet." msgid "No campsites added yet."
msgstr "No se ha añadido ningún alojamiento todavía." msgstr "No se ha añadido ningún alojamiento todavía."
@ -2637,12 +2637,12 @@ msgctxt "header"
msgid "Children (aged 2 to 10)" msgid "Children (aged 2 to 10)"
msgstr "Niños (de 2 a 10 años)" msgstr "Niños (de 2 a 10 años)"
#: pkg/campsite/admin.go:366 pkg/booking/public.go:172 #: pkg/campsite/admin.go:419 pkg/booking/public.go:172
#: pkg/booking/public.go:227 #: pkg/booking/public.go:227
msgid "Selected campsite type is not valid." msgid "Selected campsite type is not valid."
msgstr "El tipo de alojamiento escogido no es válido." msgstr "El tipo de alojamiento escogido no es válido."
#: pkg/campsite/admin.go:367 pkg/amenity/admin.go:282 #: pkg/campsite/admin.go:420 pkg/amenity/admin.go:282
msgid "Label can not be empty." msgid "Label can not be empty."
msgstr "No podéis dejar la etiqueta en blanco." msgstr "No podéis dejar la etiqueta en blanco."

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: camper\n" "Project-Id-Version: camper\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-04-19 11:18+0200\n" "POT-Creation-Date: 2024-04-19 20:53+0200\n"
"PO-Revision-Date: 2024-02-06 10:05+0100\n" "PO-Revision-Date: 2024-02-06 10:05+0100\n"
"Last-Translator: Oriol Carbonell <info@oriolcarbonell.cat>\n" "Last-Translator: Oriol Carbonell <info@oriolcarbonell.cat>\n"
"Language-Team: French <traduc@traduc.org>\n" "Language-Team: French <traduc@traduc.org>\n"
@ -910,7 +910,7 @@ msgstr "Menu"
#: web/templates/admin/campsite/carousel/index.gohtml:10 #: web/templates/admin/campsite/carousel/index.gohtml:10
#: web/templates/admin/campsite/form.gohtml:15 #: web/templates/admin/campsite/form.gohtml:15
#: web/templates/admin/campsite/index.gohtml:6 #: web/templates/admin/campsite/index.gohtml:6
#: web/templates/admin/campsite/index.gohtml:15 #: web/templates/admin/campsite/index.gohtml:18
#: web/templates/admin/campsite/type/feature/form.gohtml:16 #: web/templates/admin/campsite/type/feature/form.gohtml:16
#: web/templates/admin/campsite/type/feature/index.gohtml:10 #: web/templates/admin/campsite/type/feature/index.gohtml:10
#: web/templates/admin/campsite/type/carousel/form.gohtml:16 #: web/templates/admin/campsite/type/carousel/form.gohtml:16
@ -1462,31 +1462,31 @@ msgctxt "input"
msgid "Info (Second Column)" msgid "Info (Second Column)"
msgstr "Info (deuxième colonne)" msgstr "Info (deuxième colonne)"
#: web/templates/admin/campsite/index.gohtml:14 #: web/templates/admin/campsite/index.gohtml:15
msgctxt "action" msgctxt "action"
msgid "Add Campsite" msgid "Add Campsite"
msgstr "Ajouter un camping" msgstr "Ajouter un camping"
#: web/templates/admin/campsite/index.gohtml:21 #: web/templates/admin/campsite/index.gohtml:32
msgid "From Date"
msgstr "Partir de la date"
#: web/templates/admin/campsite/index.gohtml:28
msgid "To Date"
msgstr "À la date"
#: web/templates/admin/campsite/index.gohtml:35
msgctxt "action"
msgid "Show"
msgstr "Montrer"
#: web/templates/admin/campsite/index.gohtml:50
#: web/templates/admin/amenity/index.gohtml:20 #: web/templates/admin/amenity/index.gohtml:20
msgctxt "header" msgctxt "header"
msgid "Label" msgid "Label"
msgstr "Label" msgstr "Label"
#: web/templates/admin/campsite/index.gohtml:78 #: web/templates/admin/campsite/index.gohtml:78
msgid "From Date"
msgstr "Partir de la date"
#: web/templates/admin/campsite/index.gohtml:85
msgid "To Date"
msgstr "À la date"
#: web/templates/admin/campsite/index.gohtml:92
msgctxt "action"
msgid "Show"
msgstr "Montrer"
#: web/templates/admin/campsite/index.gohtml:96
msgid "No campsites added yet." msgid "No campsites added yet."
msgstr "Aucun camping na encore été ajouté." msgstr "Aucun camping na encore été ajouté."
@ -2637,12 +2637,12 @@ msgctxt "header"
msgid "Children (aged 2 to 10)" msgid "Children (aged 2 to 10)"
msgstr "Enfants (de 2 à 10 anys)" msgstr "Enfants (de 2 à 10 anys)"
#: pkg/campsite/admin.go:366 pkg/booking/public.go:172 #: pkg/campsite/admin.go:419 pkg/booking/public.go:172
#: pkg/booking/public.go:227 #: pkg/booking/public.go:227
msgid "Selected campsite type is not valid." msgid "Selected campsite type is not valid."
msgstr "Le type demplacement sélectionné nest pas valide." msgstr "Le type demplacement sélectionné nest pas valide."
#: pkg/campsite/admin.go:367 pkg/amenity/admin.go:282 #: pkg/campsite/admin.go:420 pkg/amenity/admin.go:282
msgid "Label can not be empty." msgid "Label can not be empty."
msgstr "L'étiquette ne peut pas être vide." msgstr "L'étiquette ne peut pas être vide."

View File

@ -0,0 +1,9 @@
-- Revert camper:booking__campsite_id from pg
begin;
alter table camper.booking
drop column if exists campsite_id
;
commit;

23
revert/booking__stay.sql Normal file
View File

@ -0,0 +1,23 @@
-- Revert camper:booking__stay from pg
begin;
set search_path to camper, public;
alter table booking
add column arrival_date date
, add column departure_date date
;
update booking
set arrival_date = lower(stay)
, departure_date = upper(stay)
;
alter table booking
drop column if exists stay
, alter column arrival_date set not null
, alter column departure_date set not null
;
commit;

View File

@ -281,3 +281,5 @@ draft_payment [draft_payment@v6] 2024-03-20T17:11:41Z jordi fita mas <jordi@tand
acsi_option__option_group [acsi_option] 2024-04-03T07:55:37Z jordi fita mas <jordi@tandem.blog> # Add option_group column to acsi_option acsi_option__option_group [acsi_option] 2024-04-03T07:55:37Z jordi fita mas <jordi@tandem.blog> # Add option_group column to acsi_option
draft_payment [draft_payment@v7 acsi_option__option_group] 2024-04-03T08:15:40Z jordi fita mas <jordi@tandem.blog> # Take option_group in account when discounting ACSI options in draft_payment draft_payment [draft_payment@v7 acsi_option__option_group] 2024-04-03T08:15:40Z jordi fita mas <jordi@tandem.blog> # Take option_group in account when discounting ACSI options in draft_payment
booking__stay [booking] 2024-04-19T16:02:11Z jordi fita mas <jordi@tandem.blog> # Replace booking arrival and departure dates with a daterange
booking__campsite_id [booking] 2024-04-19T17:58:25Z jordi fita mas <jordi@tandem.blog> # Add campsite_id to booking

View File

@ -5,7 +5,7 @@ reset client_min_messages;
begin; begin;
select plan(79); select plan(84);
set search_path to camper, public; set search_path to camper, public;
@ -43,20 +43,22 @@ select col_type_is('booking', 'campsite_type_id', 'integer');
select col_not_null('booking', 'campsite_type_id'); select col_not_null('booking', 'campsite_type_id');
select col_hasnt_default('booking', 'campsite_type_id'); select col_hasnt_default('booking', 'campsite_type_id');
select has_column('booking', 'campsite_id');
select col_is_fk('booking', 'campsite_id');
select fk_ok('booking', 'campsite_id', 'campsite', 'campsite_id');
select col_type_is('booking', 'campsite_id', 'integer');
select col_is_null('booking', 'campsite_id');
select col_hasnt_default('booking', 'campsite_id');
select has_column('booking', 'holder_name'); select has_column('booking', 'holder_name');
select col_type_is('booking', 'holder_name', 'text'); select col_type_is('booking', 'holder_name', 'text');
select col_not_null('booking', 'holder_name'); select col_not_null('booking', 'holder_name');
select col_hasnt_default('booking', 'holder_name'); select col_hasnt_default('booking', 'holder_name');
select has_column('booking', 'arrival_date'); select has_column('booking', 'stay');
select col_type_is('booking', 'arrival_date', 'date'); select col_type_is('booking', 'stay', 'daterange');
select col_not_null('booking', 'arrival_date'); select col_not_null('booking', 'stay');
select col_hasnt_default('booking', 'arrival_date'); select col_hasnt_default('booking', 'stay');
select has_column('booking', 'departure_date');
select col_type_is('booking', 'departure_date', 'date');
select col_not_null('booking', 'departure_date');
select col_hasnt_default('booking', 'departure_date');
select has_column('booking', 'number_dogs'); select has_column('booking', 'number_dogs');
select col_type_is('booking', 'number_dogs', 'integer'); select col_type_is('booking', 'number_dogs', 'integer');
@ -130,9 +132,9 @@ values (10, 2, 'Wooden lodge', 6, 7, '[1, 7]')
, (12, 4, 'Bungalow', 8, 6, '[2, 6]') , (12, 4, 'Bungalow', 8, 6, '[2, 6]')
; ;
insert into booking (company_id, campsite_type_id, holder_name, arrival_date, departure_date, number_dogs, acsi_card) insert into booking (company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card)
values (2, 10, 'Holder 2', '2024-01-18', '2024-01-19', 0, false) values (2, 10, 'Holder 2', daterange('2024-01-18', '2024-01-19'), 0, false)
, (4, 12, 'Holder 4', '2024-01-18', '2024-01-19', 0, false) , (4, 12, 'Holder 4', daterange('2024-01-18', '2024-01-19'), 0, false)
; ;
prepare booking_data as prepare booking_data as
@ -162,7 +164,7 @@ select bag_eq(
); );
select lives_ok( select lives_ok(
$$ insert into booking(company_id, campsite_type_id, holder_name, arrival_date, departure_date, number_dogs, acsi_card) values (2, 10, 'New Holder', '2024-01-18', '2024-01-19', 0, false) $$, $$ insert into booking(company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card) values (2, 10, 'New Holder', daterange('2024-01-18', '2024-01-19'), 0, false) $$,
'Users from company 2 should be able to insert a new booking type to their company.' 'Users from company 2 should be able to insert a new booking type to their company.'
); );
@ -188,7 +190,7 @@ select bag_eq(
); );
select throws_ok( select throws_ok(
$$ insert into booking (company_id, campsite_type_id, holder_name, arrival_date, departure_date, number_dogs, acsi_card) values (4, 12, 'Another holder', '2024-01-18', '2024-01-19', 0, false) $$, $$ insert into booking (company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card) values (4, 12, 'Another holder', daterange('2024-01-18', '2024-01-19'), 0, false) $$,
'42501', 'new row violates row-level security policy for table "booking"', '42501', 'new row violates row-level security policy for table "booking"',
'Users from company 2 should NOT be able to insert new bookings to company 4.' 'Users from company 2 should NOT be able to insert new bookings to company 4.'
); );
@ -245,29 +247,47 @@ select bag_eq(
); );
select throws_ok( select throws_ok(
$$ insert into booking (company_id, campsite_type_id, holder_name, arrival_date, departure_date, number_dogs, acsi_card) values (2, 10, ' ', '2024-01-18', '2024-01-19', 0, false) $$, $$ insert into booking (company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card) values (2, 10, ' ', daterange('2024-01-18', '2024-01-19'), 0, false) $$,
'23514', 'new row for relation "booking" violates check constraint "holder_name_not_empty"', '23514', 'new row for relation "booking" violates check constraint "holder_name_not_empty"',
'Should not be able to add bookings with a blank holder name.' 'Should not be able to add bookings with a blank holder name.'
); );
select throws_ok( select throws_ok(
$$ insert into booking (company_id, campsite_type_id, holder_name, arrival_date, departure_date, number_dogs, acsi_card) values (2, 10, 'Holder', '2024-01-18', '2024-01-17', 0, false) $$, $$ insert into booking (company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card) values (2, 10, 'Holder', daterange('2024-01-18', '2024-01-18'), 0, false) $$,
'23514', 'new row for relation "booking" violates check constraint "departure_after_arrival"', '23514', 'new row for relation "booking" violates check constraint "stay_not_empty"',
'Should not be able to add bookings with a departure date before the arrival.' 'Should not be able to add bookings with an empty stay.'
); );
select throws_ok( select throws_ok(
$$ insert into booking (company_id, campsite_type_id, holder_name, arrival_date, departure_date, number_dogs, acsi_card) values (2, 10, 'Holder', '2024-01-18', '2024-01-18', 0, false) $$, $$ insert into booking (company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card) values (2, 10, 'Holder', daterange('2024-01-18', '2024-01-19'), -1, false) $$,
'23514', 'new row for relation "booking" violates check constraint "departure_after_arrival"',
'Should not be able to add bookings with a departure date equal to the arrival.'
);
select throws_ok(
$$ insert into booking (company_id, campsite_type_id, holder_name, arrival_date, departure_date, number_dogs, acsi_card) values (2, 10, 'Holder', '2024-01-18', '2024-01-19', -1, false) $$,
'23514', 'new row for relation "booking" violates check constraint "number_dogs_nonnegative"', '23514', 'new row for relation "booking" violates check constraint "number_dogs_nonnegative"',
'Should not be able to add bookings owing dogs to holder.' 'Should not be able to add bookings owing dogs to holder.'
); );
select throws_ok(
$$ insert into booking (company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card, booking_status) values (2, 10, 'Holder', daterange('2024-01-18', '2024-01-19'), 0, false, 'confirmed') $$,
'23514', 'new row for relation "booking" violates check constraint "booking_needs_campsite"',
'Should not be able to confirm bookings without a campsite.'
);
select throws_ok(
$$ insert into booking (company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card, booking_status) values (2, 10, 'Holder', daterange('2024-01-18', '2024-01-19'), 0, false, 'checked-in') $$,
'23514', 'new row for relation "booking" violates check constraint "booking_needs_campsite"',
'Should not be able to checke bookings in without a campsite.'
);
select throws_ok(
$$ insert into booking (company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card, booking_status) values (2, 10, 'Holder', daterange('2024-01-18', '2024-01-19'), 0, false, 'invoiced') $$,
'23514', 'new row for relation "booking" violates check constraint "booking_needs_campsite"',
'Should not be able to invoice bookings without a campsite.'
);
select lives_ok(
$$ insert into booking (company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card, booking_status) values (2, 10, 'Holder', daterange('2024-01-18', '2024-01-19'), 0, false, 'cancelled') $$,
'Should be able to cancel bookings even without a campsite.'
);
select * select *
from finish(); from finish();

View File

@ -0,0 +1,10 @@
-- Verify camper:booking__campsite_id on pg
begin;
select campsite_id
from camper.booking
where false
;
rollback;

10
verify/booking__stay.sql Normal file
View File

@ -0,0 +1,10 @@
-- Verify camper:booking__stay on pg
begin;
select stay
from camper.booking
where false
;
rollback;

View File

@ -808,16 +808,36 @@ label[x-show] > span, label[x-show] > br {
text-align: center; text-align: center;
} }
#campsites-booking thead { #campsites-booking thead,
#campsites-booking tbody th {
position: sticky; position: sticky;
z-index: 10;
}
#campsites-booking thead {
top: 0; top: 0;
} }
#campsites-booking tbody th { #campsites-booking tbody th {
position: sticky;
left: 0; left: 0;
} }
#campsites-booking tbody td {
position: relative;
}
#campsites-booking tbody div {
border: 1px solid;
position: absolute;
z-index: 5;
top: 0;
left: calc(2.25ch * var(--booking-begin, 0) / 2);
bottom: 0;
white-space: nowrap;
overflow: hidden;
width: calc(2.25ch * (var(--booking-nights, 1) + (var(--booking-end, 0) - var(--booking-begin, 0)) / 2) - 1px);
}
#booking-filter, #booking-filter fieldset { #booking-filter, #booking-filter fieldset {
display: flex; display: flex;
gap: 1ch; gap: 1ch;

View File

@ -43,7 +43,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{{ range .Campsites -}} {{ range $campsite := .Campsites -}}
<tr> <tr>
<th scope="row"> <th scope="row">
{{- if isAdmin -}} {{- if isAdmin -}}
@ -53,8 +53,17 @@
{{- end -}} {{- end -}}
</th> </th>
{{ range $.Months }} {{ range $.Months }}
{{ range .Days }} {{ range $day := .Days }}
<td></td> {{ with index $campsite.Bookings $day -}}
<td class="booking-{{ .Status }}">
<div class="booking-status"
style="--booking-nights: {{ .Nights }}; --booking-begin: {{ if .Begin }}1{{ else }}0{{ end }}; --booking-end: {{ if .End }}1{{ else }}0{{ end }}"
title="{{ .Holder }}"
>{{ .Holder }}</div>
</td>
{{- else -}}
<td></td>
{{- end }}
{{- end }} {{- end }}
{{- end }} {{- end }}
</tr> </tr>