Add check-in form

I based the form from the documentation given by the Mossos
d’Esquadra[0], required by law.

https://registreviatgers.mossos.gencat.cat/mossos_hotels/AppJava/fitxaviatger.do?reqCode=create
This commit is contained in:
jordi fita mas 2024-04-26 17:09:36 +02:00
parent c9e8165f83
commit f2b24a83a3
51 changed files with 2243 additions and 254 deletions

View File

@ -0,0 +1,39 @@
-- Deploy camper:available_id_document_types to pg
-- requires: id_document_type
-- requires: id_document_type_i18n
begin;
set search_path to camper;
insert into id_document_type (id_document_type_id, name)
values ('D', 'DNI')
, ('P', 'Passport')
, ('C', 'Driving license')
, ('I', 'Identification document')
, ('N', 'Spanish residence permit')
, ('X', 'Residence permit from another Member State of the European Union')
;
insert into id_document_type_i18n (id_document_type_id, lang_tag, name)
values ('D', 'ca', 'DNI')
, ('P', 'ca', 'Passaport')
, ('C', 'ca', 'Permís de conduir')
, ('I', 'ca', 'Carta o document didentitat')
, ('N', 'ca', 'Permís de residència espanyol')
, ('X', 'ca', 'Permís de residència dun altre estat membre de la Unió Europea')
, ('D', 'es', 'DNI')
, ('P', 'es', 'Pasaporte')
, ('C', 'es', 'Permiso de conducir')
, ('I', 'es', 'Carta o documento de identidad')
, ('N', 'es', 'Permiso de residencia español')
, ('X', 'es', 'Permiso de residencia de otro Estado Miembro de la Unión Europea')
, ('D', 'fr', 'DNI')
, ('P', 'fr', 'Passeport')
, ('C', 'fr', 'Permis de conduire')
, ('I', 'fr', 'Carte didentité')
, ('N', 'fr', 'Permis de séjour espagnol')
, ('X', 'fr', 'Titre de séjour dun autre État membre de lUnion européenne')
;
commit;

View File

@ -0,0 +1,23 @@
-- Deploy camper:available_sexes to pg
-- requires: sex
-- requires: sex_i18n
begin;
set search_path to camper;
insert into sex (sex_id, name)
values ('F', 'Female')
, ('M', 'Male')
;
insert into sex_i18n (sex_id, lang_tag, name)
values ('F', 'ca', 'Femení')
, ('M', 'ca', 'Masculí')
, ('F', 'es', 'Femenino')
, ('M', 'es', 'Masculino')
, ('F', 'fr', 'Féminin')
, ('M', 'fr', 'Masculin')
;
commit;

35
deploy/booking_guest.sql Normal file
View File

@ -0,0 +1,35 @@
-- Deploy camper:booking_guest to pg
-- requires: roles
-- requires: schema_camper
-- requires: booking
-- requires: sex
-- requires: id_document_type
-- requires: extension_pg_libphonenumber
begin;
set search_path to camper, public;
create table booking_guest (
booking_guest_id integer generated by default as identity primary key,
booking_id integer not null references booking,
id_document_type_id varchar(1) not null references id_document_type,
id_document_number text not null,
id_document_issue_date date,
given_name text not null,
first_surname text not null,
second_surname text not null,
sex_id varchar(1) not null references sex,
birthdate date not null,
country_code country_code not null references country,
phone packed_phone_number,
address text not null,
created_at timestamp with time zone not null default current_timestamp,
updated_at timestamp with time zone not null default current_timestamp,
unique (booking_id, id_document_type_id, id_document_number)
);
grant select, insert, update, delete on table booking_guest to employee;
grant select, insert, update, delete on table booking_guest to admin;
commit;

View File

@ -0,0 +1,75 @@
-- Deploy camper:check_in_guests to pg
-- requires: roles
-- requires: schema_camper
-- requires: booking
-- requires: booking_guest
-- requires: checked_in_guest
-- requires: extension_pg_libphonenumber
begin;
set search_path to camper, public;
create or replace function check_in_guests(bid integer, guests checked_in_guest[]) returns void as
$$
insert into booking_guest
( booking_id
, id_document_type_id
, id_document_number
, id_document_issue_date
, given_name
, first_surname
, second_surname
, sex_id
, birthdate
, country_code
, phone
, address
)
select bid
, id_document_type_id
, id_document_number
, id_document_issue_date
, given_name
, first_surname
, second_surname
, sex_id
, birthdate
, country_code
, case when phone is null or phone = '' then null else parse_packed_phone_number(phone, country_code) end
, address
from unnest(guests) as guest
on conflict (booking_id, id_document_type_id, id_document_number) do update
set id_document_type_id = excluded.id_document_type_id
, id_document_number = excluded.id_document_number
, id_document_issue_date = excluded.id_document_issue_date
, given_name = excluded.given_name
, first_surname = excluded.first_surname
, second_surname = excluded.second_surname
, sex_id = excluded.sex_id
, birthdate = excluded.birthdate
, country_code = excluded.country_code
, phone = excluded.phone
, address = excluded.address
, updated_at = current_timestamp
;
delete from booking_guest
where booking_id = bid
and updated_at < current_timestamp
;
update booking
set booking_status = 'checked-in'
where booking_id = bid
and booking_status in ('created', 'confirmed')
;
$$
language sql
;
revoke execute on function check_in_guests(integer, checked_in_guest[]) from public;
grant execute on function check_in_guests(integer, checked_in_guest[]) to employee;
grant execute on function check_in_guests(integer, checked_in_guest[]) to admin;
commit;

View File

@ -0,0 +1,22 @@
-- Deploy camper:checked_in_guest to pg
-- requires: schema_camper
begin;
set search_path to camper, public;
create type checked_in_guest as (
id_document_type_id varchar(1),
id_document_number text,
id_document_issue_date date,
given_name text,
first_surname text,
second_surname text,
sex_id varchar(1),
birthdate date,
country_code country_code,
phone text,
address text
);
commit;

View File

@ -0,0 +1,17 @@
-- Deploy camper:id_document_type to pg
-- requires: roles
-- requires: schema_camper
begin;
set search_path to camper, public;
create table id_document_type (
id_document_type_id varchar(1) primary key,
name text not null
);
grant select on table id_document_type to employee;
grant select on table id_document_type to admin;
commit;

View File

@ -0,0 +1,21 @@
-- Deploy camper:id_document_type_i18n to pg
-- requires: roles
-- requires: schema_camper
-- requires: id_document_type
-- requires: language
begin;
set search_path to camper, public;
create table id_document_type_i18n (
id_document_type_id varchar(1) not null references id_document_type,
lang_tag text not null references language,
name text not null,
primary key (id_document_type_id, lang_tag)
);
grant select on table id_document_type_i18n to employee;
grant select on table id_document_type_i18n to admin;
commit;

17
deploy/sex.sql Normal file
View File

@ -0,0 +1,17 @@
-- Deploy camper:sex to pg
-- requires: roles
-- requires: schema_camper
begin;
set search_path to camper, public;
create table sex (
sex_id varchar(1) primary key,
name text not null
);
grant select on table sex to employee;
grant select on table sex to admin;
commit;

21
deploy/sex_i18n.sql Normal file
View File

@ -0,0 +1,21 @@
-- Deploy camper:sex_i18n to pg
-- requires: roles
-- requires: schema_camper
-- requires: sex
-- requires: language
begin;
set search_path to camper, public;
create table sex_i18n (
sex_id varchar(1) not null references sex,
lang_tag text not null references language,
name text not null,
primary key (sex_id, lang_tag)
);
grant select on sex_i18n to employee;
grant select on sex_i18n to admin;
commit;

View File

@ -113,6 +113,22 @@ func (h *AdminHandler) bookingHandler(user *auth.User, company *auth.Company, co
default:
httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPut)
}
case "check-in":
switch r.Method {
case http.MethodGet:
serveCheckInForm(w, r, user, company, conn, slug)
case http.MethodPost:
checkInBooking(w, r, user, company, conn, slug)
default:
httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPost)
}
case "guest":
switch r.Method {
case http.MethodGet:
serveGuestForm(w, r, user, company, conn, slug)
default:
httplib.MethodNotAllowed(w, r, http.MethodGet)
}
default:
http.NotFound(w, r)
}

339
pkg/booking/checkin.go Normal file
View File

@ -0,0 +1,339 @@
package booking
import (
"context"
"dev.tandem.ws/tandem/camper/pkg/auth"
"dev.tandem.ws/tandem/camper/pkg/database"
"dev.tandem.ws/tandem/camper/pkg/form"
httplib "dev.tandem.ws/tandem/camper/pkg/http"
"dev.tandem.ws/tandem/camper/pkg/locale"
"dev.tandem.ws/tandem/camper/pkg/template"
"github.com/jackc/pgx/v4"
"math"
"net/http"
"time"
)
func serveCheckInForm(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, slug string) {
f := newCheckinForm(slug)
if err := f.FillFromDatabase(r.Context(), conn, user.Locale); err != nil {
panic(err)
}
f.MustRender(w, r, user, company)
}
func serveGuestForm(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, slug string) {
f, err := newGuestForm(r.Context(), conn, user.Locale, slug)
if err != nil {
panic(err)
}
f.MustRender(w, r, user, company)
}
func checkInBooking(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, slug string) {
f := newCheckinForm(slug)
if err := f.Parse(r, user, conn); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := user.VerifyCSRFToken(r); err != nil {
http.Error(w, err.Error(), http.StatusForbidden)
return
}
if ok, err := f.Valid(r.Context(), conn, user.Locale); err != nil {
panic(err)
} else if !ok {
if !httplib.IsHTMxRequest(r) {
w.WriteHeader(http.StatusUnprocessableEntity)
}
f.MustRender(w, r, user, company)
}
guests := make([]*database.CheckedInGuest, 0, len(f.Guests))
for _, g := range f.Guests {
guests = append(guests, g.checkedInGuest())
}
if err := conn.CheckInGuests(r.Context(), f.Slug, guests); err != nil {
panic(err)
}
httplib.Redirect(w, r, "/admin/bookings", http.StatusSeeOther)
}
type checkInForm struct {
Slug string
Guests []*guestForm
}
func newCheckinForm(slug string) *checkInForm {
return &checkInForm{
Slug: slug,
}
}
func (f *checkInForm) FillFromDatabase(ctx context.Context, conn *database.Conn, l *locale.Locale) error {
documentTypes := mustGetDocumentTypeOptions(ctx, conn, l)
sexes := mustGetSexOptions(ctx, conn, l)
countries := mustGetCountryOptions(ctx, conn, l)
rows, err := conn.Query(ctx, `
select array[id_document_type_id]
, id_document_number
, coalesce(id_document_issue_date::text, '')
, given_name
, first_surname
, second_surname
, array[sex_id]
, birthdate::text
, array[guest.country_code::text]
, coalesce(guest.phone::text, '')
, guest.address
from booking_guest as guest
join booking using (booking_id)
where slug = $1
`, f.Slug)
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
guest := newGuestFormWithOptions(documentTypes, sexes, countries, nil)
if err := guest.FillFromRow(rows); err != nil {
return err
}
f.Guests = append(f.Guests, guest)
}
if len(f.Guests) == 0 {
var numberGuests int
var country []string
row := conn.QueryRow(ctx, "select number_adults + number_teenagers, array[coalesce(country_code, '')] from booking where slug = $1", f.Slug)
if err = row.Scan(&numberGuests, &country); err != nil {
return err
}
guests := make([]*guestForm, 0, numberGuests)
for i := 0; i < numberGuests; i++ {
guests = append(guests, newGuestFormWithOptions(documentTypes, sexes, countries, country))
}
f.Guests = guests
}
return nil
}
func mustGetDocumentTypeOptions(ctx context.Context, conn *database.Conn, l *locale.Locale) []*form.Option {
return form.MustGetOptions(ctx, conn, "select idt.id_document_type_id::text, coalesce(i18n.name, idt.name) as l10n_name from id_document_type as idt left join id_document_type_i18n as i18n on idt.id_document_type_id = i18n.id_document_type_id and i18n.lang_tag = $1 order by l10n_name", l.Language)
}
func mustGetSexOptions(ctx context.Context, conn *database.Conn, l *locale.Locale) []*form.Option {
return form.MustGetOptions(ctx, conn, "select sex.sex_id::text, coalesce(i18n.name, sex.name) as l10n_name from sex left join sex_i18n as i18n on sex.sex_id = i18n.sex_id and i18n.lang_tag = $1 order by l10n_name", l.Language)
}
func (f *checkInForm) Parse(r *http.Request, user *auth.User, conn *database.Conn) error {
if err := r.ParseForm(); err != nil {
return err
}
documentTypes := mustGetDocumentTypeOptions(r.Context(), conn, user.Locale)
sexes := mustGetSexOptions(r.Context(), conn, user.Locale)
countries := mustGetCountryOptions(r.Context(), conn, user.Locale)
guest := newGuestFormWithOptions(documentTypes, sexes, countries, nil)
count := guest.count(r)
f.Guests = make([]*guestForm, 0, count)
guest.FillValueIndex(r, 0)
f.Guests = append(f.Guests, guest)
for i := 1; i < count; i++ {
guest = newGuestFormWithOptions(documentTypes, sexes, countries, nil)
guest.FillValueIndex(r, i)
f.Guests = append(f.Guests, guest)
}
return nil
}
func (f *checkInForm) Valid(ctx context.Context, conn *database.Conn, l *locale.Locale) (bool, error) {
allOK := true
for _, g := range f.Guests {
if ok, err := g.Valid(ctx, conn, l); err != nil {
return false, err
} else if !ok {
allOK = false
}
}
return allOK, nil
}
func (f *checkInForm) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
template.MustRenderAdminFiles(w, r, user, company, f, "booking/checkin.gohtml", "booking/guest.gohtml")
}
type guestForm struct {
IDDocumentType *form.Select
IDDocumentNumber *form.Input
IDDocumentDate *form.Input
GivenName *form.Input
FirstSurname *form.Input
SecondSurname *form.Input
Sex *form.Select
Birthdate *form.Input
Country *form.Select
Address *form.Input
Phone *form.Input
}
func newGuestForm(ctx context.Context, conn *database.Conn, l *locale.Locale, slug string) (*guestForm, error) {
documentTypes := mustGetDocumentTypeOptions(ctx, conn, l)
sexes := mustGetSexOptions(ctx, conn, l)
countries := mustGetCountryOptions(ctx, conn, l)
var country []string
row := conn.QueryRow(ctx, "select array[coalesce(country_code, '')] from booking where slug = $1", slug)
if err := row.Scan(&country); err != nil {
return nil, err
}
return newGuestFormWithOptions(documentTypes, sexes, countries, country), nil
}
func newGuestFormWithOptions(documentTypes []*form.Option, sexes []*form.Option, countries []*form.Option, selectedCountry []string) *guestForm {
return &guestForm{
IDDocumentType: &form.Select{
Name: "id_document_type",
Options: documentTypes,
},
IDDocumentNumber: &form.Input{
Name: "id_document_number",
},
IDDocumentDate: &form.Input{
Name: "id_document_date",
},
GivenName: &form.Input{
Name: "given_name",
},
FirstSurname: &form.Input{
Name: "first_surname",
},
SecondSurname: &form.Input{
Name: "second_surname",
},
Sex: &form.Select{
Name: "sex",
Options: sexes,
},
Birthdate: &form.Input{
Name: "birthdate",
},
Country: &form.Select{
Name: "country",
Options: countries,
Selected: selectedCountry,
},
Address: &form.Input{
Name: "address",
},
Phone: &form.Input{
Name: "phone",
},
}
}
func (f *guestForm) count(r *http.Request) int {
keys := []string{f.IDDocumentType.Name, f.IDDocumentNumber.Name, f.IDDocumentDate.Name, f.GivenName.Name, f.FirstSurname.Name, f.SecondSurname.Name, f.Sex.Name, f.Birthdate.Name, f.Country.Name, f.Address.Name, f.Phone.Name}
min := math.MaxInt
for _, key := range keys {
l := len(r.Form[key])
if len(r.Form[key]) < min {
min = l
}
}
return min
}
func (f *guestForm) FillValueIndex(r *http.Request, idx int) {
f.IDDocumentType.FillValueIndex(r, idx)
f.IDDocumentNumber.FillValueIndex(r, idx)
f.IDDocumentDate.FillValueIndex(r, idx)
f.GivenName.FillValueIndex(r, idx)
f.FirstSurname.FillValueIndex(r, idx)
f.SecondSurname.FillValueIndex(r, idx)
f.Sex.FillValueIndex(r, idx)
f.Birthdate.FillValueIndex(r, idx)
f.Country.FillValueIndex(r, idx)
f.Address.FillValueIndex(r, idx)
f.Phone.FillValueIndex(r, idx)
}
func (f *guestForm) FillFromRow(row pgx.Rows) error {
return row.Scan(
&f.IDDocumentType.Selected,
&f.IDDocumentNumber.Val,
&f.IDDocumentDate.Val,
&f.GivenName.Val,
&f.FirstSurname.Val,
&f.SecondSurname.Val,
&f.Sex.Selected,
&f.Birthdate.Val,
&f.Country.Selected,
&f.Phone.Val,
&f.Address.Val,
)
}
func (f *guestForm) Valid(ctx context.Context, conn *database.Conn, l *locale.Locale) (bool, error) {
v := form.NewValidator(l)
today := time.Now()
yesterday := time.Date(today.Year(), today.Month(), today.Day(), 0, 0, 0, 0, time.UTC)
v.CheckSelectedOptions(f.IDDocumentType, l.GettextNoop("Selected ID document type is not valid."))
v.CheckRequired(f.IDDocumentNumber, l.GettextNoop("ID document number can not be empty."))
if f.IDDocumentDate.Val != "" {
if v.CheckValidDate(f.IDDocumentDate, l.GettextNoop("ID document issue date must be a valid date.")) {
v.CheckMaxDate(f.IDDocumentDate, yesterday, l.Gettext("ID document issue date must be in the past."))
}
}
v.CheckRequired(f.GivenName, l.GettextNoop("Full name can not be empty."))
v.CheckRequired(f.FirstSurname, l.GettextNoop("Full name can not be empty."))
v.CheckSelectedOptions(f.Sex, l.GettextNoop("Selected sex is not valid."))
if v.CheckRequired(f.Birthdate, l.GettextNoop("Birthdate can not be empty")) {
if v.CheckValidDate(f.Birthdate, l.GettextNoop("Birthdate must be a valid date.")) {
v.CheckMaxDate(f.Birthdate, yesterday, l.Gettext("Birthdate must be in the past."))
}
}
var country string
if v.CheckSelectedOptions(f.Country, l.GettextNoop("Selected country is not valid.")) {
country = f.Country.Selected[0]
}
if f.Phone.Val != "" && country != "" {
if _, err := v.CheckValidPhone(ctx, conn, f.Phone, country, l.GettextNoop("This phone number is not valid.")); err != nil {
return false, err
}
}
return v.AllOK, nil
}
func (f *guestForm) checkedInGuest() *database.CheckedInGuest {
birthdate, err := time.Parse(database.ISODateFormat, f.Birthdate.Val)
if err != nil {
panic(err)
}
issueDate, err := time.Parse(database.ISODateFormat, f.IDDocumentDate.Val)
if err != nil {
issueDate = time.Time{}
}
return &database.CheckedInGuest{
IDDocumentType: f.IDDocumentType.String(),
IDDocumentNumber: f.IDDocumentNumber.Val,
IDDocumentIssueDate: issueDate,
GivenName: f.GivenName.Val,
FirstSurname: f.FirstSurname.Val,
SecondSurname: f.SecondSurname.Val,
Sex: f.Sex.String(),
Birthdate: birthdate,
CountryCode: f.Country.String(),
Phone: f.Phone.Val,
Address: f.Address.Val,
}
}
func (f *guestForm) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
template.MustRenderAdminNoLayout(w, r, user, company, "booking/guest.gohtml", f)
}

View File

@ -547,7 +547,7 @@ func newBookingCustomerFields(ctx context.Context, conn *database.Conn, l *local
},
Country: &form.Select{
Name: "country",
Options: form.MustGetOptions(ctx, conn, "select country.country_code, coalesce(i18n.name, country.name) as l10n_name from country left join country_i18n as i18n on country.country_code = i18n.country_code and i18n.lang_tag = $1 order by l10n_name", l.Language),
Options: mustGetCountryOptions(ctx, conn, l),
},
Email: &form.Input{
Name: "email",
@ -561,6 +561,10 @@ func newBookingCustomerFields(ctx context.Context, conn *database.Conn, l *local
}
}
func mustGetCountryOptions(ctx context.Context, conn *database.Conn, l *locale.Locale) []*form.Option {
return form.MustGetOptions(ctx, conn, "select country.country_code, coalesce(i18n.name, country.name) as l10n_name from country left join country_i18n as i18n on country.country_code = i18n.country_code and i18n.lang_tag = $1 order by l10n_name", l.Language)
}
func (f *bookingCustomerFields) FillValues(r *http.Request) {
f.FullName.FillValue(r)
f.Address.FillValue(r)

View File

@ -0,0 +1,84 @@
package database
import (
"fmt"
"time"
"github.com/jackc/pgio"
"github.com/jackc/pgtype"
)
type CheckedInGuest struct {
IDDocumentType string
IDDocumentNumber string
IDDocumentIssueDate time.Time
GivenName string
FirstSurname string
SecondSurname string
Sex string
Birthdate time.Time
CountryCode string
Phone string
Address string
}
func (src CheckedInGuest) EncodeBinary(ci *pgtype.ConnInfo, dst []byte) ([]byte, error) {
typeName := CheckedInGuestTypeName
dt, ok := ci.DataTypeForName(typeName)
if !ok {
return nil, fmt.Errorf("unable to find oid for type name %v", typeName)
}
var idDocumentIssueDate interface{}
var noDate time.Time
if src.IDDocumentIssueDate != noDate {
idDocumentIssueDate = src.IDDocumentIssueDate
}
values := []interface{}{
src.IDDocumentType,
src.IDDocumentNumber,
idDocumentIssueDate,
src.GivenName,
src.FirstSurname,
src.SecondSurname,
src.Sex,
src.Birthdate,
src.CountryCode,
src.Phone,
src.Address,
}
ct := pgtype.NewValue(dt.Value).(*pgtype.CompositeType)
if err := ct.Set(values); err != nil {
return nil, err
}
return ct.EncodeBinary(ci, dst)
}
type CheckedInGuestArray []*CheckedInGuest
func (src CheckedInGuestArray) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) {
typeName := CheckedInGuestTypeName
dt, ok := ci.DataTypeForName(typeName)
if !ok {
return nil, fmt.Errorf("unable to find oid for type name %v", typeName)
}
arrayHeader := pgtype.ArrayHeader{
ElementOID: int32(dt.OID),
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(src)), LowerBound: 1}},
}
buf = arrayHeader.EncodeBinary(ci, buf)
for _, optionUnits := range src {
sp := len(buf)
buf = pgio.AppendInt32(buf, -1)
elemBuf, err := optionUnits.EncodeBinary(ci, buf)
if err != nil {
return nil, err
}
if elemBuf != nil {
buf = elemBuf
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
}
}
return buf, nil
}

View File

@ -365,3 +365,8 @@ func (tx *Tx) EditBooking(ctx context.Context, bookingID int, customerName strin
_, err := tx.Exec(ctx, "select edit_booking($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)", bookingID, customerName, zeronull.Text(customerAddress), zeronull.Text(customerPostCode), zeronull.Text(customerCity), zeronull.Text(customerCountryCode), zeronull.Text(customerEmail), zeronull.Text(customerPhone), customerLangTag, bookingStatus, campsiteIDs)
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

@ -13,6 +13,7 @@ import (
)
const (
CheckedInGuestTypeName = "checked_in_guest"
OptionUnitsTypeName = "option_units"
RedsysRequestTypeName = "redsys_request"
RedsysResponseTypeName = "redsys_response"
@ -125,6 +126,30 @@ func registerConnectionTypes(ctx context.Context, conn *pgx.Conn) error {
return err
}
checkedInGuestType, err := pgtype.NewCompositeType(
CheckedInGuestTypeName,
[]pgtype.CompositeTypeField{
{"id_document_type_id", pgtype.VarcharOID},
{"id_document_number", pgtype.TextOID},
{"id_document_issue_date", pgtype.DateOID},
{"given_name", pgtype.TextOID},
{"first_surname", pgtype.TextOID},
{"second_surname", pgtype.TextOID},
{"sex_id", pgtype.VarcharOID},
{"birthdate", pgtype.DateOID},
{"country_code", pgtype.TextOID},
{"phone", pgtype.TextOID},
{"address", pgtype.TextOID},
},
conn.ConnInfo(),
)
if err != nil {
return err
}
if _, err = registerType(ctx, conn, checkedInGuestType, checkedInGuestType.TypeName()); err != nil {
return err
}
return nil
}

View File

@ -28,8 +28,17 @@ func (input *Input) setError(err error) {
}
func (input *Input) FillValue(r *http.Request) {
input.Val = strings.TrimSpace(r.FormValue(input.Name))
input.FillValueIndex(r, 0)
}
func (input *Input) FillValueIndex(r *http.Request, idx int) {
var val string
if vs := r.Form[input.Name]; len(vs) > idx {
val = vs[idx]
}
input.Val = strings.TrimSpace(val)
}
func (input *Input) Value() (driver.Value, error) {
return input.Val, nil
}

View File

@ -48,7 +48,18 @@ func (s *Select) FillValue(r *http.Request) {
s.Selected = r.Form[s.Name]
}
func (s *Select) FillValueIndex(r *http.Request, idx int) {
if vs := r.Form[s.Name]; len(vs) > idx {
s.Selected = []string{vs[idx]}
} else {
s.Selected = nil
}
}
func (s *Select) ValidOptionsSelected() bool {
if len(s.Selected) == 0 {
return false
}
for _, selected := range s.Selected {
if !s.isValidOption(selected) {
return false

View File

@ -117,6 +117,9 @@ func mustRenderLayout(w io.Writer, user *auth.User, company *auth.Company, templ
"formatDateAttr": func(time time.Time) string {
return time.Format(database.ISODateFormat)
},
"today": func() string {
return time.Now().Format(database.ISODateFormat)
},
"queryEscape": func(s string) string {
return url.QueryEscape(s)
},

291
po/ca.po
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-04-24 19:59+0200\n"
"POT-Creation-Date: 2024-04-26 16:53+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"
@ -116,7 +116,7 @@ msgstr "Reserva"
#: web/templates/mail/payment/details.gotxt:16
#: web/templates/public/booking/fields.gohtml:14
#: web/templates/admin/payment/details.gohtml:74
#: web/templates/admin/booking/fields.gohtml:12
#: web/templates/admin/booking/fields.gohtml:13
msgctxt "title"
msgid "Accommodation"
msgstr "Allotjament"
@ -155,7 +155,7 @@ msgstr "No"
#: web/templates/public/campsite/dates.gohtml:4
#: web/templates/public/booking/fields.gohtml:30
#: web/templates/admin/payment/details.gohtml:86
#: web/templates/admin/booking/fields.gohtml:31
#: web/templates/admin/booking/fields.gohtml:32
msgctxt "input"
msgid "Arrival date"
msgstr "Data darribada"
@ -164,7 +164,7 @@ msgstr "Data darribada"
#: web/templates/public/campsite/dates.gohtml:15
#: web/templates/public/booking/fields.gohtml:41
#: web/templates/admin/payment/details.gohtml:90
#: web/templates/admin/booking/fields.gohtml:42
#: web/templates/admin/booking/fields.gohtml:43
msgctxt "input"
msgid "Departure date"
msgstr "Data de sortida"
@ -178,7 +178,7 @@ msgstr "Nits"
#: web/templates/mail/payment/details.gotxt:22
#: web/templates/public/booking/fields.gohtml:60
#: web/templates/admin/payment/details.gohtml:98
#: web/templates/admin/booking/fields.gohtml:92
#: web/templates/admin/booking/fields.gohtml:93
msgctxt "input"
msgid "Adults aged 17 or older"
msgstr "Adults de 17 anys o més"
@ -186,7 +186,7 @@ msgstr "Adults de 17 anys o més"
#: web/templates/mail/payment/details.gotxt:23
#: web/templates/public/booking/fields.gohtml:71
#: web/templates/admin/payment/details.gohtml:102
#: web/templates/admin/booking/fields.gohtml:108
#: web/templates/admin/booking/fields.gohtml:109
msgctxt "input"
msgid "Teenagers from 11 to 16 years old"
msgstr "Adolescents dentre 11 i 16 anys"
@ -194,7 +194,7 @@ msgstr "Adolescents dentre 11 i 16 anys"
#: web/templates/mail/payment/details.gotxt:24
#: web/templates/public/booking/fields.gohtml:82
#: web/templates/admin/payment/details.gohtml:106
#: web/templates/admin/booking/fields.gohtml:124
#: web/templates/admin/booking/fields.gohtml:125
msgctxt "input"
msgid "Children from 2 to 10 years old"
msgstr "Nens dentre 2 i 10 anys"
@ -202,14 +202,14 @@ msgstr "Nens dentre 2 i 10 anys"
#: web/templates/mail/payment/details.gotxt:25
#: web/templates/public/booking/fields.gohtml:100
#: web/templates/admin/payment/details.gohtml:110
#: web/templates/admin/booking/fields.gohtml:139
#: web/templates/admin/booking/fields.gohtml:140
msgctxt "input"
msgid "Dogs"
msgstr "Gossos"
#: web/templates/mail/payment/details.gotxt:26
#: web/templates/admin/payment/details.gohtml:114
#: web/templates/admin/booking/fields.gohtml:166 pkg/booking/cart.go:244
#: web/templates/admin/booking/fields.gohtml:167 pkg/booking/cart.go:242
msgctxt "cart"
msgid "Tourist tax"
msgstr "Impost turístic"
@ -239,7 +239,7 @@ msgstr "Opcions del tipus dallotjament"
#: web/templates/mail/payment/details.gotxt:39
#: web/templates/public/booking/fields.gohtml:146
#: web/templates/admin/payment/details.gohtml:140
#: web/templates/admin/booking/fields.gohtml:187
#: web/templates/admin/booking/fields.gohtml:188
msgctxt "title"
msgid "Customer Details"
msgstr "Detalls del client"
@ -247,7 +247,7 @@ msgstr "Detalls del client"
#: web/templates/mail/payment/details.gotxt:41
#: web/templates/public/booking/fields.gohtml:149
#: web/templates/admin/payment/details.gohtml:143
#: web/templates/admin/booking/fields.gohtml:190
#: web/templates/admin/booking/fields.gohtml:191
msgctxt "input"
msgid "Full name"
msgstr "Nom i cognoms"
@ -930,7 +930,7 @@ msgstr "Menú"
#: web/templates/admin/campsite/type/option/index.gohtml:10
#: web/templates/admin/campsite/type/index.gohtml:10
#: web/templates/admin/layout.gohtml:46 web/templates/admin/layout.gohtml:95
#: web/templates/admin/booking/fields.gohtml:265
#: web/templates/admin/booking/fields.gohtml:266
msgctxt "title"
msgid "Campsites"
msgstr "Allotjaments"
@ -978,6 +978,7 @@ msgid "Booking Period"
msgstr "Període de reserva"
#: web/templates/public/booking/fields.gohtml:56
#: web/templates/admin/booking/checkin.gohtml:20
msgctxt "title"
msgid "Guests"
msgstr "Hostes"
@ -987,18 +988,18 @@ msgid "Note: Due to guest capacity, we have added more accommodations to the boo
msgstr "Nota: Shan afegit més allotjaments a la reserva degut a la capacitat de cadascuna, però <strong>no</strong> es garanteix que estiguin de costat."
#: web/templates/public/booking/fields.gohtml:109
#: web/templates/admin/booking/fields.gohtml:178
#: web/templates/admin/booking/fields.gohtml:179
msgid "Note: This accommodation does <strong>not</strong> allow dogs."
msgstr "Nota: A aquest allotjament <strong>no</strong> shi permeten gossos."
#: web/templates/public/booking/fields.gohtml:121
#: web/templates/admin/booking/fields.gohtml:55
#: web/templates/admin/booking/fields.gohtml:56
msgctxt "input"
msgid "Area preferences (optional)"
msgstr "Preferències dàrea (opcional)"
#: web/templates/public/booking/fields.gohtml:123
#: web/templates/admin/booking/fields.gohtml:59
#: web/templates/admin/booking/fields.gohtml:60
msgid "Campground map"
msgstr "Mapa del càmping"
@ -1008,12 +1009,13 @@ msgid "Town or village"
msgstr "Població"
#: web/templates/public/booking/fields.gohtml:193
#: web/templates/admin/booking/fields.gohtml:203
#: web/templates/admin/booking/fields.gohtml:204
#: web/templates/admin/booking/guest.gohtml:109
msgid "Choose a country"
msgstr "Esculli un país"
#: web/templates/public/booking/fields.gohtml:247
#: web/templates/admin/booking/fields.gohtml:258
#: web/templates/admin/booking/fields.gohtml:259
msgctxt "input"
msgid "ACSI card? (optional)"
msgstr "Targeta ACSI? (opcional)"
@ -1098,8 +1100,8 @@ msgid "Down payment"
msgstr "A compte"
#: web/templates/admin/payment/index.gohtml:24
#: web/templates/admin/booking/fields.gohtml:74
#: web/templates/admin/booking/fields.gohtml:172
#: web/templates/admin/booking/fields.gohtml:75
#: web/templates/admin/booking/fields.gohtml:173
msgctxt "header"
msgid "Total"
msgstr "Total"
@ -1194,7 +1196,7 @@ msgstr "Contingut"
#: web/templates/admin/amenity/form.gohtml:91
#: web/templates/admin/home/index.gohtml:34
#: web/templates/admin/media/form.gohtml:39
#: web/templates/admin/booking/fields.gohtml:272
#: web/templates/admin/booking/form.gohtml:45
msgctxt "action"
msgid "Update"
msgstr "Actualitza"
@ -1214,7 +1216,7 @@ msgstr "Actualitza"
#: web/templates/admin/amenity/feature/form.gohtml:67
#: web/templates/admin/amenity/carousel/form.gohtml:52
#: web/templates/admin/amenity/form.gohtml:93
#: web/templates/admin/booking/fields.gohtml:274
#: web/templates/admin/booking/form.gohtml:47
msgctxt "action"
msgid "Add"
msgstr "Afegeix"
@ -2161,7 +2163,7 @@ msgid "Add Amenity"
msgstr "Afegeix instaŀlació"
#: web/templates/admin/amenity/index.gohtml:20
#: web/templates/admin/booking/grid.gohtml:13
#: web/templates/admin/booking/grid.gohtml:15
msgctxt "header"
msgid "Label"
msgstr "Etiqueta"
@ -2204,7 +2206,8 @@ msgid "Logout"
msgstr "Surt"
#: web/templates/admin/layout.gohtml:92
#: web/templates/admin/booking/form.gohtml:15
#: web/templates/admin/booking/form.gohtml:19
#: web/templates/admin/booking/checkin.gohtml:10
#: web/templates/admin/booking/index.gohtml:6
#: web/templates/admin/booking/index.gohtml:16
msgctxt "title"
@ -2312,51 +2315,53 @@ msgctxt "title"
msgid "Upload Media"
msgstr "Pujada de mèdia"
#: web/templates/admin/booking/fields.gohtml:18
#: web/templates/admin/booking/fields.gohtml:19
msgid "Choose an accommodation"
msgstr "Esculliu un allotjament"
#: web/templates/admin/booking/fields.gohtml:72
#: web/templates/admin/booking/fields.gohtml:73
msgctxt "header"
msgid "Units"
msgstr "Unitats"
#: web/templates/admin/booking/fields.gohtml:73
#: web/templates/admin/booking/fields.gohtml:74
msgctxt "header"
msgid "Decription"
msgstr "Descripció"
#: web/templates/admin/booking/fields.gohtml:80 pkg/booking/cart.go:234
#: web/templates/admin/booking/fields.gohtml:81 pkg/booking/cart.go:232
msgctxt "cart"
msgid "Night"
msgstr "Nit"
#: web/templates/admin/booking/fields.gohtml:199
#: web/templates/admin/booking/fields.gohtml:200
msgctxt "input"
msgid "Country (optional)"
msgstr "País (opcional)"
#: web/templates/admin/booking/fields.gohtml:211
#: web/templates/admin/booking/fields.gohtml:212
#: web/templates/admin/booking/guest.gohtml:128
msgctxt "input"
msgid "Address (optional)"
msgstr "Adreça (opcional)"
#: web/templates/admin/booking/fields.gohtml:220
#: web/templates/admin/booking/fields.gohtml:221
msgctxt "input"
msgid "Postcode (optional)"
msgstr "Codi postal (opcional)"
#: web/templates/admin/booking/fields.gohtml:229
#: web/templates/admin/booking/fields.gohtml:230
msgctxt "input"
msgid "Town or village (optional)"
msgstr "Població (opcional)"
#: web/templates/admin/booking/fields.gohtml:238
#: web/templates/admin/booking/fields.gohtml:239
msgctxt "input"
msgid "Email (optional)"
msgstr "Correu-e (opcional)"
#: web/templates/admin/booking/fields.gohtml:247
#: web/templates/admin/booking/fields.gohtml:248
#: web/templates/admin/booking/guest.gohtml:117
msgctxt "input"
msgid "Phone (optional)"
msgstr "Telèfon (opcional)"
@ -2371,6 +2376,31 @@ msgctxt "title"
msgid "New Booking"
msgstr "Nova reserva"
#: web/templates/admin/booking/form.gohtml:25
msgctxt "action"
msgid "Check-in Booking"
msgstr "Registra la reserva"
#: web/templates/admin/booking/checkin.gohtml:6
msgctxt "title"
msgid "Check-in Booking"
msgstr "Registre de la reserva"
#: web/templates/admin/booking/checkin.gohtml:15
msgctxt "action"
msgid "Edit Booking"
msgstr "Edita la reserva"
#: web/templates/admin/booking/checkin.gohtml:26
msgctxt "action"
msgid "Check-in"
msgstr "Registra"
#: web/templates/admin/booking/checkin.gohtml:31
msgctxt "action"
msgid "Add another guest"
msgstr "Afegeix un altre hoste"
#: web/templates/admin/booking/index.gohtml:14
msgctxt "action"
msgid "Add Booking"
@ -2400,6 +2430,64 @@ msgstr "Nom del titular"
msgid "No booking found."
msgstr "No sha trobat cap reserva."
#: web/templates/admin/booking/guest.gohtml:5
msgctxt "action"
msgid "Remove"
msgstr "Esborra"
#: web/templates/admin/booking/guest.gohtml:8
msgctxt "input"
msgid "ID document number"
msgstr "Número de document didentitat"
#: web/templates/admin/booking/guest.gohtml:20
msgctxt "input"
msgid "ID document type"
msgstr "Tipus de document"
#: web/templates/admin/booking/guest.gohtml:25
msgid "Choose an ID document type"
msgstr "Esculli un tipus de document"
#: web/templates/admin/booking/guest.gohtml:33
msgctxt "input"
msgid "ID document issue date (if any)"
msgstr "Data dexpedició (si hi consta)"
#: web/templates/admin/booking/guest.gohtml:44
msgctxt "input"
msgid "First surname"
msgstr "Primer cognom"
#: web/templates/admin/booking/guest.gohtml:56
msgctxt "input"
msgid "Second surname (if has one)"
msgstr "Segon cognom (si en té)"
#: web/templates/admin/booking/guest.gohtml:67
msgctxt "input"
msgid "Given name"
msgstr "Nom"
#: web/templates/admin/booking/guest.gohtml:79
msgctxt "input"
msgid "Sex"
msgstr "Sexe"
#: web/templates/admin/booking/guest.gohtml:84
msgid "Choose a sex"
msgstr "Esculli un sexe"
#: web/templates/admin/booking/guest.gohtml:92
msgctxt "input"
msgid "Birthdate"
msgstr "Data de naixement"
#: web/templates/admin/booking/guest.gohtml:104
msgctxt "input"
msgid "Nationality"
msgstr "Nacionalitat"
#: pkg/payment/settings.go:37
msgctxt "redsys environment"
msgid "Test"
@ -2501,12 +2589,12 @@ msgid "Slide image must be an image media type."
msgstr "La imatge de la diapositiva ha de ser un mèdia de tipus imatge."
#: pkg/app/login.go:56 pkg/app/user.go:246 pkg/company/admin.go:224
#: pkg/booking/public.go:545
#: pkg/booking/public.go:596
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/company/admin.go:225
#: pkg/booking/admin.go:316 pkg/booking/public.go:546
#: pkg/booking/admin.go:437 pkg/booking/public.go:597
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."
@ -2717,8 +2805,8 @@ msgctxt "header"
msgid "Children (aged 2 to 10)"
msgstr "Mainada (entre 2 i 10 anys)"
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:292 pkg/booking/public.go:173
#: pkg/booking/public.go:228
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:413 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."
@ -2814,7 +2902,8 @@ msgstr "No podeu deixar ladreça de lenllaç en blanc."
msgid "This web address is not valid. It should be like https://domain.com/."
msgstr "Aquesta adreça web no és vàlida. Hauria de ser similar a https://domini.com/."
#: pkg/company/admin.go:207 pkg/booking/public.go:530
#: pkg/company/admin.go:207 pkg/booking/checkin.go:301
#: pkg/booking/public.go:581
msgid "Selected country is not valid."
msgstr "El país escollit no és vàlid."
@ -2834,15 +2923,16 @@ msgstr "No podeu deixar el NIF en blanc."
msgid "This VAT number is not valid."
msgstr "Aquest NIF no és vàlid."
#: pkg/company/admin.go:219 pkg/booking/public.go:548
#: pkg/company/admin.go:219 pkg/booking/public.go:599
msgid "Phone can not be empty."
msgstr "No podeu deixar el telèfon en blanc."
#: pkg/company/admin.go:220 pkg/booking/admin.go:321 pkg/booking/public.go:549
#: pkg/company/admin.go:220 pkg/booking/checkin.go:305 pkg/booking/admin.go:442
#: pkg/booking/public.go:600
msgid "This phone number is not valid."
msgstr "Aquest número de telèfon no és vàlid."
#: pkg/company/admin.go:230 pkg/booking/public.go:538
#: pkg/company/admin.go:230 pkg/booking/public.go:589
msgid "Address can not be empty."
msgstr "No podeu deixar ladreça en blanc."
@ -2854,11 +2944,11 @@ msgstr "No podeu deixar la població en blanc."
msgid "Province can not be empty."
msgstr "No podeu deixar la província en blanc."
#: pkg/company/admin.go:233 pkg/booking/public.go:540
#: pkg/company/admin.go:233 pkg/booking/public.go:591
msgid "Postcode can not be empty."
msgstr "No podeu deixar el codi postal en blanc."
#: pkg/company/admin.go:234 pkg/booking/admin.go:311 pkg/booking/public.go:541
#: pkg/company/admin.go:234 pkg/booking/admin.go:432 pkg/booking/public.go:592
msgid "This postcode is not valid."
msgstr "Aquest codi postal no és vàlid."
@ -2910,169 +3000,202 @@ msgstr "No podeu deixar el fitxer del mèdia en blanc."
msgid "Filename can not be empty."
msgstr "No podeu deixar el nom del fitxer en blanc."
#: pkg/booking/cart.go:235
#: pkg/booking/checkin.go:285
msgid "Selected ID document type is not valid."
msgstr "El tipus de document didentitat escollit no és vàlid."
#: pkg/booking/checkin.go:286
msgid "ID document number can not be empty."
msgstr "No podeu deixar el número document didentitat en blanc."
#: pkg/booking/checkin.go:288
msgid "ID document issue date must be a valid date."
msgstr "La data dexpedició del document didentitat ha de ser una data vàlida."
#: pkg/booking/checkin.go:289
msgid "ID document issue date must be in the past."
msgstr "La data dexpedició del document didentitat ha de ser al passat."
#: pkg/booking/checkin.go:292 pkg/booking/checkin.go:293
#: pkg/booking/admin.go:425 pkg/booking/public.go:585
msgid "Full name can not be empty."
msgstr "No podeu deixar el nom i els cognoms en blanc."
#: pkg/booking/checkin.go:294
msgid "Selected sex is not valid."
msgstr "El sexe escollit no és vàlid."
#: pkg/booking/checkin.go:295
msgid "Birthdate can not be empty"
msgstr "No podeu deixar la data de naixement en blanc."
#: pkg/booking/checkin.go:296
msgid "Birthdate must be a valid date."
msgstr "La data de naixement ha de ser una data vàlida."
#: pkg/booking/checkin.go:297
msgid "Birthdate must be in the past."
msgstr "La data de naixement ha de ser al passat."
#: pkg/booking/cart.go:233
msgctxt "cart"
msgid "Adult"
msgstr "Adult"
#: pkg/booking/cart.go:236
#: pkg/booking/cart.go:234
msgctxt "cart"
msgid "Teenager"
msgstr "Adolescent"
#: pkg/booking/cart.go:237
#: pkg/booking/cart.go:235
msgctxt "cart"
msgid "Child"
msgstr "Nen"
#: pkg/booking/cart.go:238
#: pkg/booking/cart.go:236
msgctxt "cart"
msgid "Dog"
msgstr "Gos"
#: pkg/booking/admin.go:144
#: pkg/booking/admin.go:217
msgctxt "filename"
msgid "bookings.ods"
msgstr "reserves.ods"
#: pkg/booking/admin.go:304 pkg/booking/public.go:534
msgid "Full name can not be empty."
msgstr "No podeu deixar el nom i els cognoms en blanc."
#: pkg/booking/admin.go:305 pkg/booking/public.go:535
#: pkg/booking/admin.go:426 pkg/booking/public.go:586
msgid "Full name must have at least one letter."
msgstr "El nom i els cognoms han de tenir com a mínim una lletra."
#: pkg/booking/admin.go:310
#: pkg/booking/admin.go:431
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:320
#: pkg/booking/admin.go:441
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:327
#: pkg/booking/admin.go:448
msgid "You must select at least one accommodation."
msgstr "Heu descollir com a mínim un allotjament."
#: pkg/booking/admin.go:333
#: pkg/booking/admin.go:454
msgid "The selected accommodations have no available openings in the requested dates."
msgstr "Els allotjaments escollits no estan disponibles a les dates demanades."
#: pkg/booking/public.go:277 pkg/booking/public.go:306
#: pkg/booking/public.go:284 pkg/booking/public.go:313
msgid "Arrival date must be a valid date."
msgstr "La data darribada ha de ser una data vàlida."
#: pkg/booking/public.go:291 pkg/booking/public.go:313
#: pkg/booking/public.go:298 pkg/booking/public.go:320
msgid "Departure date must be a valid date."
msgstr "La data de sortida ha de ser una data vàlida."
#: pkg/booking/public.go:305
#: pkg/booking/public.go:312
msgid "Arrival date can not be empty"
msgstr "No podeu deixar la data darribada en blanc."
#: pkg/booking/public.go:307
#: pkg/booking/public.go:314
#, c-format
msgid "Arrival date must be %s or after."
msgstr "La data darribada ha de ser igual o posterior a %s."
#: pkg/booking/public.go:308
#: pkg/booking/public.go:315
#, c-format
msgid "Arrival date must be %s or before."
msgstr "La data darribada ha de ser anterior o igual a %s."
#: pkg/booking/public.go:312
#: pkg/booking/public.go:319
msgid "Departure date can not be empty"
msgstr "No podeu deixar la data de sortida en blanc."
#: pkg/booking/public.go:314
#: pkg/booking/public.go:321
#, c-format
msgid "Departure date must be %s or after."
msgstr "La data de sortida ha de ser igual o posterior a %s."
#: pkg/booking/public.go:315
#: pkg/booking/public.go:322
#, c-format
msgid "Departure date must be %s or before."
msgstr "La data de sortida ha de ser anterior o igual a %s."
#: pkg/booking/public.go:369
#: pkg/booking/public.go:380
#, c-format
msgid "There can be at most %d guests in this accommodation."
msgstr "Hi poden haver com a màxim %d convidats a aquest allotjament."
#: pkg/booking/public.go:389
#: pkg/booking/public.go:400
msgid "Number of adults can not be empty"
msgstr "No podeu deixar el número dadults en blanc."
#: pkg/booking/public.go:390
#: pkg/booking/public.go:401
msgid "Number of adults must be an integer."
msgstr "El número dadults ha de ser enter."
#: pkg/booking/public.go:391
#: pkg/booking/public.go:402
msgid "There must be at least one adult."
msgstr "Hi ha dhaver com a mínim un adult."
#: pkg/booking/public.go:394
#: pkg/booking/public.go:405
msgid "Number of teenagers can not be empty"
msgstr "No podeu deixar el número dadolescents en blanc."
#: pkg/booking/public.go:395
#: pkg/booking/public.go:406
msgid "Number of teenagers must be an integer."
msgstr "El número dadolescents ha de ser enter."
#: pkg/booking/public.go:396
#: pkg/booking/public.go:407
msgid "Number of teenagers can not be negative."
msgstr "El número dadolescents no pot ser negatiu."
#: pkg/booking/public.go:399
#: pkg/booking/public.go:410
msgid "Number of children can not be empty"
msgstr "No podeu deixar el número de nens en blanc."
#: pkg/booking/public.go:400
#: pkg/booking/public.go:411
msgid "Number of children must be an integer."
msgstr "El número de nens ha de ser enter."
#: pkg/booking/public.go:401
#: pkg/booking/public.go:412
msgid "Number of children can not be negative."
msgstr "El número de nens no pot ser negatiu."
#: pkg/booking/public.go:404
#: pkg/booking/public.go:415
msgid "Number of dogs can not be empty"
msgstr "No podeu deixar el número de gossos en blanc."
#: pkg/booking/public.go:405
#: pkg/booking/public.go:416
msgid "Number of dogs must be an integer."
msgstr "El número de gossos ha de ser enter."
#: pkg/booking/public.go:406
#: pkg/booking/public.go:417
msgid "Number of dogs can not be negative."
msgstr "El número de gossos no pot ser negatiu."
#: pkg/booking/public.go:477
#: pkg/booking/public.go:524
#, c-format
msgid "%s can not be empty"
msgstr "No podeu deixar %s en blanc."
#: pkg/booking/public.go:478
#: pkg/booking/public.go:525
#, c-format
msgid "%s must be an integer."
msgstr "%s ha de ser un número enter."
#: pkg/booking/public.go:479
#: pkg/booking/public.go:526
#, c-format
msgid "%s must be %d or greater."
msgstr "El valor de %s ha de ser com a mínim %d."
#: pkg/booking/public.go:480
#: pkg/booking/public.go:527
#, c-format
msgid "%s must be at most %d."
msgstr "El valor de %s ha de ser com a màxim %d."
#: pkg/booking/public.go:539
#: pkg/booking/public.go:590
msgid "Town or village can not be empty."
msgstr "No podeu deixar la població en blanc."
#: pkg/booking/public.go:554
#: pkg/booking/public.go:605
msgid "It is mandatory to agree to the reservation conditions."
msgstr "És obligatori acceptar les condicions de reserves."

291
po/es.po
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-04-24 19:59+0200\n"
"POT-Creation-Date: 2024-04-26 16:53+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"
@ -116,7 +116,7 @@ msgstr "Reserva"
#: web/templates/mail/payment/details.gotxt:16
#: web/templates/public/booking/fields.gohtml:14
#: web/templates/admin/payment/details.gohtml:74
#: web/templates/admin/booking/fields.gohtml:12
#: web/templates/admin/booking/fields.gohtml:13
msgctxt "title"
msgid "Accommodation"
msgstr "Alojamientos"
@ -155,7 +155,7 @@ msgstr "No"
#: web/templates/public/campsite/dates.gohtml:4
#: web/templates/public/booking/fields.gohtml:30
#: web/templates/admin/payment/details.gohtml:86
#: web/templates/admin/booking/fields.gohtml:31
#: web/templates/admin/booking/fields.gohtml:32
msgctxt "input"
msgid "Arrival date"
msgstr "Fecha de llegada"
@ -164,7 +164,7 @@ msgstr "Fecha de llegada"
#: web/templates/public/campsite/dates.gohtml:15
#: web/templates/public/booking/fields.gohtml:41
#: web/templates/admin/payment/details.gohtml:90
#: web/templates/admin/booking/fields.gohtml:42
#: web/templates/admin/booking/fields.gohtml:43
msgctxt "input"
msgid "Departure date"
msgstr "Fecha de salida"
@ -178,7 +178,7 @@ msgstr "Noches"
#: web/templates/mail/payment/details.gotxt:22
#: web/templates/public/booking/fields.gohtml:60
#: web/templates/admin/payment/details.gohtml:98
#: web/templates/admin/booking/fields.gohtml:92
#: web/templates/admin/booking/fields.gohtml:93
msgctxt "input"
msgid "Adults aged 17 or older"
msgstr "Adultos de 17 años o más"
@ -186,7 +186,7 @@ msgstr "Adultos de 17 años o más"
#: web/templates/mail/payment/details.gotxt:23
#: web/templates/public/booking/fields.gohtml:71
#: web/templates/admin/payment/details.gohtml:102
#: web/templates/admin/booking/fields.gohtml:108
#: web/templates/admin/booking/fields.gohtml:109
msgctxt "input"
msgid "Teenagers from 11 to 16 years old"
msgstr "Adolescentes de 11 a 16 años"
@ -194,7 +194,7 @@ msgstr "Adolescentes de 11 a 16 años"
#: web/templates/mail/payment/details.gotxt:24
#: web/templates/public/booking/fields.gohtml:82
#: web/templates/admin/payment/details.gohtml:106
#: web/templates/admin/booking/fields.gohtml:124
#: web/templates/admin/booking/fields.gohtml:125
msgctxt "input"
msgid "Children from 2 to 10 years old"
msgstr "Niños de 2 a 10 años"
@ -202,14 +202,14 @@ msgstr "Niños de 2 a 10 años"
#: web/templates/mail/payment/details.gotxt:25
#: web/templates/public/booking/fields.gohtml:100
#: web/templates/admin/payment/details.gohtml:110
#: web/templates/admin/booking/fields.gohtml:139
#: web/templates/admin/booking/fields.gohtml:140
msgctxt "input"
msgid "Dogs"
msgstr "Perros"
#: web/templates/mail/payment/details.gotxt:26
#: web/templates/admin/payment/details.gohtml:114
#: web/templates/admin/booking/fields.gohtml:166 pkg/booking/cart.go:244
#: web/templates/admin/booking/fields.gohtml:167 pkg/booking/cart.go:242
msgctxt "cart"
msgid "Tourist tax"
msgstr "Impuesto turístico"
@ -239,7 +239,7 @@ msgstr "Opciones del tipo de alojamiento"
#: web/templates/mail/payment/details.gotxt:39
#: web/templates/public/booking/fields.gohtml:146
#: web/templates/admin/payment/details.gohtml:140
#: web/templates/admin/booking/fields.gohtml:187
#: web/templates/admin/booking/fields.gohtml:188
msgctxt "title"
msgid "Customer Details"
msgstr "Detalles del cliente"
@ -247,7 +247,7 @@ msgstr "Detalles del cliente"
#: web/templates/mail/payment/details.gotxt:41
#: web/templates/public/booking/fields.gohtml:149
#: web/templates/admin/payment/details.gohtml:143
#: web/templates/admin/booking/fields.gohtml:190
#: web/templates/admin/booking/fields.gohtml:191
msgctxt "input"
msgid "Full name"
msgstr "Nombre y apellidos"
@ -930,7 +930,7 @@ msgstr "Menú"
#: web/templates/admin/campsite/type/option/index.gohtml:10
#: web/templates/admin/campsite/type/index.gohtml:10
#: web/templates/admin/layout.gohtml:46 web/templates/admin/layout.gohtml:95
#: web/templates/admin/booking/fields.gohtml:265
#: web/templates/admin/booking/fields.gohtml:266
msgctxt "title"
msgid "Campsites"
msgstr "Alojamientos"
@ -978,6 +978,7 @@ msgid "Booking Period"
msgstr "Periodo de reserva"
#: web/templates/public/booking/fields.gohtml:56
#: web/templates/admin/booking/checkin.gohtml:20
msgctxt "title"
msgid "Guests"
msgstr "Huéspedes"
@ -987,18 +988,18 @@ msgid "Note: Due to guest capacity, we have added more accommodations to the boo
msgstr "Nota: Se han añadido alojamientos a la reserva debido a la capacidad de cada una, pero <strong>no</strong> se garantiza que estén de lado."
#: web/templates/public/booking/fields.gohtml:109
#: web/templates/admin/booking/fields.gohtml:178
#: web/templates/admin/booking/fields.gohtml:179
msgid "Note: This accommodation does <strong>not</strong> allow dogs."
msgstr "Nota: En este alojamiento <strong>no</strong> se permiten perros."
#: web/templates/public/booking/fields.gohtml:121
#: web/templates/admin/booking/fields.gohtml:55
#: web/templates/admin/booking/fields.gohtml:56
msgctxt "input"
msgid "Area preferences (optional)"
msgstr "Preferencias de área (opcional)"
#: web/templates/public/booking/fields.gohtml:123
#: web/templates/admin/booking/fields.gohtml:59
#: web/templates/admin/booking/fields.gohtml:60
msgid "Campground map"
msgstr "Mapa del camping"
@ -1008,12 +1009,13 @@ msgid "Town or village"
msgstr "Población"
#: web/templates/public/booking/fields.gohtml:193
#: web/templates/admin/booking/fields.gohtml:203
#: web/templates/admin/booking/fields.gohtml:204
#: web/templates/admin/booking/guest.gohtml:109
msgid "Choose a country"
msgstr "Escoja un país"
#: web/templates/public/booking/fields.gohtml:247
#: web/templates/admin/booking/fields.gohtml:258
#: web/templates/admin/booking/fields.gohtml:259
msgctxt "input"
msgid "ACSI card? (optional)"
msgstr "¿Tarjeta ACSI? (opcional)"
@ -1098,8 +1100,8 @@ msgid "Down payment"
msgstr "A cuenta"
#: web/templates/admin/payment/index.gohtml:24
#: web/templates/admin/booking/fields.gohtml:74
#: web/templates/admin/booking/fields.gohtml:172
#: web/templates/admin/booking/fields.gohtml:75
#: web/templates/admin/booking/fields.gohtml:173
msgctxt "header"
msgid "Total"
msgstr "Total"
@ -1194,7 +1196,7 @@ msgstr "Contenido"
#: web/templates/admin/amenity/form.gohtml:91
#: web/templates/admin/home/index.gohtml:34
#: web/templates/admin/media/form.gohtml:39
#: web/templates/admin/booking/fields.gohtml:272
#: web/templates/admin/booking/form.gohtml:45
msgctxt "action"
msgid "Update"
msgstr "Actualizar"
@ -1214,7 +1216,7 @@ msgstr "Actualizar"
#: web/templates/admin/amenity/feature/form.gohtml:67
#: web/templates/admin/amenity/carousel/form.gohtml:52
#: web/templates/admin/amenity/form.gohtml:93
#: web/templates/admin/booking/fields.gohtml:274
#: web/templates/admin/booking/form.gohtml:47
msgctxt "action"
msgid "Add"
msgstr "Añadir"
@ -2161,7 +2163,7 @@ msgid "Add Amenity"
msgstr "Añadir instalación"
#: web/templates/admin/amenity/index.gohtml:20
#: web/templates/admin/booking/grid.gohtml:13
#: web/templates/admin/booking/grid.gohtml:15
msgctxt "header"
msgid "Label"
msgstr "Etiqueta"
@ -2204,7 +2206,8 @@ msgid "Logout"
msgstr "Salir"
#: web/templates/admin/layout.gohtml:92
#: web/templates/admin/booking/form.gohtml:15
#: web/templates/admin/booking/form.gohtml:19
#: web/templates/admin/booking/checkin.gohtml:10
#: web/templates/admin/booking/index.gohtml:6
#: web/templates/admin/booking/index.gohtml:16
msgctxt "title"
@ -2312,51 +2315,53 @@ msgctxt "title"
msgid "Upload Media"
msgstr "Subida de medio"
#: web/templates/admin/booking/fields.gohtml:18
#: web/templates/admin/booking/fields.gohtml:19
msgid "Choose an accommodation"
msgstr "Escoja un alojamiento"
#: web/templates/admin/booking/fields.gohtml:72
#: web/templates/admin/booking/fields.gohtml:73
msgctxt "header"
msgid "Units"
msgstr "Unidades"
#: web/templates/admin/booking/fields.gohtml:73
#: web/templates/admin/booking/fields.gohtml:74
msgctxt "header"
msgid "Decription"
msgstr "Descripción"
#: web/templates/admin/booking/fields.gohtml:80 pkg/booking/cart.go:234
#: web/templates/admin/booking/fields.gohtml:81 pkg/booking/cart.go:232
msgctxt "cart"
msgid "Night"
msgstr "Noche"
#: web/templates/admin/booking/fields.gohtml:199
#: web/templates/admin/booking/fields.gohtml:200
msgctxt "input"
msgid "Country (optional)"
msgstr "País (opcional)"
#: web/templates/admin/booking/fields.gohtml:211
#: web/templates/admin/booking/fields.gohtml:212
#: web/templates/admin/booking/guest.gohtml:128
msgctxt "input"
msgid "Address (optional)"
msgstr "Dirección (opcional)"
#: web/templates/admin/booking/fields.gohtml:220
#: web/templates/admin/booking/fields.gohtml:221
msgctxt "input"
msgid "Postcode (optional)"
msgstr "Código postal (opcional)"
#: web/templates/admin/booking/fields.gohtml:229
#: web/templates/admin/booking/fields.gohtml:230
msgctxt "input"
msgid "Town or village (optional)"
msgstr "Población (opcional)"
#: web/templates/admin/booking/fields.gohtml:238
#: web/templates/admin/booking/fields.gohtml:239
msgctxt "input"
msgid "Email (optional)"
msgstr "Correo-e (opcional)"
#: web/templates/admin/booking/fields.gohtml:247
#: web/templates/admin/booking/fields.gohtml:248
#: web/templates/admin/booking/guest.gohtml:117
msgctxt "input"
msgid "Phone (optional)"
msgstr "Teléfono (opcional)"
@ -2371,6 +2376,31 @@ msgctxt "title"
msgid "New Booking"
msgstr "Nueva reserva"
#: web/templates/admin/booking/form.gohtml:25
msgctxt "action"
msgid "Check-in Booking"
msgstr "Registrar reserva"
#: web/templates/admin/booking/checkin.gohtml:6
msgctxt "title"
msgid "Check-in Booking"
msgstr "Registro de la reserva"
#: web/templates/admin/booking/checkin.gohtml:15
msgctxt "action"
msgid "Edit Booking"
msgstr "Editar la reserva"
#: web/templates/admin/booking/checkin.gohtml:26
msgctxt "action"
msgid "Check-in"
msgstr "Registrar"
#: web/templates/admin/booking/checkin.gohtml:31
msgctxt "action"
msgid "Add another guest"
msgstr "Añadir otro huésped"
#: web/templates/admin/booking/index.gohtml:14
msgctxt "action"
msgid "Add Booking"
@ -2400,6 +2430,64 @@ msgstr "Nombre del titular"
msgid "No booking found."
msgstr "No se ha encontrado ninguna reserva."
#: web/templates/admin/booking/guest.gohtml:5
msgctxt "action"
msgid "Remove"
msgstr "Borrar"
#: web/templates/admin/booking/guest.gohtml:8
msgctxt "input"
msgid "ID document number"
msgstr "Número de documento de identidad"
#: web/templates/admin/booking/guest.gohtml:20
msgctxt "input"
msgid "ID document type"
msgstr "Tipo de documento"
#: web/templates/admin/booking/guest.gohtml:25
msgid "Choose an ID document type"
msgstr "Escoja un tipo de documento"
#: web/templates/admin/booking/guest.gohtml:33
msgctxt "input"
msgid "ID document issue date (if any)"
msgstr "Fecha expedición del documento (si hay)"
#: web/templates/admin/booking/guest.gohtml:44
msgctxt "input"
msgid "First surname"
msgstr "Primer apellido"
#: web/templates/admin/booking/guest.gohtml:56
msgctxt "input"
msgid "Second surname (if has one)"
msgstr "Segundo apellido (si tiene)"
#: web/templates/admin/booking/guest.gohtml:67
msgctxt "input"
msgid "Given name"
msgstr "Nombre"
#: web/templates/admin/booking/guest.gohtml:79
msgctxt "input"
msgid "Sex"
msgstr "Sexo"
#: web/templates/admin/booking/guest.gohtml:84
msgid "Choose a sex"
msgstr "Escoja un sexo"
#: web/templates/admin/booking/guest.gohtml:92
msgctxt "input"
msgid "Birthdate"
msgstr "Fecha de nacimiento"
#: web/templates/admin/booking/guest.gohtml:104
msgctxt "input"
msgid "Nationality"
msgstr "Nacionalidad"
#: pkg/payment/settings.go:37
msgctxt "redsys environment"
msgid "Test"
@ -2501,12 +2589,12 @@ msgid "Slide image must be an image media type."
msgstr "La imagen de la diapositiva tiene que ser un medio de tipo imagen."
#: pkg/app/login.go:56 pkg/app/user.go:246 pkg/company/admin.go:224
#: pkg/booking/public.go:545
#: pkg/booking/public.go:596
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/company/admin.go:225
#: pkg/booking/admin.go:316 pkg/booking/public.go:546
#: pkg/booking/admin.go:437 pkg/booking/public.go:597
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."
@ -2717,8 +2805,8 @@ 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:292 pkg/booking/public.go:173
#: pkg/booking/public.go:228
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:413 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."
@ -2814,7 +2902,8 @@ msgstr "No podéis dejar la dirección del enlace en blanco."
msgid "This web address is not valid. It should be like https://domain.com/."
msgstr "Esta dirección web no es válida. Tiene que ser parecido a https://dominio.com/."
#: pkg/company/admin.go:207 pkg/booking/public.go:530
#: pkg/company/admin.go:207 pkg/booking/checkin.go:301
#: pkg/booking/public.go:581
msgid "Selected country is not valid."
msgstr "El país escogido no es válido."
@ -2834,15 +2923,16 @@ msgstr "No podéis dejar el NIF en blanco."
msgid "This VAT number is not valid."
msgstr "Este NIF no es válido."
#: pkg/company/admin.go:219 pkg/booking/public.go:548
#: pkg/company/admin.go:219 pkg/booking/public.go:599
msgid "Phone can not be empty."
msgstr "No podéis dejar el teléfono en blanco."
#: pkg/company/admin.go:220 pkg/booking/admin.go:321 pkg/booking/public.go:549
#: pkg/company/admin.go:220 pkg/booking/checkin.go:305 pkg/booking/admin.go:442
#: pkg/booking/public.go:600
msgid "This phone number is not valid."
msgstr "Este teléfono no es válido."
#: pkg/company/admin.go:230 pkg/booking/public.go:538
#: pkg/company/admin.go:230 pkg/booking/public.go:589
msgid "Address can not be empty."
msgstr "No podéis dejar la dirección en blanco."
@ -2854,11 +2944,11 @@ msgstr "No podéis dejar la población en blanco."
msgid "Province can not be empty."
msgstr "No podéis dejar la provincia en blanco."
#: pkg/company/admin.go:233 pkg/booking/public.go:540
#: pkg/company/admin.go:233 pkg/booking/public.go:591
msgid "Postcode can not be empty."
msgstr "No podéis dejar el código postal en blanco."
#: pkg/company/admin.go:234 pkg/booking/admin.go:311 pkg/booking/public.go:541
#: pkg/company/admin.go:234 pkg/booking/admin.go:432 pkg/booking/public.go:592
msgid "This postcode is not valid."
msgstr "Este código postal no es válido."
@ -2910,169 +3000,202 @@ msgstr "No podéis dejar el archivo del medio en blanco."
msgid "Filename can not be empty."
msgstr "No podéis dejar el nombre del archivo en blanco."
#: pkg/booking/cart.go:235
#: pkg/booking/checkin.go:285
msgid "Selected ID document type is not valid."
msgstr "El tipo de documento de identidad escogido no es válido."
#: pkg/booking/checkin.go:286
msgid "ID document number can not be empty."
msgstr "No podéis dejar el número del documento de identidad en blanco."
#: pkg/booking/checkin.go:288
msgid "ID document issue date must be a valid date."
msgstr "La fecha de expedición del documento de identidad tiene que ser una fecha válida."
#: pkg/booking/checkin.go:289
msgid "ID document issue date must be in the past."
msgstr "La fecha de expedición del documento de identidad tiene que ser del pasado."
#: pkg/booking/checkin.go:292 pkg/booking/checkin.go:293
#: pkg/booking/admin.go:425 pkg/booking/public.go:585
msgid "Full name can not be empty."
msgstr "No podéis dejar el nombre y los apellidos en blanco."
#: pkg/booking/checkin.go:294
msgid "Selected sex is not valid."
msgstr "El sexo escogido no es válido."
#: pkg/booking/checkin.go:295
msgid "Birthdate can not be empty"
msgstr "No podéis dejar la fecha de nacimiento en blanco."
#: pkg/booking/checkin.go:296
msgid "Birthdate must be a valid date."
msgstr "La fecha de nacimiento tiene que ser una fecha válida."
#: pkg/booking/checkin.go:297
msgid "Birthdate must be in the past."
msgstr "La fecha de nacimiento tiene que ser del pasado."
#: pkg/booking/cart.go:233
msgctxt "cart"
msgid "Adult"
msgstr "Adulto"
#: pkg/booking/cart.go:236
#: pkg/booking/cart.go:234
msgctxt "cart"
msgid "Teenager"
msgstr "Adolescente"
#: pkg/booking/cart.go:237
#: pkg/booking/cart.go:235
msgctxt "cart"
msgid "Child"
msgstr "Niño"
#: pkg/booking/cart.go:238
#: pkg/booking/cart.go:236
msgctxt "cart"
msgid "Dog"
msgstr "Perro"
#: pkg/booking/admin.go:144
#: pkg/booking/admin.go:217
msgctxt "filename"
msgid "bookings.ods"
msgstr "reservas.ods"
#: pkg/booking/admin.go:304 pkg/booking/public.go:534
msgid "Full name can not be empty."
msgstr "No podéis dejar el nombre y los apellidos en blanco."
#: pkg/booking/admin.go:305 pkg/booking/public.go:535
#: pkg/booking/admin.go:426 pkg/booking/public.go:586
msgid "Full name must have at least one letter."
msgstr "El nombre y los apellidos tienen que tener como mínimo una letra."
#: pkg/booking/admin.go:310
#: pkg/booking/admin.go:431
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:320
#: pkg/booking/admin.go:441
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:327
#: pkg/booking/admin.go:448
msgid "You must select at least one accommodation."
msgstr "Tenéis que seleccionar como mínimo un alojamiento."
#: pkg/booking/admin.go:333
#: pkg/booking/admin.go:454
msgid "The selected accommodations have no available openings in the requested dates."
msgstr "Los alojamientos seleccionados no tienen disponibilidad en las fechas pedidas."
#: pkg/booking/public.go:277 pkg/booking/public.go:306
#: pkg/booking/public.go:284 pkg/booking/public.go:313
msgid "Arrival date must be a valid date."
msgstr "La fecha de llegada tiene que ser una fecha válida."
#: pkg/booking/public.go:291 pkg/booking/public.go:313
#: pkg/booking/public.go:298 pkg/booking/public.go:320
msgid "Departure date must be a valid date."
msgstr "La fecha de partida tiene que ser una fecha válida."
#: pkg/booking/public.go:305
#: pkg/booking/public.go:312
msgid "Arrival date can not be empty"
msgstr "No podéis dejar la fecha de llegada en blanco."
#: pkg/booking/public.go:307
#: pkg/booking/public.go:314
#, c-format
msgid "Arrival date must be %s or after."
msgstr "La fecha de llegada tiene que ser igual o posterior a %s."
#: pkg/booking/public.go:308
#: pkg/booking/public.go:315
#, c-format
msgid "Arrival date must be %s or before."
msgstr "La fecha de llegada tiene que ser anterior o igual a %s."
#: pkg/booking/public.go:312
#: pkg/booking/public.go:319
msgid "Departure date can not be empty"
msgstr "No podéis dejar la fecha de partida en blanco."
#: pkg/booking/public.go:314
#: pkg/booking/public.go:321
#, c-format
msgid "Departure date must be %s or after."
msgstr "La fecha de partida tiene que igual o posterior a %s."
#: pkg/booking/public.go:315
#: pkg/booking/public.go:322
#, c-format
msgid "Departure date must be %s or before."
msgstr "La fecha de partida tiene que ser anterior o igual a %s."
#: pkg/booking/public.go:369
#: pkg/booking/public.go:380
#, c-format
msgid "There can be at most %d guests in this accommodation."
msgstr "Solo puede haber como máximo %d invitados en este alojamiento."
#: pkg/booking/public.go:389
#: pkg/booking/public.go:400
msgid "Number of adults can not be empty"
msgstr "No podéis dejar el número de adultos blanco."
#: pkg/booking/public.go:390
#: pkg/booking/public.go:401
msgid "Number of adults must be an integer."
msgstr "El número de adultos tiene que ser entero."
#: pkg/booking/public.go:391
#: pkg/booking/public.go:402
msgid "There must be at least one adult."
msgstr "Tiene que haber como mínimo un adulto."
#: pkg/booking/public.go:394
#: pkg/booking/public.go:405
msgid "Number of teenagers can not be empty"
msgstr "No podéis dejar el número de adolescentes en blanco."
#: pkg/booking/public.go:395
#: pkg/booking/public.go:406
msgid "Number of teenagers must be an integer."
msgstr "El número de adolescentes tiene que ser entero."
#: pkg/booking/public.go:396
#: pkg/booking/public.go:407
msgid "Number of teenagers can not be negative."
msgstr "El número de adolescentes no puede ser negativo."
#: pkg/booking/public.go:399
#: pkg/booking/public.go:410
msgid "Number of children can not be empty"
msgstr "No podéis dejar el número de niños en blanco."
#: pkg/booking/public.go:400
#: pkg/booking/public.go:411
msgid "Number of children must be an integer."
msgstr "El número de niños tiene que ser entero."
#: pkg/booking/public.go:401
#: pkg/booking/public.go:412
msgid "Number of children can not be negative."
msgstr "El número de niños no puede ser negativo."
#: pkg/booking/public.go:404
#: pkg/booking/public.go:415
msgid "Number of dogs can not be empty"
msgstr "No podéis dejar el número de perros en blanco."
#: pkg/booking/public.go:405
#: pkg/booking/public.go:416
msgid "Number of dogs must be an integer."
msgstr "El número de perros tiene que ser entero."
#: pkg/booking/public.go:406
#: pkg/booking/public.go:417
msgid "Number of dogs can not be negative."
msgstr "El número de perros no puede ser negativo."
#: pkg/booking/public.go:477
#: pkg/booking/public.go:524
#, c-format
msgid "%s can not be empty"
msgstr "No podéis dejar %s en blanco."
#: pkg/booking/public.go:478
#: pkg/booking/public.go:525
#, c-format
msgid "%s must be an integer."
msgstr "%s tiene que ser un número entero."
#: pkg/booking/public.go:479
#: pkg/booking/public.go:526
#, c-format
msgid "%s must be %d or greater."
msgstr "%s tiene que ser como mínimo %d."
#: pkg/booking/public.go:480
#: pkg/booking/public.go:527
#, c-format
msgid "%s must be at most %d."
msgstr "%s tiene que ser como máximo %d"
#: pkg/booking/public.go:539
#: pkg/booking/public.go:590
msgid "Town or village can not be empty."
msgstr "No podéis dejar la población en blanco."
#: pkg/booking/public.go:554
#: pkg/booking/public.go:605
msgid "It is mandatory to agree to the reservation conditions."
msgstr "Es obligatorio aceptar las condiciones de reserva."

291
po/fr.po
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-04-24 19:59+0200\n"
"POT-Creation-Date: 2024-04-26 16:53+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"
@ -116,7 +116,7 @@ msgstr "Réservation"
#: web/templates/mail/payment/details.gotxt:16
#: web/templates/public/booking/fields.gohtml:14
#: web/templates/admin/payment/details.gohtml:74
#: web/templates/admin/booking/fields.gohtml:12
#: web/templates/admin/booking/fields.gohtml:13
msgctxt "title"
msgid "Accommodation"
msgstr "Hébergement"
@ -155,7 +155,7 @@ msgstr "Non"
#: web/templates/public/campsite/dates.gohtml:4
#: web/templates/public/booking/fields.gohtml:30
#: web/templates/admin/payment/details.gohtml:86
#: web/templates/admin/booking/fields.gohtml:31
#: web/templates/admin/booking/fields.gohtml:32
msgctxt "input"
msgid "Arrival date"
msgstr "Date darrivée"
@ -164,7 +164,7 @@ msgstr "Date darrivée"
#: web/templates/public/campsite/dates.gohtml:15
#: web/templates/public/booking/fields.gohtml:41
#: web/templates/admin/payment/details.gohtml:90
#: web/templates/admin/booking/fields.gohtml:42
#: web/templates/admin/booking/fields.gohtml:43
msgctxt "input"
msgid "Departure date"
msgstr "Date de depart"
@ -178,7 +178,7 @@ msgstr "Nuits"
#: web/templates/mail/payment/details.gotxt:22
#: web/templates/public/booking/fields.gohtml:60
#: web/templates/admin/payment/details.gohtml:98
#: web/templates/admin/booking/fields.gohtml:92
#: web/templates/admin/booking/fields.gohtml:93
msgctxt "input"
msgid "Adults aged 17 or older"
msgstr "Adultes âgés 17 ans ou plus"
@ -186,7 +186,7 @@ msgstr "Adultes âgés 17 ans ou plus"
#: web/templates/mail/payment/details.gotxt:23
#: web/templates/public/booking/fields.gohtml:71
#: web/templates/admin/payment/details.gohtml:102
#: web/templates/admin/booking/fields.gohtml:108
#: web/templates/admin/booking/fields.gohtml:109
msgctxt "input"
msgid "Teenagers from 11 to 16 years old"
msgstr "Adolescents de 11 à 16 ans"
@ -194,7 +194,7 @@ msgstr "Adolescents de 11 à 16 ans"
#: web/templates/mail/payment/details.gotxt:24
#: web/templates/public/booking/fields.gohtml:82
#: web/templates/admin/payment/details.gohtml:106
#: web/templates/admin/booking/fields.gohtml:124
#: web/templates/admin/booking/fields.gohtml:125
msgctxt "input"
msgid "Children from 2 to 10 years old"
msgstr "Enfants de 2 à 10 ans"
@ -202,14 +202,14 @@ msgstr "Enfants de 2 à 10 ans"
#: web/templates/mail/payment/details.gotxt:25
#: web/templates/public/booking/fields.gohtml:100
#: web/templates/admin/payment/details.gohtml:110
#: web/templates/admin/booking/fields.gohtml:139
#: web/templates/admin/booking/fields.gohtml:140
msgctxt "input"
msgid "Dogs"
msgstr "Chiens"
#: web/templates/mail/payment/details.gotxt:26
#: web/templates/admin/payment/details.gohtml:114
#: web/templates/admin/booking/fields.gohtml:166 pkg/booking/cart.go:244
#: web/templates/admin/booking/fields.gohtml:167 pkg/booking/cart.go:242
msgctxt "cart"
msgid "Tourist tax"
msgstr "Taxe touristique"
@ -239,7 +239,7 @@ msgstr "Options de type demplacement de camping"
#: web/templates/mail/payment/details.gotxt:39
#: web/templates/public/booking/fields.gohtml:146
#: web/templates/admin/payment/details.gohtml:140
#: web/templates/admin/booking/fields.gohtml:187
#: web/templates/admin/booking/fields.gohtml:188
msgctxt "title"
msgid "Customer Details"
msgstr "Détails du client"
@ -247,7 +247,7 @@ msgstr "Détails du client"
#: web/templates/mail/payment/details.gotxt:41
#: web/templates/public/booking/fields.gohtml:149
#: web/templates/admin/payment/details.gohtml:143
#: web/templates/admin/booking/fields.gohtml:190
#: web/templates/admin/booking/fields.gohtml:191
msgctxt "input"
msgid "Full name"
msgstr "Nom et prénom"
@ -930,7 +930,7 @@ msgstr "Menu"
#: web/templates/admin/campsite/type/option/index.gohtml:10
#: web/templates/admin/campsite/type/index.gohtml:10
#: web/templates/admin/layout.gohtml:46 web/templates/admin/layout.gohtml:95
#: web/templates/admin/booking/fields.gohtml:265
#: web/templates/admin/booking/fields.gohtml:266
msgctxt "title"
msgid "Campsites"
msgstr "Locatifs"
@ -978,6 +978,7 @@ msgid "Booking Period"
msgstr "Période de réservation"
#: web/templates/public/booking/fields.gohtml:56
#: web/templates/admin/booking/checkin.gohtml:20
msgctxt "title"
msgid "Guests"
msgstr "Personnes logeant"
@ -987,18 +988,18 @@ msgid "Note: Due to guest capacity, we have added more accommodations to the boo
msgstr "Remarque : En raison de la capacité daccueils, nous avons ajouté dautres hébergements à la réservation, mais nous <strong>ne pouvons</strong> garantir quils seront côte à côte."
#: web/templates/public/booking/fields.gohtml:109
#: web/templates/admin/booking/fields.gohtml:178
#: web/templates/admin/booking/fields.gohtml:179
msgid "Note: This accommodation does <strong>not</strong> allow dogs."
msgstr "Remarque : Dans cet hébergement les chiens <strong>ne</strong> sont pas acceptés."
#: web/templates/public/booking/fields.gohtml:121
#: web/templates/admin/booking/fields.gohtml:55
#: web/templates/admin/booking/fields.gohtml:56
msgctxt "input"
msgid "Area preferences (optional)"
msgstr "Préférences de zone (facultatif)"
#: web/templates/public/booking/fields.gohtml:123
#: web/templates/admin/booking/fields.gohtml:59
#: web/templates/admin/booking/fields.gohtml:60
msgid "Campground map"
msgstr "Plan du camping"
@ -1008,12 +1009,13 @@ msgid "Town or village"
msgstr "Ville"
#: web/templates/public/booking/fields.gohtml:193
#: web/templates/admin/booking/fields.gohtml:203
#: web/templates/admin/booking/fields.gohtml:204
#: web/templates/admin/booking/guest.gohtml:109
msgid "Choose a country"
msgstr "Choisissez un pays"
#: web/templates/public/booking/fields.gohtml:247
#: web/templates/admin/booking/fields.gohtml:258
#: web/templates/admin/booking/fields.gohtml:259
msgctxt "input"
msgid "ACSI card? (optional)"
msgstr "Carte ACSI ? (facultatif)"
@ -1098,8 +1100,8 @@ msgid "Down payment"
msgstr "Acompte"
#: web/templates/admin/payment/index.gohtml:24
#: web/templates/admin/booking/fields.gohtml:74
#: web/templates/admin/booking/fields.gohtml:172
#: web/templates/admin/booking/fields.gohtml:75
#: web/templates/admin/booking/fields.gohtml:173
msgctxt "header"
msgid "Total"
msgstr "Totale"
@ -1194,7 +1196,7 @@ msgstr "Contenu"
#: web/templates/admin/amenity/form.gohtml:91
#: web/templates/admin/home/index.gohtml:34
#: web/templates/admin/media/form.gohtml:39
#: web/templates/admin/booking/fields.gohtml:272
#: web/templates/admin/booking/form.gohtml:45
msgctxt "action"
msgid "Update"
msgstr "Mettre à jour"
@ -1214,7 +1216,7 @@ msgstr "Mettre à jour"
#: web/templates/admin/amenity/feature/form.gohtml:67
#: web/templates/admin/amenity/carousel/form.gohtml:52
#: web/templates/admin/amenity/form.gohtml:93
#: web/templates/admin/booking/fields.gohtml:274
#: web/templates/admin/booking/form.gohtml:47
msgctxt "action"
msgid "Add"
msgstr "Ajouter"
@ -2161,7 +2163,7 @@ msgid "Add Amenity"
msgstr "Ajouter un installation"
#: web/templates/admin/amenity/index.gohtml:20
#: web/templates/admin/booking/grid.gohtml:13
#: web/templates/admin/booking/grid.gohtml:15
msgctxt "header"
msgid "Label"
msgstr "Label"
@ -2204,7 +2206,8 @@ msgid "Logout"
msgstr "Déconnexion"
#: web/templates/admin/layout.gohtml:92
#: web/templates/admin/booking/form.gohtml:15
#: web/templates/admin/booking/form.gohtml:19
#: web/templates/admin/booking/checkin.gohtml:10
#: web/templates/admin/booking/index.gohtml:6
#: web/templates/admin/booking/index.gohtml:16
msgctxt "title"
@ -2312,51 +2315,53 @@ msgctxt "title"
msgid "Upload Media"
msgstr "Envoyer un fichier"
#: web/templates/admin/booking/fields.gohtml:18
#: web/templates/admin/booking/fields.gohtml:19
msgid "Choose an accommodation"
msgstr "Choisissez un hébergement"
#: web/templates/admin/booking/fields.gohtml:72
#: web/templates/admin/booking/fields.gohtml:73
msgctxt "header"
msgid "Units"
msgstr "Unités"
#: web/templates/admin/booking/fields.gohtml:73
#: web/templates/admin/booking/fields.gohtml:74
msgctxt "header"
msgid "Decription"
msgstr "Description"
#: web/templates/admin/booking/fields.gohtml:80 pkg/booking/cart.go:234
#: web/templates/admin/booking/fields.gohtml:81 pkg/booking/cart.go:232
msgctxt "cart"
msgid "Night"
msgstr "Nuit"
#: web/templates/admin/booking/fields.gohtml:199
#: web/templates/admin/booking/fields.gohtml:200
msgctxt "input"
msgid "Country (optional)"
msgstr "Pays (facultatif)"
#: web/templates/admin/booking/fields.gohtml:211
#: web/templates/admin/booking/fields.gohtml:212
#: web/templates/admin/booking/guest.gohtml:128
msgctxt "input"
msgid "Address (optional)"
msgstr "Adresse (facultatif)"
#: web/templates/admin/booking/fields.gohtml:220
#: web/templates/admin/booking/fields.gohtml:221
msgctxt "input"
msgid "Postcode (optional)"
msgstr "Code postal (facultatif)"
#: web/templates/admin/booking/fields.gohtml:229
#: web/templates/admin/booking/fields.gohtml:230
msgctxt "input"
msgid "Town or village (optional)"
msgstr "Ville (facultatif)"
#: web/templates/admin/booking/fields.gohtml:238
#: web/templates/admin/booking/fields.gohtml:239
msgctxt "input"
msgid "Email (optional)"
msgstr "E-mail (facultatif)"
#: web/templates/admin/booking/fields.gohtml:247
#: web/templates/admin/booking/fields.gohtml:248
#: web/templates/admin/booking/guest.gohtml:117
msgctxt "input"
msgid "Phone (optional)"
msgstr "Téléphone (facultatif)"
@ -2371,6 +2376,31 @@ msgctxt "title"
msgid "New Booking"
msgstr "Nouvelle réservation"
#: web/templates/admin/booking/form.gohtml:25
msgctxt "action"
msgid "Check-in Booking"
msgstr "Enregistrer réservation"
#: web/templates/admin/booking/checkin.gohtml:6
msgctxt "title"
msgid "Check-in Booking"
msgstr "Enregistrement de la réservation"
#: web/templates/admin/booking/checkin.gohtml:15
msgctxt "action"
msgid "Edit Booking"
msgstr "Modifier la réservation"
#: web/templates/admin/booking/checkin.gohtml:26
msgctxt "action"
msgid "Check-in"
msgstr "Enregitrer"
#: web/templates/admin/booking/checkin.gohtml:31
msgctxt "action"
msgid "Add another guest"
msgstr "Ajouter un autre invité"
#: web/templates/admin/booking/index.gohtml:14
msgctxt "action"
msgid "Add Booking"
@ -2400,6 +2430,64 @@ msgstr "Nom du titulaire"
msgid "No booking found."
msgstr "Aucune réservation trouvée."
#: web/templates/admin/booking/guest.gohtml:5
msgctxt "action"
msgid "Remove"
msgstr "Retirer"
#: web/templates/admin/booking/guest.gohtml:8
msgctxt "input"
msgid "ID document number"
msgstr "Numéro de document didentité"
#: web/templates/admin/booking/guest.gohtml:20
msgctxt "input"
msgid "ID document type"
msgstr "Type de document didentité"
#: web/templates/admin/booking/guest.gohtml:25
msgid "Choose an ID document type"
msgstr "Choisissez un type de document didentité"
#: web/templates/admin/booking/guest.gohtml:33
msgctxt "input"
msgid "ID document issue date (if any)"
msgstr "Date de délivrance du document didentité (si j'en ai)"
#: web/templates/admin/booking/guest.gohtml:44
msgctxt "input"
msgid "First surname"
msgstr "Premier nom"
#: web/templates/admin/booking/guest.gohtml:56
msgctxt "input"
msgid "Second surname (if has one)"
msgstr "Deuxième nom"
#: web/templates/admin/booking/guest.gohtml:67
msgctxt "input"
msgid "Given name"
msgstr "Prénom"
#: web/templates/admin/booking/guest.gohtml:79
msgctxt "input"
msgid "Sex"
msgstr "Sexe"
#: web/templates/admin/booking/guest.gohtml:84
msgid "Choose a sex"
msgstr "Choisissez un sexe"
#: web/templates/admin/booking/guest.gohtml:92
msgctxt "input"
msgid "Birthdate"
msgstr "Date de naissance"
#: web/templates/admin/booking/guest.gohtml:104
msgctxt "input"
msgid "Nationality"
msgstr "Nationalité"
#: pkg/payment/settings.go:37
msgctxt "redsys environment"
msgid "Test"
@ -2501,12 +2589,12 @@ msgid "Slide image must be an image media type."
msgstr "Limage de la diapositive doit être de type média dimage."
#: pkg/app/login.go:56 pkg/app/user.go:246 pkg/company/admin.go:224
#: pkg/booking/public.go:545
#: pkg/booking/public.go:596
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/company/admin.go:225
#: pkg/booking/admin.go:316 pkg/booking/public.go:546
#: pkg/booking/admin.go:437 pkg/booking/public.go:597
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."
@ -2717,8 +2805,8 @@ msgctxt "header"
msgid "Children (aged 2 to 10)"
msgstr "Enfants (de 2 à 10 anys)"
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:292 pkg/booking/public.go:173
#: pkg/booking/public.go:228
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:413 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."
@ -2814,7 +2902,8 @@ msgstr "Laddresse du lien ne peut pas être vide."
msgid "This web address is not valid. It should be like https://domain.com/."
msgstr "Cette adresse web nest pas valide. Il devrait en être https://domain.com/."
#: pkg/company/admin.go:207 pkg/booking/public.go:530
#: pkg/company/admin.go:207 pkg/booking/checkin.go:301
#: pkg/booking/public.go:581
msgid "Selected country is not valid."
msgstr "Le pays sélectionné nest pas valide."
@ -2834,15 +2923,16 @@ msgstr "Le numéro de TVA ne peut pas être vide."
msgid "This VAT number is not valid."
msgstr "Ce numéro de TVA nest pas valide."
#: pkg/company/admin.go:219 pkg/booking/public.go:548
#: pkg/company/admin.go:219 pkg/booking/public.go:599
msgid "Phone can not be empty."
msgstr "Le téléphone ne peut pas être vide."
#: pkg/company/admin.go:220 pkg/booking/admin.go:321 pkg/booking/public.go:549
#: pkg/company/admin.go:220 pkg/booking/checkin.go:305 pkg/booking/admin.go:442
#: pkg/booking/public.go:600
msgid "This phone number is not valid."
msgstr "Ce numéro de téléphone nest pas valide."
#: pkg/company/admin.go:230 pkg/booking/public.go:538
#: pkg/company/admin.go:230 pkg/booking/public.go:589
msgid "Address can not be empty."
msgstr "Ladresse ne peut pas être vide."
@ -2854,11 +2944,11 @@ msgstr "La ville ne peut pas être vide."
msgid "Province can not be empty."
msgstr "La province ne peut pas être vide."
#: pkg/company/admin.go:233 pkg/booking/public.go:540
#: pkg/company/admin.go:233 pkg/booking/public.go:591
msgid "Postcode can not be empty."
msgstr "Le code postal ne peut pas être vide."
#: pkg/company/admin.go:234 pkg/booking/admin.go:311 pkg/booking/public.go:541
#: pkg/company/admin.go:234 pkg/booking/admin.go:432 pkg/booking/public.go:592
msgid "This postcode is not valid."
msgstr "Ce code postal nest pas valide."
@ -2910,169 +3000,202 @@ msgstr "Le fichier téléchargé ne peut pas être vide."
msgid "Filename can not be empty."
msgstr "Le nom de fichier ne peut pas être vide."
#: pkg/booking/cart.go:235
#: pkg/booking/checkin.go:285
msgid "Selected ID document type is not valid."
msgstr "Le type de document didentité sélectionné nest pas valide."
#: pkg/booking/checkin.go:286
msgid "ID document number can not be empty."
msgstr "Le numéro de documento didentité ne peut pas être vide."
#: pkg/booking/checkin.go:288
msgid "ID document issue date must be a valid date."
msgstr "La date de délivrance du document didentité doit être une date valide."
#: pkg/booking/checkin.go:289
msgid "ID document issue date must be in the past."
msgstr "La ate de délivrance du document didentité doit être du passé."
#: pkg/booking/checkin.go:292 pkg/booking/checkin.go:293
#: pkg/booking/admin.go:425 pkg/booking/public.go:585
msgid "Full name can not be empty."
msgstr "Le nom complet ne peut pas être vide."
#: pkg/booking/checkin.go:294
msgid "Selected sex is not valid."
msgstr "Le sexe sélectionné nest pas valide."
#: pkg/booking/checkin.go:295
msgid "Birthdate can not be empty"
msgstr "La date de naissance ne peut pas être vide."
#: pkg/booking/checkin.go:296
msgid "Birthdate must be a valid date."
msgstr "La date de naissance doit être une date valide."
#: pkg/booking/checkin.go:297
msgid "Birthdate must be in the past."
msgstr "La date de naissance doit être du passé."
#: pkg/booking/cart.go:233
msgctxt "cart"
msgid "Adult"
msgstr "Adulte"
#: pkg/booking/cart.go:236
#: pkg/booking/cart.go:234
msgctxt "cart"
msgid "Teenager"
msgstr "Adolescent"
#: pkg/booking/cart.go:237
#: pkg/booking/cart.go:235
msgctxt "cart"
msgid "Child"
msgstr "Enfant"
#: pkg/booking/cart.go:238
#: pkg/booking/cart.go:236
msgctxt "cart"
msgid "Dog"
msgstr "Chien"
#: pkg/booking/admin.go:144
#: pkg/booking/admin.go:217
msgctxt "filename"
msgid "bookings.ods"
msgstr "reservations.ods"
#: pkg/booking/admin.go:304 pkg/booking/public.go:534
msgid "Full name can not be empty."
msgstr "Le nom complet ne peut pas être vide."
#: pkg/booking/admin.go:305 pkg/booking/public.go:535
#: pkg/booking/admin.go:426 pkg/booking/public.go:586
msgid "Full name must have at least one letter."
msgstr "Le nom complet doit comporter au moins une lettre."
#: pkg/booking/admin.go:310
#: pkg/booking/admin.go:431
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:320
#: pkg/booking/admin.go:441
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:327
#: pkg/booking/admin.go:448
msgid "You must select at least one accommodation."
msgstr "Vous devez sélectionner au moins un hébergement."
#: pkg/booking/admin.go:333
#: pkg/booking/admin.go:454
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."
#: pkg/booking/public.go:277 pkg/booking/public.go:306
#: pkg/booking/public.go:284 pkg/booking/public.go:313
msgid "Arrival date must be a valid date."
msgstr "La date darrivée doit être une date valide."
#: pkg/booking/public.go:291 pkg/booking/public.go:313
#: pkg/booking/public.go:298 pkg/booking/public.go:320
msgid "Departure date must be a valid date."
msgstr "La date de départ doit être une date valide."
#: pkg/booking/public.go:305
#: pkg/booking/public.go:312
msgid "Arrival date can not be empty"
msgstr "La date darrivée ne peut pas être vide"
#: pkg/booking/public.go:307
#: pkg/booking/public.go:314
#, c-format
msgid "Arrival date must be %s or after."
msgstr "La date darrivée doit être égale ou postérieure à %s."
#: pkg/booking/public.go:308
#: pkg/booking/public.go:315
#, c-format
msgid "Arrival date must be %s or before."
msgstr "La date darrivée doit être antérieure ou égale à %s."
#: pkg/booking/public.go:312
#: pkg/booking/public.go:319
msgid "Departure date can not be empty"
msgstr "La date de départ ne peut pas être vide"
#: pkg/booking/public.go:314
#: pkg/booking/public.go:321
#, c-format
msgid "Departure date must be %s or after."
msgstr "La date de départ doit être égale ou postérieure à %s."
#: pkg/booking/public.go:315
#: pkg/booking/public.go:322
#, c-format
msgid "Departure date must be %s or before."
msgstr "La date de départ doit être antérieure ou égale à %s."
#: pkg/booking/public.go:369
#: pkg/booking/public.go:380
#, c-format
msgid "There can be at most %d guests in this accommodation."
msgstr "Il peut y avoir au plus %d invités dans cet hébergement."
#: pkg/booking/public.go:389
#: pkg/booking/public.go:400
msgid "Number of adults can not be empty"
msgstr "Le nombre dadultes ne peut pas être vide."
#: pkg/booking/public.go:390
#: pkg/booking/public.go:401
msgid "Number of adults must be an integer."
msgstr "Le nombre dadultes doit être un entier."
#: pkg/booking/public.go:391
#: pkg/booking/public.go:402
msgid "There must be at least one adult."
msgstr "Il doit y avoir au moins un adulte."
#: pkg/booking/public.go:394
#: pkg/booking/public.go:405
msgid "Number of teenagers can not be empty"
msgstr "Le nombre dadolescents ne peut pas être vide."
#: pkg/booking/public.go:395
#: pkg/booking/public.go:406
msgid "Number of teenagers must be an integer."
msgstr "Le nombre dadolescents doit être un entier."
#: pkg/booking/public.go:396
#: pkg/booking/public.go:407
msgid "Number of teenagers can not be negative."
msgstr "Le nombre dadolescents ne peut pas être négatif."
#: pkg/booking/public.go:399
#: pkg/booking/public.go:410
msgid "Number of children can not be empty"
msgstr "Le nombre denfants ne peut pas être vide."
#: pkg/booking/public.go:400
#: pkg/booking/public.go:411
msgid "Number of children must be an integer."
msgstr "Le nombre denfants doit être un entier."
#: pkg/booking/public.go:401
#: pkg/booking/public.go:412
msgid "Number of children can not be negative."
msgstr "Le nombre denfants ne peut pas être négatif."
#: pkg/booking/public.go:404
#: pkg/booking/public.go:415
msgid "Number of dogs can not be empty"
msgstr "Le nombre de chiens ne peut pas être vide."
#: pkg/booking/public.go:405
#: pkg/booking/public.go:416
msgid "Number of dogs must be an integer."
msgstr "Le nombre de chiens nuits être un entier."
#: pkg/booking/public.go:406
#: pkg/booking/public.go:417
msgid "Number of dogs can not be negative."
msgstr "Le nombre de chiens ne peut pas être négatif."
#: pkg/booking/public.go:477
#: pkg/booking/public.go:524
#, c-format
msgid "%s can not be empty"
msgstr "%s ne peut pas être vide"
#: pkg/booking/public.go:478
#: pkg/booking/public.go:525
#, c-format
msgid "%s must be an integer."
msgstr "%s doit être un entier."
#: pkg/booking/public.go:479
#: pkg/booking/public.go:526
#, c-format
msgid "%s must be %d or greater."
msgstr "%s doit être %d ou plus."
#: pkg/booking/public.go:480
#: pkg/booking/public.go:527
#, c-format
msgid "%s must be at most %d."
msgstr "%s doit être tout au plus %d."
#: pkg/booking/public.go:539
#: pkg/booking/public.go:590
msgid "Town or village can not be empty."
msgstr "La ville ne peut pas être vide."
#: pkg/booking/public.go:554
#: pkg/booking/public.go:605
msgid "It is mandatory to agree to the reservation conditions."
msgstr "Il est obligatoire daccepter les conditions de réservation."

View File

@ -0,0 +1,10 @@
-- Revert camper:available_id_document_types from pg
begin;
set search_path to camper;
delete from id_document_type_i18n;
delete from id_document_type;
commit;

View File

@ -0,0 +1,10 @@
-- Revert camper:available_sexes from pg
begin;
set search_path to camper;
delete from sex_i18n;
delete from sex;
commit;

7
revert/booking_guest.sql Normal file
View File

@ -0,0 +1,7 @@
-- Revert camper:booking_guest from pg
begin;
drop table if exists camper.booking_guest;
commit;

View File

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

View File

@ -0,0 +1,7 @@
-- Revert camper:checked_in_guest from pg
begin;
drop type if exists camper.checked_in_guest;
commit;

View File

@ -0,0 +1,7 @@
-- Revert camper:id_document_type from pg
begin;
drop table if exists camper.id_document_type;
commit;

View File

@ -0,0 +1,7 @@
-- Revert camper:id_document_type_i18n from pg
begin;
drop table if exists camper.id_document_type_i18n;
commit;

7
revert/sex.sql Normal file
View File

@ -0,0 +1,7 @@
-- Revert camper:sex from pg
begin;
drop table if exists camper.sex;
commit;

7
revert/sex_i18n.sql Normal file
View File

@ -0,0 +1,7 @@
-- Revert camper:sex_i18n from pg
begin;
drop table if exists camper.sex_i18n;
commit;

View File

@ -290,3 +290,12 @@ booking_option [roles schema_camper booking campsite_type_option positive_intege
add_booking_from_payment [roles schema_camper booking booking__payment_fields booking__stay booking_option payment payment__acsi_card payment_customer payment_option] 2024-04-24T11:23:22Z jordi fita mas <jordi@tandem.blog> # Add function to create a pre-booking from a payment
edit_booking [roles schema_camper booking booking__payment_fields booking__stay booking_campsite] 2024-04-24T16:27:10Z jordi fita mas <jordi@tandem.blog> # Add function to update a booking
edit_booking_from_payment [roles schema_camper booking booking__payment_fields booking__stay booking_option payment payment__acsi_card payment_option] 2024-04-25T17:18:41Z jordi fita mas <jordi@tandem.blog> # Add function to edit a booking from a payment
sex [roles schema_camper] 2024-04-25T22:02:27Z jordi fita mas <jordi@tandem.blog> # Add sex relation
sex_i18n [roles schema_camper sex language] 2024-04-25T22:06:21Z jordi fita mas <jordi@tandem.blog> # Add relation of sex translations
available_sexes [sex sex_i18n] 2024-04-25T22:09:27Z jordi fita mas <jordi@tandem.blog> # Add available sexes
id_document_type [roles schema_camper] 2024-04-25T22:26:47Z jordi fita mas <jordi@tandem.blog> # Add relation of identity document type
id_document_type_i18n [roles schema_camper id_document_type language] 2024-04-25T22:29:20Z jordi fita mas <jordi@tandem.blog> # Add relation for translations of ID document types
available_id_document_types [id_document_type id_document_type_i18n] 2024-04-25T22:32:05Z jordi fita mas <jordi@tandem.blog> # Add available ID document types
booking_guest [roles schema_camper booking sex id_document_type extension_pg_libphonenumber] 2024-04-26T09:40:17Z jordi fita mas <jordi@tandem.blog> # Add relation of booking guests
checked_in_guest [schema_camper] 2024-04-26T09:58:54Z jordi fita mas <jordi@tandem.blog> # Add type for checked-in guest
check_in_guests [roles schema_camper booking booking_guest checked_in_guest extension_pg_libphonenumber] 2024-04-26T10:31:53Z jordi fita mas <jordi@tandem.blog> # Add function to check-in guests

111
test/booking_guest.sql Normal file
View File

@ -0,0 +1,111 @@
-- Test booking_guest
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(78);
set search_path to camper, public;
select has_table('booking_guest');
select has_pk('booking_guest');
select col_is_unique('booking_guest', array['booking_id', 'id_document_type_id', 'id_document_number']);
select table_privs_are('booking_guest', 'guest', array[]::text[]);
select table_privs_are('booking_guest', 'employee', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('booking_guest', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('booking_guest', 'authenticator', array[]::text[]);
select has_column('booking_guest', 'booking_guest_id');
select col_is_pk('booking_guest', 'booking_guest_id');
select col_type_is('booking_guest', 'booking_guest_id', 'integer');
select col_not_null('booking_guest', 'booking_guest_id');
select col_hasnt_default('booking_guest', 'booking_guest_id');
select has_column('booking_guest', 'booking_id');
select col_is_fk('booking_guest', 'booking_id');
select fk_ok('booking_guest', 'booking_id', 'booking', 'booking_id');
select col_type_is('booking_guest', 'booking_id', 'integer');
select col_not_null('booking_guest', 'booking_id');
select col_hasnt_default('booking_guest', 'booking_id');
select has_column('booking_guest', 'id_document_type_id');
select col_is_fk('booking_guest', 'id_document_type_id');
select fk_ok('booking_guest', 'id_document_type_id', 'id_document_type', 'id_document_type_id');
select col_type_is('booking_guest', 'id_document_type_id', 'character varying(1)');
select col_not_null('booking_guest', 'id_document_type_id');
select col_hasnt_default('booking_guest', 'id_document_type_id');
select has_column('booking_guest', 'id_document_number');
select col_type_is('booking_guest', 'id_document_number', 'text');
select col_not_null('booking_guest', 'id_document_number');
select col_hasnt_default('booking_guest', 'id_document_number');
select has_column('booking_guest', 'id_document_issue_date');
select col_type_is('booking_guest', 'id_document_issue_date', 'date');
select col_is_null('booking_guest', 'id_document_issue_date');
select col_hasnt_default('booking_guest', 'id_document_issue_date');
select has_column('booking_guest', 'given_name');
select col_type_is('booking_guest', 'given_name', 'text');
select col_not_null('booking_guest', 'given_name');
select col_hasnt_default('booking_guest', 'given_name');
select has_column('booking_guest', 'first_surname');
select col_type_is('booking_guest', 'first_surname', 'text');
select col_not_null('booking_guest', 'first_surname');
select col_hasnt_default('booking_guest', 'first_surname');
select has_column('booking_guest', 'second_surname');
select col_type_is('booking_guest', 'second_surname', 'text');
select col_not_null('booking_guest', 'second_surname');
select col_hasnt_default('booking_guest', 'second_surname');
select has_column('booking_guest', 'sex_id');
select col_is_fk('booking_guest', 'sex_id');
select fk_ok('booking_guest', 'sex_id', 'sex', 'sex_id');
select col_type_is('booking_guest', 'sex_id', 'character varying(1)');
select col_not_null('booking_guest', 'sex_id');
select col_hasnt_default('booking_guest', 'sex_id');
select has_column('booking_guest', 'birthdate');
select col_type_is('booking_guest', 'birthdate', 'date');
select col_not_null('booking_guest', 'birthdate');
select col_hasnt_default('booking_guest', 'birthdate');
select has_column('booking_guest', 'country_code');
select col_is_fk('booking_guest', 'country_code');
select fk_ok('booking_guest', 'country_code', 'country', 'country_code');
select col_type_is('booking_guest', 'country_code', 'country_code');
select col_not_null('booking_guest', 'country_code');
select col_hasnt_default('booking_guest', 'country_code');
select has_column('booking_guest', 'phone');
select col_type_is('booking_guest', 'phone', 'packed_phone_number');
select col_is_null('booking_guest', 'phone');
select col_hasnt_default('booking_guest', 'phone');
select has_column('booking_guest', 'address');
select col_type_is('booking_guest', 'address', 'text');
select col_not_null('booking_guest', 'address');
select col_hasnt_default('booking_guest', 'address');
select has_column('booking_guest', 'created_at');
select col_type_is('booking_guest', 'created_at', 'timestamp with time zone');
select col_not_null('booking_guest', 'created_at');
select col_has_default('booking_guest', 'created_at');
select col_default_is('booking_guest', 'created_at', 'CURRENT_TIMESTAMP');
select has_column('booking_guest', 'updated_at');
select col_type_is('booking_guest', 'updated_at', 'timestamp with time zone');
select col_not_null('booking_guest', 'updated_at');
select col_has_default('booking_guest', 'updated_at');
select col_default_is('booking_guest', 'updated_at', 'CURRENT_TIMESTAMP');
select *
from finish();
rollback;

89
test/check_in_guests.sql Normal file
View File

@ -0,0 +1,89 @@
-- Test check_in_guests
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(13);
set search_path to camper, public;
select has_function('camper', 'check_in_guests', array['integer', 'checked_in_guest[]']);
select function_lang_is('camper', 'check_in_guests', array['integer', 'checked_in_guest[]'], 'sql');
select function_returns('camper', 'check_in_guests', array['integer', 'checked_in_guest[]'], 'void');
select isnt_definer('camper', 'check_in_guests', array['integer', 'checked_in_guest[]']);
select volatility_is('camper', 'check_in_guests', array['integer', 'checked_in_guest[]'], 'volatile');
select function_privs_are('camper', 'check_in_guests', array ['integer', 'checked_in_guest[]'], 'guest', array[]::text[]);
select function_privs_are('camper', 'check_in_guests', array ['integer', 'checked_in_guest[]'], 'employee', array['EXECUTE']);
select function_privs_are('camper', 'check_in_guests', array ['integer', 'checked_in_guest[]'], 'admin', array['EXECUTE']);
select function_privs_are('camper', 'check_in_guests', array ['integer', 'checked_in_guest[]'], 'authenticator', array[]::text[]);
set client_min_messages to warning;
truncate booking_guest 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 (12, 2, 'Wooden lodge', 6, 7, '[1, 7]')
;
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, booking_status)
values (14, 2, 12, 'Holder 2', daterange('2024-01-18', '2024-01-29'), false, 'EUR', '', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 'confirmed')
, (16, 2, 12, 'Holder 4', daterange('2024-01-28', '2024-01-29'), true, 'USD', 'None', 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 'invoiced')
;
insert into booking_guest (booking_id, id_document_type_id, id_document_number, id_document_issue_date, given_name, first_surname, second_surname, sex_id, birthdate, country_code, phone, address, created_at, updated_at)
values (14, 'D', '4444444A', '2022-01-01', 'Given', 'First', 'Last', 'F', '1991-07-02', 'ES', '+32 977 977 977', 'Fake St., 123', '2023-03-03 03:03:03', '2023-03-03 03:03:03')
, (16, 'P', '123456-ABC', '2020-12-20', 'Mah', 'Name', '', 'M', '1980-03-31', 'JP', '123 123 123', 'Dunno', '2022-02-02 02:02:02', '2022-02-02 02:02:02')
;
select lives_ok(
$$ select check_in_guests(14, array[('C', 'UABCA', null, 'Jan-Michael', 'Vincent', '', 'M', '1944-07-15', 'US', '5673159097', 'One Rabbit Road'), ('D', '4444444A', '2021-02-02', 'Gibbon', 'Di', 'Luffy', 'M', '1981-09-25', 'GB', null, '')]::checked_in_guest[]) $$,
'Should be able to check-in guests for the first booking'
);
select lives_ok(
$$ select check_in_guests(16, array[('I', 'Huh?', '1999-09-09', 'Mayumi', 'Tanaka', '', 'F', '1955-01-15', 'JP', '5083262771', 'Somewhere')]::checked_in_guest[]) $$,
'Should be able to check-in guests for the second booking'
);
select bag_eq (
$$ select booking_id, booking_status from booking $$,
$$ values (14, 'checked-in')
, (16, 'invoiced')
$$,
'Should have updated the status if it was created or confirmed'
);
select bag_eq (
$$ select booking_id, id_document_type_id::text, id_document_number, id_document_issue_date::text, given_name, first_surname, second_surname, sex_id::text, birthdate::text, country_code::text, phone::text, address, created_at, updated_at from booking_guest $$,
$$ values (14, 'C', 'UABCA', null, 'Jan-Michael', 'Vincent', '', 'M', '1944-07-15', 'US', '+1 567-315-9097', 'One Rabbit Road', current_timestamp, current_timestamp)
, (14, 'D', '4444444A', '2021-02-02', 'Gibbon', 'Di', 'Luffy', 'M', '1981-09-25', 'GB', null, '', '2023-03-03 03:03:03', current_timestamp)
, (16, 'I', 'Huh?', '1999-09-09', 'Mayumi', 'Tanaka', '', 'F', '1955-01-15', 'JP', '+81 50-8326-2771', 'Somewhere', current_timestamp, current_timestamp)
$$,
'Should have updated guest information'
);
select *
from finish();
rollback;

30
test/checked_in_guest.sql Normal file
View File

@ -0,0 +1,30 @@
-- Test checked_in_guest
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(13);
set search_path to camper, public;
select has_composite('camper', 'checked_in_guest', 'Composite type camper.checked_in_guest should exist');
select columns_are('camper', 'checked_in_guest', array['id_document_type_id', 'id_document_number', 'id_document_issue_date', 'given_name', 'first_surname', 'second_surname', 'sex_id', 'birthdate', 'country_code', 'phone', 'address']);
select col_type_is('camper'::name, 'checked_in_guest'::name, 'id_document_type_id'::name, 'character varying(1)');
select col_type_is('camper'::name, 'checked_in_guest'::name, 'id_document_number'::name, 'text');
select col_type_is('camper'::name, 'checked_in_guest'::name, 'id_document_issue_date'::name, 'date');
select col_type_is('camper'::name, 'checked_in_guest'::name, 'given_name'::name, 'text');
select col_type_is('camper'::name, 'checked_in_guest'::name, 'first_surname'::name, 'text');
select col_type_is('camper'::name, 'checked_in_guest'::name, 'second_surname'::name, 'text');
select col_type_is('camper'::name, 'checked_in_guest'::name, 'sex_id'::name, 'character varying(1)');
select col_type_is('camper'::name, 'checked_in_guest'::name, 'birthdate'::name, 'date');
select col_type_is('camper'::name, 'checked_in_guest'::name, 'country_code'::name, 'country_code');
select col_type_is('camper'::name, 'checked_in_guest'::name, 'phone'::name, 'text');
select col_type_is('camper'::name, 'checked_in_guest'::name, 'address'::name, 'text');
select *
from finish();
rollback;

35
test/id_document_type.sql Normal file
View File

@ -0,0 +1,35 @@
-- Test id_document_type
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(15);
set search_path to camper, public;
select has_table('id_document_type');
select has_pk('id_document_type');
select table_privs_are('id_document_type', 'guest', array[]::text[]);
select table_privs_are('id_document_type', 'employee', array['SELECT']);
select table_privs_are('id_document_type', 'admin', array['SELECT']);
select table_privs_are('id_document_type', 'authenticator', array[]::text[]);
select has_column('id_document_type', 'id_document_type_id');
select col_is_pk('id_document_type', 'id_document_type_id');
select col_type_is('id_document_type', 'id_document_type_id', 'character varying(1)');
select col_not_null('id_document_type', 'id_document_type_id');
select col_hasnt_default('id_document_type', 'id_document_type_id');
select has_column('id_document_type', 'name');
select col_type_is('id_document_type', 'name', 'text');
select col_not_null('id_document_type', 'name');
select col_hasnt_default('id_document_type', 'name');
select *
from finish();
rollback;

View File

@ -0,0 +1,44 @@
-- Test id_document_type_i18n
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(23);
set search_path to camper, public;
select has_table('id_document_type_i18n');
select has_pk('id_document_type_i18n');
select col_is_pk('id_document_type_i18n', array['id_document_type_id', 'lang_tag']);
select table_privs_are('id_document_type_i18n', 'guest', array[]::text[]);
select table_privs_are('id_document_type_i18n', 'employee', array['SELECT']);
select table_privs_are('id_document_type_i18n', 'admin', array['SELECT']);
select table_privs_are('id_document_type_i18n', 'authenticator', array[]::text[]);
select has_column('id_document_type_i18n', 'id_document_type_id');
select col_is_fk('id_document_type_i18n', 'id_document_type_id');
select fk_ok('id_document_type_i18n', 'id_document_type_id', 'id_document_type', 'id_document_type_id');
select col_type_is('id_document_type_i18n', 'id_document_type_id', 'character varying(1)');
select col_not_null('id_document_type_i18n', 'id_document_type_id');
select col_hasnt_default('id_document_type_i18n', 'id_document_type_id');
select has_column('id_document_type_i18n', 'lang_tag');
select col_is_fk('id_document_type_i18n', 'lang_tag');
select fk_ok('id_document_type_i18n', 'lang_tag', 'language', 'lang_tag');
select col_type_is('id_document_type_i18n', 'lang_tag', 'text');
select col_not_null('id_document_type_i18n', 'lang_tag');
select col_hasnt_default('id_document_type_i18n', 'lang_tag');
select has_column('id_document_type_i18n', 'name');
select col_type_is('id_document_type_i18n', 'name', 'text');
select col_not_null('id_document_type_i18n', 'name');
select col_hasnt_default('id_document_type_i18n', 'name');
select *
from finish();
rollback;

34
test/sex.sql Normal file
View File

@ -0,0 +1,34 @@
-- Test sex
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(15);
set search_path to camper, public;
select has_table('sex');
select has_pk('sex');
select table_privs_are('sex', 'guest', array[]::text[]);
select table_privs_are('sex', 'employee', array['SELECT']);
select table_privs_are('sex', 'admin', array['SELECT']);
select table_privs_are('sex', 'authenticator', array[]::text[]);
select has_column('sex', 'sex_id');
select col_is_pk('sex', 'sex_id');
select col_type_is('sex', 'sex_id', 'character varying(1)');
select col_not_null('sex', 'sex_id');
select col_hasnt_default('sex', 'sex_id');
select has_column('sex', 'name');
select col_type_is('sex', 'name', 'text');
select col_not_null('sex', 'name');
select col_hasnt_default('sex', 'name');
select *
from finish();
rollback;

44
test/sex_i18n.sql Normal file
View File

@ -0,0 +1,44 @@
-- Test sex_i18n
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(23);
set search_path to camper, public;
select has_table('sex_i18n');
select has_pk('sex_i18n');
select col_is_pk('sex_i18n', array['sex_id', 'lang_tag']);
select table_privs_are('sex_i18n', 'guest', array[]::text[]);
select table_privs_are('sex_i18n', 'employee', array['SELECT']);
select table_privs_are('sex_i18n', 'admin', array['SELECT']);
select table_privs_are('sex_i18n', 'authenticator', array[]::text[]);
select has_column('sex_i18n', 'sex_id');
select col_is_fk('sex_i18n', 'sex_id');
select fk_ok('sex_i18n', 'sex_id', 'sex', 'sex_id');
select col_type_is('sex_i18n', 'sex_id', 'character varying(1)');
select col_not_null('sex_i18n', 'sex_id');
select col_hasnt_default('sex_i18n', 'sex_id');
select has_column('sex_i18n', 'lang_tag');
select col_is_fk('sex_i18n', 'lang_tag');
select fk_ok('sex_i18n', 'lang_tag', 'language', 'lang_tag');
select col_type_is('sex_i18n', 'lang_tag', 'text');
select col_not_null('sex_i18n', 'lang_tag');
select col_hasnt_default('sex_i18n', 'lang_tag');
select has_column('sex_i18n', 'name');
select col_type_is('sex_i18n', 'name', 'text');
select col_not_null('sex_i18n', 'name');
select col_hasnt_default('sex_i18n', 'name');
select *
from finish();
rollback;

View File

@ -0,0 +1,35 @@
-- Verify camper:available_id_document_types on pg
begin;
set search_path to camper;
select 1 / count(*) from id_document_type where id_document_type_id = 'D' and name = 'DNI';
select 1 / count(*) from id_document_type where id_document_type_id = 'P' and name = 'Passport';
select 1 / count(*) from id_document_type where id_document_type_id = 'C' and name = 'Driving license';
select 1 / count(*) from id_document_type where id_document_type_id = 'I' and name = 'Identification document';
select 1 / count(*) from id_document_type where id_document_type_id = 'N' and name = 'Spanish residence permit';
select 1 / count(*) from id_document_type where id_document_type_id = 'X' and name = 'Residence permit from another Member State of the European Union';
select 1 / count(*) from id_document_type_i18n where id_document_type_id = 'D' and lang_tag = 'ca' and name = 'DNI';
select 1 / count(*) from id_document_type_i18n where id_document_type_id = 'P' and lang_tag = 'ca' and name = 'Passaport';
select 1 / count(*) from id_document_type_i18n where id_document_type_id = 'C' and lang_tag = 'ca' and name = 'Permís de conduir';
select 1 / count(*) from id_document_type_i18n where id_document_type_id = 'I' and lang_tag = 'ca' and name = 'Carta o document didentitat';
select 1 / count(*) from id_document_type_i18n where id_document_type_id = 'N' and lang_tag = 'ca' and name = 'Permís de residència espanyol';
select 1 / count(*) from id_document_type_i18n where id_document_type_id = 'X' and lang_tag = 'ca' and name = 'Permís de residència dun altre estat membre de la Unió Europea';
select 1 / count(*) from id_document_type_i18n where id_document_type_id = 'D' and lang_tag = 'es' and name = 'DNI';
select 1 / count(*) from id_document_type_i18n where id_document_type_id = 'P' and lang_tag = 'es' and name = 'Pasaporte';
select 1 / count(*) from id_document_type_i18n where id_document_type_id = 'C' and lang_tag = 'es' and name = 'Permiso de conducir';
select 1 / count(*) from id_document_type_i18n where id_document_type_id = 'I' and lang_tag = 'es' and name = 'Carta o documento de identidad';
select 1 / count(*) from id_document_type_i18n where id_document_type_id = 'N' and lang_tag = 'es' and name = 'Permiso de residencia español';
select 1 / count(*) from id_document_type_i18n where id_document_type_id = 'X' and lang_tag = 'es' and name = 'Permiso de residencia de otro Estado Miembro de la Unión Europea';
select 1 / count(*) from id_document_type_i18n where id_document_type_id = 'D' and lang_tag = 'fr' and name = 'DNI';
select 1 / count(*) from id_document_type_i18n where id_document_type_id = 'P' and lang_tag = 'fr' and name = 'Passeport';
select 1 / count(*) from id_document_type_i18n where id_document_type_id = 'C' and lang_tag = 'fr' and name = 'Permis de conduire';
select 1 / count(*) from id_document_type_i18n where id_document_type_id = 'I' and lang_tag = 'fr' and name = 'Carte didentité';
select 1 / count(*) from id_document_type_i18n where id_document_type_id = 'N' and lang_tag = 'fr' and name = 'Permis de séjour espagnol';
select 1 / count(*) from id_document_type_i18n where id_document_type_id = 'X' and lang_tag = 'fr' and name = 'Titre de séjour dun autre État membre de lUnion européenne';
rollback;

View File

@ -0,0 +1,19 @@
-- Verify camper:available_sexes on pg
begin;
set search_path to camper;
select 1 / count(*) from sex where sex_id = 'F' and name = 'Female';
select 1 / count(*) from sex where sex_id = 'M' and name = 'Male';
select 1 / count(*) from sex_i18n where sex_id = 'F' and lang_tag = 'ca' and name = 'Femení';
select 1 / count(*) from sex_i18n where sex_id = 'M' and lang_tag = 'ca' and name = 'Masculí';
select 1 / count(*) from sex_i18n where sex_id = 'F' and lang_tag = 'es' and name = 'Femenino';
select 1 / count(*) from sex_i18n where sex_id = 'M' and lang_tag = 'es' and name = 'Masculino';
select 1 / count(*) from sex_i18n where sex_id = 'F' and lang_tag = 'fr' and name = 'Féminin';
select 1 / count(*) from sex_i18n where sex_id = 'M' and lang_tag = 'fr' and name = 'Masculin';
rollback;

23
verify/booking_guest.sql Normal file
View File

@ -0,0 +1,23 @@
-- Verify camper:booking_guest on pg
begin;
select booking_guest_id
, booking_id
, id_document_type_id
, id_document_number
, id_document_issue_date
, given_name
, first_surname
, second_surname
, sex_id
, birthdate
, country_code
, phone
, address
, created_at
, updated_at
from camper.booking_guest
where false;
rollback;

View File

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

View File

@ -0,0 +1,7 @@
-- Verify camper:checked_in_guest on pg
begin;
select pg_catalog.has_type_privilege('camper.checked_in_guest', 'usage');
rollback;

View File

@ -0,0 +1,10 @@
-- Verify camper:id_document_type on pg
begin;
select id_document_type_id
, name
from camper.id_document_type
where false;
rollback;

View File

@ -0,0 +1,11 @@
-- Verify camper:id_document_type_i18n on pg
begin;
select id_document_type_id
, lang_tag
, name
from camper.id_document_type_i18n
where false;
rollback;

10
verify/sex.sql Normal file
View File

@ -0,0 +1,10 @@
-- Verify camper:sex on pg
begin;
select sex_id
, name
from camper.sex
where false;
rollback;

11
verify/sex_i18n.sql Normal file
View File

@ -0,0 +1,11 @@
-- Verify camper:sex_i18n on pg
begin;
select sex_id
, lang_tag
, name
from camper.sex_i18n
where false;
rollback;

View File

@ -919,3 +919,83 @@ label[x-show] > span, label[x-show] > br {
}
/*</editor-fold>*/
/*<editor-fold desc="Check-in guests">*/
#checkin-guests {
display: flex;
flex-direction: column;
}
#checkin-guests :is(label, fieldset) {
margin-top: 0;
}
#checkin-guests br {
display: none;
}
#checkin-guests > fieldset {
counter-reset: guest-count;
}
#checkin-guests > fieldset > fieldset {
counter-increment: guest-count;
position: relative;
display: grid;
grid-template-columns: repeat(4, 1fr);
border-bottom: .5px solid;
padding: 2.25em 0 1em;
column-gap: 1ch;
row-gap: .75em;
}
#checkin-guests > fieldset > fieldset::before {
content: '#' counter(guest-count);
position: absolute;
}
#checkin-guests fieldset fieldset:first-of-type button {
display: none;
}
#checkin-guests fieldset button {
position: absolute;
right: 0;
width: min-content;
min-width: unset;
line-height: 1;
border: none;
cursor: pointer;
}
#checkin-guests > fieldset > fieldset::before,
#checkin-guests fieldset button {
top: .25em;
}
#checkin-guests fieldset button:hover {
background-color: var(--camper--color--light-gray);
}
#checkin-guests fieldset button::before {
content: '⌫';
}
#checkin-guests :is(input, select) {
width: 100%;
}
#checkin-guests label:nth-of-type(2), #checkin-guests label:nth-of-type(9) {
grid-column: span 2;
}
#checkin-guests label:last-of-type {
grid-column: 1 / -1;
}
#checkin-guests footer {
display: flex;
justify-content: space-between;
}
/*</editor-fold>*/

View File

@ -0,0 +1,34 @@
<!--
SPDX-FileCopyrightText: 2023 jordi fita mas <jordi@tandem.blog>
SPDX-License-Identifier: AGPL-3.0-only
-->
{{ define "title" -}}
{{( pgettext "Check-in Booking" "title" )}}
{{- end }}
{{ define "breadcrumb" -}}
<li><a href="./">{{( pgettext "Bookings" "title" )}}</a></li>
{{- end }}
{{ define "content" -}}
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/booking.checkinForm*/ -}}
<a href="/admin/bookings/{{ .Slug }}">{{( pgettext "Edit Booking" "action" )}}</a>
<h2>{{ template "title" .}}</h2>
<form method="post" action="/admin/bookings/{{ .Slug }}/check-in" id="checkin-guests">
{{ CSRFInput }}
<fieldset>
<legend>{{( pgettext "Guests" "title" )}}</legend>
{{ range .Guests -}}
{{ template "guest.gohtml" . }}
{{- end }}
</fieldset>
<footer>
<button type="submit">{{( pgettext "Check-in" "action" )}}</button>
<button type="button"
data-hx-get="/admin/bookings/{{ .Slug }}/guest"
data-hx-target="#checkin-guests > fieldset"
data-hx-swap="beforeend"
>{{( pgettext "Add another guest" "action" )}}</button>
</footer>
</form>
{{- end }}

View File

@ -21,6 +21,9 @@
{{ define "content" -}}
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/booking.adminBookingForm*/ -}}
{{ if .ID -}}
<a href="{{ .URL }}/check-in">{{( pgettext "Check-in Booking" "action" )}}</a>
{{- end }}
<h2>{{ template "title" .}}</h2>
<form id="booking-form"
data-hx-ext="morph"

View File

@ -0,0 +1,139 @@
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/booking.guestForm*/ -}}
<fieldset>
<button type="button"
onclick="this.closest('fieldset').remove(this);return false;"
><span class="sr-only">{{( pgettext "Remove" "action" )}}</span></button>
{{ with .IDDocumentNumber -}}
<label>
{{( pgettext "ID document number" "input" )}}<br>
<input type="text"
required
name="{{ .Name }}"
value="{{ .Val }}"
{{ template "error-attrs" . }}
><br>
{{ template "error-message" . }}
</label>
{{- end }}
{{ with .IDDocumentType -}}
<label>
{{( pgettext "ID document type" "input" )}}<br>
<select name="{{ .Name }}"
required
{{ template "error-attrs" . }}
>
<option value="">{{( gettext "Choose an ID document type" )}}</option>
{{ template "list-options" . }}
</select><br>
{{ template "error-message" . }}
</label>
{{- end }}
{{ with .IDDocumentDate -}}
<label>
{{( pgettext "ID document issue date (if any)" "input" )}}<br>
<input type="date"
max="{{ today }}"
name="{{ .Name }}"
value="{{ .Val }}"
{{ template "error-attrs" . }}
><br>
{{ template "error-message" . }}
</label>
{{- end }}
{{ with .FirstSurname -}}
<label>
{{( pgettext "First surname" "input" )}}<br>
<input type="text"
required
name="{{ .Name }}"
value="{{ .Val }}"
{{ template "error-attrs" . }}
><br>
{{ template "error-message" . }}
</label>
{{- end }}
{{ with .SecondSurname -}}
<label>
{{( pgettext "Second surname (if has one)" "input" )}}<br>
<input type="text"
name="{{ .Name }}"
value="{{ .Val }}"
{{ template "error-attrs" . }}
><br>
{{ template "error-message" . }}
</label>
{{- end }}
{{ with .GivenName -}}
<label>
{{( pgettext "Given name" "input" )}}<br>
<input type="text"
required
name="{{ .Name }}"
value="{{ .Val }}"
{{ template "error-attrs" . }}
><br>
{{ template "error-message" . }}
</label>
{{- end }}
{{ with .Sex -}}
<label>
{{( pgettext "Sex" "input" )}}<br>
<select name="{{ .Name }}"
required
{{ template "error-attrs" . }}
>
<option value="">{{( gettext "Choose a sex" )}}</option>
{{ template "list-options" . }}
</select><br>
{{ template "error-message" . }}
</label>
{{- end }}
{{ with .Birthdate -}}
<label>
{{( pgettext "Birthdate" "input" )}}<br>
<input type="date"
required
max="{{ today }}"
name="{{ .Name }}"
value="{{ .Val }}"
{{ template "error-attrs" . }}
><br>
{{ template "error-message" . }}
</label>
{{- end }}
{{ with .Country -}}
<label>
{{( pgettext "Nationality" "input" )}}<br>
<select name="{{ .Name }}"
required
{{ template "error-attrs" . }}
>
<option value="">{{( gettext "Choose a country" )}}</option>
{{ template "list-options" . }}
</select><br>
{{ template "error-message" . }}
</label>
{{- end }}
{{ with .Phone -}}
<label>
{{( pgettext "Phone (optional)" "input" )}}<br>
<input type="tel"
name="{{ .Name }}"
value="{{ .Val }}"
{{ template "error-attrs" . }}
><br>
{{ template "error-message" . }}
</label>
{{- end }}
{{ with .Address -}}
<label>
{{( pgettext "Address (optional)" "input" )}}<br>
<input type="text"
name="{{ .Name }}"
value="{{ .Val }}"
{{ template "error-attrs" . }}
><br>
{{ template "error-message" . }}
</label>
{{- end }}
</fieldset>