Compute and show the “cart” for the booking form

I have to ask number and age ranges of hosts of guests for all campsite
types, not only those that have price options for adults, children, etc.
because i must compute the tourist tax for adults.  These numbers will
be used to generate de rows for guests when actually creating the
booking, which is not done already.

To satisfy the campsite types that do have a price per guest, not only
per night, i had to add the prices for each range in the
campsite_type_cost relation.  If a campsite type does not have price
per person, then that should be zero; the website then does not display
the price.

The minimal price for any campsite type is one adult for one night,
thus to compute the price i need at least the campsite type, the dates,
and the number of adults, that has a minimum of one.  I changed the
order of the form to ask for these values first, so i can compute the
initial price as soon as possible.  To help further, i show the
<fieldset>s progressively when visitors select options.
This commit is contained in:
jordi fita mas 2024-02-04 06:37:25 +01:00
parent ebcc2a017f
commit 2c36e45663
24 changed files with 2087 additions and 964 deletions

View File

@ -1208,50 +1208,48 @@ values (92, 'en', 'Peak season')
, (94, 'es', 'Temporada baja')
;
select set_season_range(92, '[2023-04-06, 2023-04-10]');
select set_season_range(94, '[2023-04-11, 2023-04-27]');
select set_season_range(93, '[2023-04-28, 2023-04-30]');
select set_season_range(94, '[2023-05-01, 2023-06-01]');
select set_season_range(93, '[2023-06-02, 2023-06-03]');
select set_season_range(94, '[2023-06-04, 2023-06-08]');
select set_season_range(93, '[2023-06-09, 2023-06-10]');
select set_season_range(94, '[2023-06-11, 2023-06-15]');
select set_season_range(93, '[2023-06-16, 2023-06-22]');
select set_season_range(92, '[2023-06-23, 2023-06-25]');
select set_season_range(93, '[2023-06-26, 2023-06-30]');
select set_season_range(92, '[2023-07-01, 2023-08-27]');
select set_season_range(93, '[2023-08-28, 2023-09-02]');
select set_season_range(94, '[2023-09-03, 2023-09-07]');
select set_season_range(93, '[2023-09-08, 2023-09-10]');
select set_season_range(94, '[2023-09-11, 2023-09-14]');
select set_season_range(93, '[2023-09-15, 2023-09-16]');
select set_season_range(94, '[2023-09-17, 2023-09-21]');
select set_season_range(93, '[2023-09-22, 2023-09-23]');
select set_season_range(94, '[2023-09-24, 2023-09-28]');
select set_season_range(93, '[2023-09-29, 2023-09-30]');
select set_season_range(94, '[2023-10-01, 2023-10-12]');
select set_season_range(92, daterange(make_date(extract(year from current_date)::int, 4, 6), make_date(extract(year from current_date)::int, 4, 11)));
select set_season_range(94, daterange(make_date(extract(year from current_date)::int, 4, 11), make_date(extract(year from current_date)::int, 4, 28)));
select set_season_range(93, daterange(make_date(extract(year from current_date)::int, 4, 28), make_date(extract(year from current_date)::int, 5, 1)));
select set_season_range(94, daterange(make_date(extract(year from current_date)::int, 5, 1), make_date(extract(year from current_date)::int, 6, 2)));
select set_season_range(93, daterange(make_date(extract(year from current_date)::int, 6, 2), make_date(extract(year from current_date)::int, 6, 4)));
select set_season_range(94, daterange(make_date(extract(year from current_date)::int, 6, 4), make_date(extract(year from current_date)::int, 6, 9)));
select set_season_range(93, daterange(make_date(extract(year from current_date)::int, 6, 9), make_date(extract(year from current_date)::int, 6, 11)));
select set_season_range(94, daterange(make_date(extract(year from current_date)::int, 6, 11), make_date(extract(year from current_date)::int, 6, 16)));
select set_season_range(93, daterange(make_date(extract(year from current_date)::int, 6, 16), make_date(extract(year from current_date)::int, 6, 23)));
select set_season_range(92, daterange(make_date(extract(year from current_date)::int, 6, 23), make_date(extract(year from current_date)::int, 6, 26)));
select set_season_range(93, daterange(make_date(extract(year from current_date)::int, 6, 26), make_date(extract(year from current_date)::int, 7, 1)));
select set_season_range(92, daterange(make_date(extract(year from current_date)::int, 7, 1), make_date(extract(year from current_date)::int, 8, 28)));
select set_season_range(93, daterange(make_date(extract(year from current_date)::int, 8, 28), make_date(extract(year from current_date)::int, 9, 3)));
select set_season_range(94, daterange(make_date(extract(year from current_date)::int, 9, 3), make_date(extract(year from current_date)::int, 9, 8)));
select set_season_range(93, daterange(make_date(extract(year from current_date)::int, 9, 8), make_date(extract(year from current_date)::int, 9, 11)));
select set_season_range(94, daterange(make_date(extract(year from current_date)::int, 9, 11), make_date(extract(year from current_date)::int, 9, 15)));
select set_season_range(93, daterange(make_date(extract(year from current_date)::int, 9, 15), make_date(extract(year from current_date)::int, 9, 17)));
select set_season_range(94, daterange(make_date(extract(year from current_date)::int, 9, 17), make_date(extract(year from current_date)::int, 9, 22)));
select set_season_range(93, daterange(make_date(extract(year from current_date)::int, 9, 22), make_date(extract(year from current_date)::int, 9, 24)));
select set_season_range(94, daterange(make_date(extract(year from current_date)::int, 9, 24), make_date(extract(year from current_date)::int, 9, 29)));
select set_season_range(93, daterange(make_date(extract(year from current_date)::int, 9, 29), make_date(extract(year from current_date)::int, 10, 1)));
select set_season_range(94, daterange(make_date(extract(year from current_date)::int, 10, 1), make_date(extract(year from current_date)::int, 10, 13)));
select set_campsite_type_cost (slug, 92, '20.00') from campsite_type where campsite_type_id = 72;
select set_campsite_type_cost (slug, 93, '16.50') from campsite_type where campsite_type_id = 72;
select set_campsite_type_cost (slug, 94, '12.50') from campsite_type where campsite_type_id = 72;
select set_campsite_type_cost (slug, 92, '20.00') from campsite_type where campsite_type_id = 73;
select set_campsite_type_cost (slug, 93, '16.50') from campsite_type where campsite_type_id = 73;
select set_campsite_type_cost (slug, 94, '12.50') from campsite_type where campsite_type_id = 73;
select set_campsite_type_cost (slug, 92, '20.00') from campsite_type where campsite_type_id = 74;
select set_campsite_type_cost (slug, 93, '16.50') from campsite_type where campsite_type_id = 74;
select set_campsite_type_cost (slug, 94, '12.50') from campsite_type where campsite_type_id = 74;
select set_campsite_type_cost (slug, 92, '20.00') from campsite_type where campsite_type_id = 75;
select set_campsite_type_cost (slug, 93, '16.50') from campsite_type where campsite_type_id = 75;
select set_campsite_type_cost (slug, 94, '12.50') from campsite_type where campsite_type_id = 75;
select set_campsite_type_cost (slug, 92, '20.00') from campsite_type where campsite_type_id = 76;
select set_campsite_type_cost (slug, 93, '16.50') from campsite_type where campsite_type_id = 76;
select set_campsite_type_cost (slug, 94, '12.50') from campsite_type where campsite_type_id = 76;
select set_campsite_type_cost (slug, 92, '4.00', '7.95', '7.95', '6.40') from campsite_type where campsite_type_id = 72;
select set_campsite_type_cost (slug, 93, '2.00', '7.40', '7.40', '5.90') from campsite_type where campsite_type_id = 72;
select set_campsite_type_cost (slug, 94, '0.00', '6.60', '6.60', '5.40') from campsite_type where campsite_type_id = 72;
select set_campsite_type_cost (slug, 92, '120.00', '0.0', '0.0', '0.0') from campsite_type where campsite_type_id = 73;
select set_campsite_type_cost (slug, 93, '100.00', '0.0', '0.0', '0.0') from campsite_type where campsite_type_id = 73;
select set_campsite_type_cost (slug, 94, '80.00', '0.0', '0.0', '0.0') from campsite_type where campsite_type_id = 73;
select set_campsite_type_cost (slug, 92, '170.00', '0.0', '0.0', '0.0') from campsite_type where campsite_type_id = 74;
select set_campsite_type_cost (slug, 93, '135.00', '0.0', '0.0', '0.0') from campsite_type where campsite_type_id = 74;
select set_campsite_type_cost (slug, 94, '105.00', '0.0', '0.0', '0.0') from campsite_type where campsite_type_id = 74;
select set_campsite_type_cost (slug, 92, '180.00', '0.0', '0.0', '0.0') from campsite_type where campsite_type_id = 75;
select set_campsite_type_cost (slug, 93, '145.00', '0.0', '0.0', '0.0') from campsite_type where campsite_type_id = 75;
select set_campsite_type_cost (slug, 94, '115.00', '0.0', '0.0', '0.0') from campsite_type where campsite_type_id = 75;
select set_campsite_type_cost (slug, 92, '200.00', '0.0', '0.0', '0.0') from campsite_type where campsite_type_id = 76;
select set_campsite_type_cost (slug, 93, '165.00', '0.0', '0.0', '0.0') from campsite_type where campsite_type_id = 76;
select set_campsite_type_cost (slug, 94, '125.00', '0.0', '0.0', '0.0') from campsite_type where campsite_type_id = 76;
alter table campsite_type_option alter column campsite_type_option_id restart with 102;
insert into campsite_type_option (campsite_type_id, name, range)
values (72, 'Adults', '[1, 6]')
, (72, 'Nens (de 2 a 10 anys)', '[0, 4]')
, (72, 'Tenda petita (màx. 2 pers.)', '[0, 3]')
values (72, 'Tenda petita (màx. 2 pers.)', '[0, 3]')
, (72, 'Tenda gran', '[0, 3]')
, (72, 'Caravana', '[0, 3]')
, (72, 'Autocaravana', '[0, 3]')
@ -1261,61 +1259,47 @@ values (72, 'Adults', '[1, 6]')
, (72, 'Punt electricitat', '[0, 4]')
;
insert into campsite_type_option_i18n (campsite_type_option_id, lang_tag, name)
values (102, 'en', 'Adults')
, (102, 'es', 'Adultos')
, (103, 'en', 'Children (from 2 to 10 years)')
, (103, 'es', 'Niños (de 2 a 10 años)')
, (104, 'en', 'Small tent (2 pax max.)')
, (104, 'es', 'Tienda pequeña (máx. 2 pers.)')
, (105, 'en', 'Big tent')
, (105, 'es', 'Tienda grande')
, (106, 'en', 'Caravan')
, (106, 'es', 'Caravana')
, (107, 'en', 'Motorhome')
, (107, 'es', 'Autocaravana')
, (108, 'en', 'Van')
, (108, 'es', 'Furgoneta')
, (109, 'en', 'Car')
, (109, 'es', 'Coche')
, (110, 'en', 'Motorcycle')
, (110, 'es', 'Moto')
, (111, 'en', 'Electricity')
, (111, 'es', 'Puntos de electricidad')
;
select translate_campsite_type_option(102, 'en', 'Small tent (2 pax max.)');
select translate_campsite_type_option(102, 'es', 'Tienda pequeña (máx. 2 pers.)');
select translate_campsite_type_option(103, 'en', 'Big tent');
select translate_campsite_type_option(103, 'es', 'Tienda grande');
select translate_campsite_type_option(104, 'en', 'Caravan');
select translate_campsite_type_option(104, 'es', 'Caravana');
select translate_campsite_type_option(105, 'en', 'Motorhome');
select translate_campsite_type_option(105, 'es', 'Autocaravana');
select translate_campsite_type_option(106, 'en', 'Van');
select translate_campsite_type_option(106, 'es', 'Furgoneta');
select translate_campsite_type_option(107, 'en', 'Car');
select translate_campsite_type_option(107, 'es', 'Coche');
select translate_campsite_type_option(108, 'en', 'Motorcycle');
select translate_campsite_type_option(108, 'es', 'Moto');
select translate_campsite_type_option(109, 'en', 'Electricity');
select translate_campsite_type_option(109, 'es', 'Puntos de electricidad');
insert into campsite_type_option_cost (campsite_type_option_id, season_id, cost_per_night)
values (102, 92, 795)
, (102, 93, 740)
, (102, 94, 660)
, (103, 92, 640)
, (103, 93, 590)
, (103, 94, 540)
, (104, 92, 620)
, (104, 93, 550)
, (104, 94, 500)
, (105, 92, 800)
, (105, 93, 720)
, (105, 94, 620)
, (106, 92, 900)
, (106, 93, 750)
, (106, 94, 650)
, (107, 92, 1220)
, (107, 93, 1100)
, (107, 94, 950)
, (108, 92, 950)
, (108, 93, 850)
, (108, 94, 750)
, (109, 92, 700)
, (109, 93, 630)
, (109, 94, 530)
, (110, 92, 400)
, (110, 93, 360)
, (110, 94, 360)
, (111, 92, 690)
, (111, 93, 610)
, (111, 94, 590)
;
select set_campsite_type_option_cost(102, 92, '6.20');
select set_campsite_type_option_cost(102, 93, '5.50');
select set_campsite_type_option_cost(102, 94, '5.00');
select set_campsite_type_option_cost(103, 92, '8.00');
select set_campsite_type_option_cost(103, 93, '7.20');
select set_campsite_type_option_cost(103, 94, '6.20');
select set_campsite_type_option_cost(104, 92, '9.00');
select set_campsite_type_option_cost(104, 93, '7.50');
select set_campsite_type_option_cost(104, 94, '6.50');
select set_campsite_type_option_cost(105, 92, '12.20');
select set_campsite_type_option_cost(105, 93, '11.00');
select set_campsite_type_option_cost(105, 94, '9.50');
select set_campsite_type_option_cost(106, 92, '9.50');
select set_campsite_type_option_cost(106, 93, '8.50');
select set_campsite_type_option_cost(106, 94, '7.50');
select set_campsite_type_option_cost(107, 92, '7.00');
select set_campsite_type_option_cost(107, 93, '6.30');
select set_campsite_type_option_cost(107, 94, '5.30');
select set_campsite_type_option_cost(108, 92, '4.00');
select set_campsite_type_option_cost(108, 93, '3.60');
select set_campsite_type_option_cost(108, 94, '3.60');
select set_campsite_type_option_cost(109, 92, '6.90');
select set_campsite_type_option_cost(109, 93, '6.10');
select set_campsite_type_option_cost(109, 94, '5.90');
insert into campsite_type_carousel (campsite_type_id, media_id, caption)
values (72, 77, 'Llegenda')

View File

@ -0,0 +1,18 @@
-- Deploy camper:campsite_type_cost__per_age to pg
-- requires: campsite_type_cost
begin;
alter table camper.campsite_type_cost
add column cost_per_adult integer not null default 0 constraint per_adult_not_negative check(cost_per_adult >= 0)
, add column cost_per_teenager integer not null default 0 constraint per_teenager_not_negative check(cost_per_teenager >= 0)
, add column cost_per_child integer not null default 0 constraint per_child_not_negative check(cost_per_child >= 0)
;
alter table camper.campsite_type_cost
alter column cost_per_adult drop default
, alter column cost_per_teenager drop default
, alter column cost_per_child drop default
;
commit;

View File

@ -3,6 +3,8 @@
-- requires: schema_camper
-- requires: campsite_type_cost
-- requires: parse_price
-- requires: campsite_type__bookable_nights
-- requires: campsite_type_cost__per_age
begin;
@ -10,20 +12,25 @@ set search_path to camper, public;
drop function if exists set_campsite_type_cost(uuid, integer, integer, text, integer);
create or replace function set_campsite_type_cost(slug uuid, season_id integer, cost_per_night text, decimal_places integer default 2) returns void as
create or replace function set_campsite_type_cost(slug uuid, season_id integer, per_night text, per_adult text, per_teenager text, per_child text) returns void as
$$
insert into campsite_type_cost (campsite_type_id, season_id, cost_per_night)
select campsite_type_id, season_id, parse_price(cost_per_night, decimal_places)
insert into campsite_type_cost (campsite_type_id, season_id, cost_per_night, cost_per_adult, cost_per_teenager, cost_per_child)
select campsite_type_id, season_id, parse_price(per_night, decimal_digits), parse_price(per_adult, decimal_digits), parse_price(per_teenager, decimal_digits), parse_price(per_child, decimal_digits)
from campsite_type
join company using (company_id)
join currency using (currency_code)
where campsite_type.slug = set_campsite_type_cost.slug
on conflict (campsite_type_id, season_id) do update
set cost_per_night = excluded.cost_per_night
, cost_per_adult = excluded.cost_per_adult
, cost_per_teenager = excluded.cost_per_teenager
, cost_per_child = excluded.cost_per_child
;
$$
language sql
;
revoke execute on function set_campsite_type_cost(uuid, integer, text, integer) from public;
grant execute on function set_campsite_type_cost(uuid, integer, text, integer) to admin;
revoke execute on function set_campsite_type_cost(uuid, integer, text, text, text, text) from public;
grant execute on function set_campsite_type_cost(uuid, integer, text, text, text, text) to admin;
commit;

201
pkg/booking/cart.go Normal file
View File

@ -0,0 +1,201 @@
package booking
import (
"context"
"net/http"
"strconv"
"time"
"github.com/jackc/pgx/v4"
"dev.tandem.ws/tandem/camper/pkg/auth"
"dev.tandem.ws/tandem/camper/pkg/database"
httplib "dev.tandem.ws/tandem/camper/pkg/http"
"dev.tandem.ws/tandem/camper/pkg/locale"
"dev.tandem.ws/tandem/camper/pkg/template"
)
func handleBookingCart(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
var head string
head, r.URL.Path = httplib.ShiftPath(r.URL.Path)
switch head {
case "":
switch r.Method {
case http.MethodGet:
f := newBookingForm(r.Context(), company, conn, user.Locale)
if err := f.Parse(r); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if cart, err := computeCart(r.Context(), conn, f); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
} else {
cart.MustRender(w, r, user, company)
}
default:
httplib.MethodNotAllowed(w, r, http.MethodGet)
}
default:
http.NotFound(w, r)
}
}
type bookingCart struct {
Lines []*cartLine
Total string
}
type cartLine struct {
Concept string
Units int
Subtotal string
}
func (cart *bookingCart) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
template.MustRenderPublicNoLayout(w, r, user, company, "booking/cart.gohtml", cart)
}
func computeCart(ctx context.Context, conn *database.Conn, f *bookingForm) (*bookingCart, error) {
cart := &bookingCart{
Total: "0.0",
}
campsiteType := f.CampsiteType.String()
if campsiteType == "" {
return cart, nil
}
arrivalDate, err := time.Parse(database.ISODateFormat, f.ArrivalDate.Val)
if err != nil {
return cart, nil
}
departureDate, err := time.Parse(database.ISODateFormat, f.DepartureDate.Val)
if err != nil {
return cart, nil
}
numAdults, err := strconv.Atoi(f.NumberAdults.Val)
if err != nil {
return cart, nil
}
numTeenagers, err := strconv.Atoi(f.NumberTeenagers.Val)
if err != nil {
return cart, nil
}
numChildren, err := strconv.Atoi(f.NumberChildren.Val)
if err != nil {
return cart, nil
}
optionMap := make(map[int]*campsiteTypeOption)
typeOptions := f.CampsiteTypeOptions[campsiteType]
optionIDs := make([]int, 0, len(typeOptions))
optionUnits := make([]int, 0, len(typeOptions))
for _, option := range typeOptions {
units, _ := strconv.Atoi(option.Input.Val)
if units < 1 {
continue
}
optionMap[option.ID] = option
optionIDs = append(optionIDs, option.ID)
optionUnits = append(optionUnits, units)
}
row := conn.QueryRow(ctx, `
with per_person as (
select count(*) as num_nights
, sum(cost_per_night)::integer as nights
, sum(cost_per_adult * $4)::integer as adults
, sum(cost_per_teenager * $5)::integer as teenagers
, sum(cost_per_child * $6)::integer as children
, sum(tourist_tax * $4)::integer as tourist_tax
, max(decimal_digits) as decimal_digits
from generate_series($1, $2, interval '1 day') as date(day)
left join season_calendar on season_range @> date.day::date
left join season using (season_id)
left join campsite_type using (company_id)
left join campsite_type_cost using (campsite_type_id, season_id)
left join company using (company_id)
left join currency using (currency_code)
where campsite_type.slug = $3
), per_option as (
select campsite_type_option_id
, sum(cost_per_night * units)::integer as option_cost
from generate_series($1, $2, interval '1 day') as date(day)
join season_calendar on season_range @> date.day::date
join campsite_type_option_cost using (season_id)
join unnest($7::integer[], $8::integer[]) as option_units(campsite_type_option_id, units) using (campsite_type_option_id)
group by campsite_type_option_id
union all select -1, 0
)
select num_nights
, coalesce(to_price(nights, decimal_digits), '')
, coalesce(to_price(adults, decimal_digits), '')
, coalesce(to_price(teenagers, decimal_digits), '')
, coalesce(to_price(children, decimal_digits), '')
, coalesce(to_price(tourist_tax, decimal_digits), '')
, coalesce(to_price(nights + adults + teenagers + children + tourist_tax + sum(option_cost)::integer, decimal_digits), '')
, array_agg((campsite_type_option_id, to_price(option_cost, decimal_digits)))
from per_person, per_option
group by num_nights
, nights
, adults
, teenagers
, children
, tourist_tax
, decimal_digits
`, pgx.QueryResultFormats{pgx.BinaryFormatCode}, arrivalDate, departureDate.AddDate(0, 0, -1), campsiteType, numAdults, numTeenagers, numChildren, optionIDs, optionUnits)
var numNights int
var nights string
var adults string
var teenagers string
var children string
var touristTax string
var total string
var optionPrices database.RecordArray
if err = row.Scan(&numNights, &nights, &adults, &teenagers, &children, &touristTax, &total, &optionPrices); err != nil {
if database.ErrorIsNotFound(err) {
return cart, nil
}
return nil, err
}
maybeAddLine := func(units int, subtotal string, concept string) {
if units > 0 && subtotal != "" {
cart.Lines = append(cart.Lines, &cartLine{
Concept: concept,
Units: units,
Subtotal: subtotal,
})
}
}
maybeAddLine(numNights, nights, locale.PgettextNoop("Night", "cart"))
maybeAddLine(numAdults, adults, locale.PgettextNoop("Adult", "cart"))
maybeAddLine(numTeenagers, teenagers, locale.PgettextNoop("Teenager", "cart"))
maybeAddLine(numChildren, children, locale.PgettextNoop("Child", "cart"))
for _, el := range optionPrices.Elements {
optionID := el.Fields[0].Get()
if optionID == nil {
continue
}
subtotal := el.Fields[1].Get()
if subtotal == nil {
continue
}
option := optionMap[int(optionID.(int32))]
if option == nil {
continue
}
units, _ := strconv.Atoi(option.Input.Val)
maybeAddLine(units, subtotal.(string), option.Label)
}
maybeAddLine(numAdults, touristTax, locale.PgettextNoop("Tourist tax", "cart"))
if total != "" {
cart.Total = total
}
return cart, nil
}

View File

@ -48,6 +48,8 @@ func (h *PublicHandler) Handler(user *auth.User, company *auth.Company, conn *da
default:
httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPost)
}
case "cart":
handleBookingCart(w, r, user, company, conn)
case "success":
handleSuccessfulPayment(w, r, user, company, conn)
case "failure":
@ -74,12 +76,17 @@ func makeReservation(w http.ResponseWriter, r *http.Request, user *auth.User, co
page.MustRender(w, r, user, company, conn)
return
}
cart, err := computeCart(r.Context(), conn, f)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
schema := httplib.Protocol(r)
authority := httplib.Host(r)
request := &redsys.Request{
TransactionType: redsys.TransactionTypeCharge,
Amount: "12.34",
Amount: cart.Total,
OrderNumber: randomOrderNumber(),
Product: "Test Booking",
SuccessURL: fmt.Sprintf("%s://%s/%s/booking/success", schema, authority, user.Locale.Language),
@ -106,6 +113,7 @@ func randomOrderNumber() string {
type publicPage struct {
*template.PublicPage
Form *bookingForm
Cart *bookingCart
}
func newPublicPage(ctx context.Context, company *auth.Company, conn *database.Conn, l *locale.Locale) *publicPage {
@ -121,7 +129,12 @@ func newPublicPageWithForm(form *bookingForm) *publicPage {
func (p *publicPage) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
p.Setup(r, user, company, conn)
template.MustRenderPublic(w, r, user, company, "booking.gohtml", p)
var err error
p.Cart, err = computeCart(r.Context(), conn, p.Form)
if err != nil {
panic(err)
}
template.MustRenderPublicFiles(w, r, user, company, p, "booking/form.gohtml", "booking/cart.gohtml")
}
type bookingForm struct {
@ -136,12 +149,17 @@ type bookingForm struct {
CampsiteTypeOptions map[string][]*campsiteTypeOption
ArrivalDate *form.Input
DepartureDate *form.Input
NumberAdults *form.Input
NumberTeenagers *form.Input
NumberChildren *form.Input
NumberDogs *form.Input
ZonePreferences map[string]*form.Input
ACSICard *form.Checkbox
Agreement *form.Checkbox
}
type campsiteTypeOption struct {
ID int
Label string
Min int
Max int
@ -186,6 +204,8 @@ func newBookingForm(ctx context.Context, company *auth.Company, conn *database.C
}
return &bookingForm{
CampsiteTypeOptions: mustGetCampsiteTypeOptions(ctx, conn, company, l),
ZonePreferences: zonePreferences,
FullName: &form.Input{
Name: "full_name",
},
@ -212,14 +232,28 @@ func newBookingForm(ctx context.Context, company *auth.Company, conn *database.C
Name: "campsite_type",
Options: typeSelectOptions,
},
CampsiteTypeOptions: mustGetCampsiteTypeOptions(ctx, conn, company, l),
ArrivalDate: &form.Input{
Name: "arrival_date",
},
DepartureDate: &form.Input{
Name: "departure_date",
},
ZonePreferences: zonePreferences,
NumberAdults: &form.Input{
Name: "number_adults",
Val: "1",
},
NumberTeenagers: &form.Input{
Name: "number_teenagers",
Val: "0",
},
NumberChildren: &form.Input{
Name: "number_children",
Val: "0",
},
NumberDogs: &form.Input{
Name: "number_dogs",
Val: "0",
},
ACSICard: &form.Checkbox{
Name: "acsi_card",
},
@ -231,7 +265,8 @@ func newBookingForm(ctx context.Context, company *auth.Company, conn *database.C
func mustGetCampsiteTypeOptions(ctx context.Context, conn *database.Conn, company *auth.Company, l *locale.Locale) map[string][]*campsiteTypeOption {
rows, err := conn.Query(ctx, `
select 'campsite_type_option_' || option.campsite_type_option_id
select option.campsite_type_option_id
, 'campsite_type_option_' || option.campsite_type_option_id
, slug
, coalesce(i18n.name, option.name) as l10_name
, lower(range)::text
@ -254,7 +289,7 @@ func mustGetCampsiteTypeOptions(ctx context.Context, conn *database.Conn, compan
option := &campsiteTypeOption{
Input: &form.Input{},
}
if err = rows.Scan(&option.Input.Name, &slug, &option.Label, &option.Input.Val, &option.Min, &option.Max); err != nil {
if err = rows.Scan(&option.ID, &option.Input.Name, &slug, &option.Label, &option.Input.Val, &option.Min, &option.Max); err != nil {
panic(err)
}
options[slug] = append(options[slug], option)
@ -279,6 +314,10 @@ func (f *bookingForm) Parse(r *http.Request) error {
f.CampsiteType.FillValue(r)
f.ArrivalDate.FillValue(r)
f.DepartureDate.FillValue(r)
f.NumberAdults.FillValue(r)
f.NumberTeenagers.FillValue(r)
f.NumberChildren.FillValue(r)
f.NumberDogs.FillValue(r)
f.ACSICard.FillValue(r)
f.Agreement.FillValue(r)
for _, preferences := range f.ZonePreferences {
@ -329,6 +368,27 @@ func (f *bookingForm) Valid(ctx context.Context, conn *database.Conn, l *locale.
v.CheckDateAfter(f.DepartureDate, f.ArrivalDate, l.GettextNoop("The departure date must be after the arrival date."))
}
}
if v.CheckRequired(f.NumberAdults, l.GettextNoop("Number of adults can not be empty")) {
if v.CheckValidInteger(f.NumberAdults, l.GettextNoop("Number of adults must be an integer.")) {
v.CheckMinInteger(f.NumberAdults, 1, l.GettextNoop("There must be at least one adult."))
}
}
if v.CheckRequired(f.NumberTeenagers, l.GettextNoop("Number of teenagers can not be empty")) {
if v.CheckValidInteger(f.NumberTeenagers, l.GettextNoop("Number of teenagers must be an integer.")) {
v.CheckMinInteger(f.NumberTeenagers, 0, l.GettextNoop("Number of teenagers can not be negative."))
}
}
if v.CheckRequired(f.NumberTeenagers, l.GettextNoop("Number of children can not be empty")) {
if v.CheckValidInteger(f.NumberTeenagers, l.GettextNoop("Number of children must be an integer.")) {
v.CheckMinInteger(f.NumberTeenagers, 0, l.GettextNoop("Number of children can not be negative."))
}
}
if v.CheckRequired(f.NumberTeenagers, l.GettextNoop("Number of dogs can not be empty")) {
if v.CheckValidInteger(f.NumberTeenagers, l.GettextNoop("Number of dogs must be an integer.")) {
v.CheckMinInteger(f.NumberTeenagers, 0, l.GettextNoop("Number of dogs can not be negative."))
}
}
v.Check(f.Agreement, f.Agreement.Checked, l.GettextNoop("It is mandatory to agree to the reservation conditions."))
for _, options := range f.CampsiteTypeOptions {
for _, option := range options {

View File

@ -193,7 +193,7 @@ func translateTypes(ctx context.Context, tx *database.Tx, company *auth.Company,
func setTypePrices(ctx context.Context, tx *database.Tx, slug string, prices map[int]*typePriceForm) error {
for seasonID, p := range prices {
if err := tx.SetCampsiteTypeCost(ctx, slug, seasonID, p.PricePerNight.Val); err != nil {
if err := tx.SetCampsiteTypeCost(ctx, slug, seasonID, p.PricePerNight.Val, p.PricePerAdult.Val, p.PricePerTeenager.Val, p.PricePerChild.Val); err != nil {
return err
}
}
@ -292,8 +292,11 @@ type typeForm struct {
}
type typePriceForm struct {
SeasonName string
PricePerNight *form.Input
SeasonName string
PricePerNight *form.Input
PricePerAdult *form.Input
PricePerTeenager *form.Input
PricePerChild *form.Input
}
func newTypeForm(ctx context.Context, company *auth.Company, conn *database.Conn) (*typeForm, error) {
@ -359,6 +362,18 @@ func newTypeForm(ctx context.Context, company *auth.Company, conn *database.Conn
Name: fmt.Sprintf("season.%d.price_per_night", id),
Val: "0",
},
PricePerAdult: &form.Input{
Name: fmt.Sprintf("season.%d.price_per_adult", id),
Val: "0",
},
PricePerTeenager: &form.Input{
Name: fmt.Sprintf("season.%d.price_per_teenager", id),
Val: "0",
},
PricePerChild: &form.Input{
Name: fmt.Sprintf("season.%d.price_per_child", id),
Val: "0",
},
}
}
@ -475,6 +490,9 @@ func (f *typeForm) FillFromDatabase(ctx context.Context, conn *database.Conn, sl
rows, err := conn.Query(ctx, `
select season_id
, to_price(cost_per_night)
, to_price(cost_per_adult)
, to_price(cost_per_teenager)
, to_price(cost_per_child)
from campsite_type
join campsite_type_cost using (campsite_type_id)
where slug = $1
@ -487,11 +505,17 @@ func (f *typeForm) FillFromDatabase(ctx context.Context, conn *database.Conn, sl
for rows.Next() {
var seasonID int
var pricePerNight string
if err = rows.Scan(&seasonID, &pricePerNight); err != nil {
var pricePerAdult string
var pricePerTeenager string
var pricePerChild string
if err = rows.Scan(&seasonID, &pricePerNight, &pricePerAdult, &pricePerTeenager, &pricePerChild); err != nil {
return err
}
if p, ok := f.Prices[seasonID]; ok {
p.PricePerNight.Val = pricePerNight
p.PricePerAdult.Val = pricePerAdult
p.PricePerTeenager.Val = pricePerTeenager
p.PricePerChild.Val = pricePerChild
}
}
return rows.Err()
@ -519,6 +543,9 @@ func (f *typeForm) Parse(r *http.Request) error {
f.Media.FillValue(r)
for _, p := range f.Prices {
p.PricePerNight.FillValue(r)
p.PricePerAdult.FillValue(r)
p.PricePerTeenager.FillValue(r)
p.PricePerChild.FillValue(r)
}
return nil
}
@ -556,6 +583,21 @@ func (f *typeForm) Valid(ctx context.Context, conn *database.Conn, l *locale.Loc
v.CheckMinDecimal(p.PricePerNight, 0.0, l.GettextNoop("Price per night must be zero or greater."))
}
}
if v.CheckRequired(p.PricePerAdult, l.GettextNoop("Price per adult can not be empty.")) {
if v.CheckValidDecimal(p.PricePerAdult, l.GettextNoop("Price per adult must be a decimal number.")) {
v.CheckMinDecimal(p.PricePerAdult, 0.0, l.GettextNoop("Price per adult must be zero or greater."))
}
}
if v.CheckRequired(p.PricePerTeenager, l.GettextNoop("Price per teenager can not be empty.")) {
if v.CheckValidDecimal(p.PricePerTeenager, l.GettextNoop("Price per teenager must be a decimal number.")) {
v.CheckMinDecimal(p.PricePerTeenager, 0.0, l.GettextNoop("Price per teenager must be zero or greater."))
}
}
if v.CheckRequired(p.PricePerChild, l.GettextNoop("Price per child can not be empty.")) {
if v.CheckValidDecimal(p.PricePerChild, l.GettextNoop("Price per child must be a decimal number.")) {
v.CheckMinDecimal(p.PricePerChild, 0.0, l.GettextNoop("Price per child must be zero or greater."))
}
}
}
return v.AllOK, nil
}

View File

@ -83,10 +83,9 @@ type publicPage struct {
}
type typePrice struct {
SeasonName string
SeasonColor string
PricePerNight string
Options []*optionPrice
SeasonName string
SeasonColor string
Options []*optionPrice
}
type optionPrice struct {
@ -100,10 +99,6 @@ type typeFeature struct {
}
func newPublicPage(ctx context.Context, company *auth.Company, conn *database.Conn, loc *locale.Locale, slug string) (*publicPage, error) {
prices, err := collectPrices(ctx, conn, loc.Language, slug)
if err != nil {
return nil, err
}
features, err := collectFeatures(ctx, conn, loc.Language, slug)
if err != nil {
return nil, err
@ -118,7 +113,6 @@ func newPublicPage(ctx context.Context, company *auth.Company, conn *database.Co
Slug: slug,
Carousel: mustCollectSlides(ctx, conn, loc, slug),
Calendar: calendar,
Prices: prices,
Features: features,
}
row := conn.QueryRow(ctx, `
@ -155,18 +149,25 @@ func newPublicPage(ctx context.Context, company *auth.Company, conn *database.Co
); err != nil {
return nil, err
}
if err := conn.QueryRow(ctx, "select to_price(tourist_tax, $1) from company where company_id = $2", company.DecimalDigits, company.ID).Scan(&page.TouristTax); err != nil {
if err = conn.QueryRow(ctx, "select to_price(tourist_tax, $1) from company where company_id = $2", company.DecimalDigits, company.ID).Scan(&page.TouristTax); err != nil {
return nil, err
}
page.Prices, err = collectPrices(ctx, conn, loc.Language, slug, page.Name)
if err != nil {
return nil, err
}
return page, nil
}
func collectPrices(ctx context.Context, conn *database.Conn, language language.Tag, slug string) ([]*typePrice, error) {
func collectPrices(ctx context.Context, conn *database.Conn, language language.Tag, slug string, campsiteName string) ([]*typePrice, error) {
rows, err := conn.Query(ctx, `
select coalesce(i18n.name, season.name) as l10n_name
, to_color(season.color)::text
, to_price(coalesce(cost.cost_per_night, 0))
, case when cost.cost_per_night > 0 then to_price(coalesce(cost.cost_per_night, 0)) else '' end
, case when cost.cost_per_adult > 0 then to_price(coalesce(cost.cost_per_adult, 0)) else '' end
, case when cost.cost_per_teenager > 0 then to_price(coalesce(cost.cost_per_teenager, 0)) else '' end
, case when cost.cost_per_child > 0 then to_price(coalesce(cost.cost_per_child, 0)) else '' end
, array_agg((coalesce(option_i18n.name, option.name), to_price(coalesce(option_cost.cost_per_night, 0))) order by option.position) filter (where option.campsite_type_option_id is not null)
from season
left join season_i18n as i18n on season.season_id = i18n.season_id and i18n.lang_tag = $1
@ -185,6 +186,9 @@ func collectPrices(ctx context.Context, conn *database.Conn, language language.T
, season.name
, season.color
, cost.cost_per_night
, cost.cost_per_adult
, cost.cost_per_teenager
, cost.cost_per_child
, season.position
order by season.position, l10n_name
`, pgx.QueryResultFormats{pgx.BinaryFormatCode}, language, slug)
@ -195,15 +199,49 @@ func collectPrices(ctx context.Context, conn *database.Conn, language language.T
var prices []*typePrice
for rows.Next() {
price := &typePrice{}
var pricePerNight string
var pricePerAdult string
var pricePerTeenager string
var pricePerChild string
var options database.RecordArray
if err = rows.Scan(
&price.SeasonName,
&price.SeasonColor,
&price.PricePerNight,
&pricePerNight,
&pricePerAdult,
&pricePerTeenager,
&pricePerChild,
&options,
); err != nil {
return nil, err
}
if pricePerNight != "" {
option := &optionPrice{
PricePerNight: pricePerNight,
}
if pricePerAdult != "" || pricePerTeenager != "" || pricePerChild != "" || len(options.Elements) > 0 {
option.OptionName = campsiteName
}
price.Options = append(price.Options, option)
}
if pricePerAdult != "" {
price.Options = append(price.Options, &optionPrice{
OptionName: locale.PgettextNoop("Adults", "header"),
PricePerNight: pricePerAdult,
})
}
if pricePerTeenager != pricePerAdult && pricePerTeenager != "" {
price.Options = append(price.Options, &optionPrice{
OptionName: locale.PgettextNoop("Teenagers (aged 11 to 16)", "header"),
PricePerNight: pricePerTeenager,
})
}
if pricePerChild != "" {
price.Options = append(price.Options, &optionPrice{
OptionName: locale.PgettextNoop("Children (aged 2 to 10)", "header"),
PricePerNight: pricePerChild,
})
}
for _, el := range options.Elements {
price.Options = append(price.Options, &optionPrice{
OptionName: el.Fields[0].Get().(string),

View File

@ -147,8 +147,8 @@ func (tx *Tx) TranslateCampsiteType(ctx context.Context, slug string, langTag la
_, err := tx.Exec(ctx, "select translate_campsite_type($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", slug, langTag, name, spiel, info, facilities, description, additionalInfo, checkIn, checkOut)
return err
}
func (tx *Tx) SetCampsiteTypeCost(ctx context.Context, slug string, seasonID int, costPerNight string) error {
_, err := tx.Exec(ctx, "select set_campsite_type_cost($1, $2, $3)", slug, seasonID, costPerNight)
func (tx *Tx) SetCampsiteTypeCost(ctx context.Context, slug string, seasonID int, perNight string, perAdult string, perTeenager string, perChild string) error {
_, err := tx.Exec(ctx, "select set_campsite_type_cost($1, $2, $3, $4, $5, $6)", slug, seasonID, perNight, perAdult, perTeenager, perChild)
return err
}

556
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-02-02 21:57+0100\n"
"POT-Creation-Date: 2024-02-04 06:23+0100\n"
"PO-Revision-Date: 2023-07-22 23:45+0200\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Catalan <ca@dodds.net>\n"
@ -94,7 +94,7 @@ msgid "The campsite offers many different services."
msgstr "El càmping disposa de diversos serveis."
#: web/templates/public/amenity.gohtml:39
#: web/templates/public/campsite/type.gohtml:114
#: web/templates/public/campsite/type.gohtml:113
#: web/templates/public/campsite/page.gohtml:39
msgctxt "title"
msgid "Features"
@ -164,7 +164,7 @@ msgid "Check-out Date"
msgstr "Data de sortida"
#: web/templates/public/campsite/type.gohtml:56
#: web/templates/public/booking.gohtml:171
#: web/templates/public/booking/cart.gohtml:25
msgctxt "action"
msgid "Book"
msgstr "Reserva"
@ -182,61 +182,60 @@ msgctxt "title"
msgid "Prices"
msgstr "Preus"
#: web/templates/public/campsite/type.gohtml:87
#: web/templates/public/campsite/type.gohtml:93
msgid "%s: %s €/night"
msgstr "%s: %s €/nit"
#: web/templates/public/campsite/type.gohtml:88
msgid "%s: %s/night"
msgstr "%s: %s/nit"
#: web/templates/public/campsite/type.gohtml:89
msgid "%s €/night"
msgstr "%s €/nit"
#: web/templates/public/campsite/type.gohtml:90
msgid "%s/night"
msgstr "%s/nit"
#: web/templates/public/campsite/type.gohtml:96
#: web/templates/public/campsite/type.gohtml:95
msgid "*Minimum %d nights per stay"
msgstr "*Mínim %d nits per estada"
#: web/templates/public/campsite/type.gohtml:101
#: web/templates/public/campsite/type.gohtml:100
msgid "10 % VAT included."
msgstr "IVA del 10 % inclòs."
#: web/templates/public/campsite/type.gohtml:102
#: web/templates/public/campsite/type.gohtml:101
msgid "Tourist tax: %s/night per person aged 17 or older."
msgstr "Impost turístic: %s/nit per persona major de 16 anys."
#: web/templates/public/campsite/type.gohtml:104
#: web/templates/public/campsite/type.gohtml:103
msgid "Dogs: %s/night, tied, accompanied, and minimal barking."
msgstr "Gossos: %s/night, lligats, acompanyats i el mínim de lladrucs."
msgstr "Gossos: %s/nit, lligats, acompanyats i el mínim de lladrucs."
#: web/templates/public/campsite/type.gohtml:106
#: web/templates/public/campsite/type.gohtml:105
msgid "No dogs allowed."
msgstr "No es permeten gossos."
#: web/templates/public/campsite/type.gohtml:125
#: web/templates/public/campsite/type.gohtml:124
msgctxt "title"
msgid "Info"
msgstr "Informació"
#: web/templates/public/campsite/type.gohtml:129
#: web/templates/public/campsite/type.gohtml:128
msgctxt "title"
msgid "Facilities"
msgstr "Equipaments"
#: web/templates/public/campsite/type.gohtml:133
#: web/templates/public/campsite/type.gohtml:132
msgctxt "title"
msgid "Description"
msgstr "Descripció"
#: web/templates/public/campsite/type.gohtml:137
#: web/templates/public/campsite/type.gohtml:136
msgctxt "title"
msgid "Additional Information"
msgstr "Informació addicional"
#: web/templates/public/campsite/type.gohtml:140
#: web/templates/public/campsite/type.gohtml:139
msgctxt "time"
msgid "Check-in"
msgstr "Entrada"
#: web/templates/public/campsite/type.gohtml:144
#: web/templates/public/campsite/type.gohtml:143
msgctxt "time"
msgid "Check-out"
msgstr "Sortida"
@ -482,99 +481,6 @@ msgctxt "legend"
msgid "Faucet"
msgstr "Aixeta"
#: web/templates/public/booking.gohtml:7 web/templates/public/booking.gohtml:12
#: web/templates/public/layout.gohtml:70
msgctxt "title"
msgid "Booking"
msgstr "Reserva"
#: web/templates/public/booking.gohtml:16
msgctxt "title"
msgid "Customer Details"
msgstr "Detalls del client"
#: web/templates/public/booking.gohtml:19
msgctxt "input"
msgid "Full name"
msgstr "Nom i cognoms"
#: web/templates/public/booking.gohtml:28
msgctxt "input"
msgid "Address (optional)"
msgstr "Adreça (opcional)"
#: web/templates/public/booking.gohtml:37
msgctxt "input"
msgid "Postcode (optional)"
msgstr "Codi postal (opcional)"
#: web/templates/public/booking.gohtml:46
msgctxt "input"
msgid "Town or village (optional)"
msgstr "Població (opcional)"
#: web/templates/public/booking.gohtml:55
#: web/templates/admin/taxDetails.gohtml:101
msgctxt "input"
msgid "Country"
msgstr "País"
#: web/templates/public/booking.gohtml:58
msgid "Choose a country"
msgstr "Esculli un país"
#: web/templates/public/booking.gohtml:66 web/templates/admin/login.gohtml:27
#: web/templates/admin/profile.gohtml:38
#: web/templates/admin/taxDetails.gohtml:53
msgctxt "input"
msgid "Email"
msgstr "Correu-e"
#: web/templates/public/booking.gohtml:75
#: web/templates/admin/taxDetails.gohtml:45
msgctxt "input"
msgid "Phone"
msgstr "Telèfon"
#: web/templates/public/booking.gohtml:84
msgctxt "title"
msgid "Accommodation"
msgstr "Allotjaments"
#: web/templates/public/booking.gohtml:101
msgctxt "input"
msgid "Area preferences (optional)"
msgstr "Preferències dàrea (opcional)"
#: web/templates/public/booking.gohtml:108
msgid "Campground map"
msgstr "Mapa del càmping"
#: web/templates/public/booking.gohtml:126
msgctxt "title"
msgid "Booking Period"
msgstr "Període de reserva"
#: web/templates/public/booking.gohtml:129
msgctxt "input"
msgid "Arrival date"
msgstr "Data darribada"
#: web/templates/public/booking.gohtml:139
msgctxt "input"
msgid "Departure date"
msgstr "Data de sortida"
#: web/templates/public/booking.gohtml:151
msgctxt "input"
msgid "ACSI card? (optional)"
msgstr "Targeta ACSI? (opcional)"
#: web/templates/public/booking.gohtml:158
msgctxt "input"
msgid "I have read and I accept %[1]sthe reservation conditions%[2]s"
msgstr "He llegit i accepto %[1]sles condicions de reserves%[2]s"
#: web/templates/public/layout.gohtml:12 web/templates/public/layout.gohtml:48
msgid "Campsite Montagut"
msgstr "Càmping Montagut"
@ -608,6 +514,13 @@ msgctxt "title"
msgid "Campsites"
msgstr "Allotjaments"
#: web/templates/public/layout.gohtml:70
#: web/templates/public/booking/form.gohtml:7
#: web/templates/public/booking/form.gohtml:16
msgctxt "title"
msgid "Booking"
msgstr "Reserva"
#: web/templates/public/layout.gohtml:91
msgctxt "title"
msgid "Sections"
@ -622,6 +535,123 @@ msgstr "Obertura"
msgid "<abbr title=\"Catalonia Tourism Registry\">RTC</abbr> <abbr title=\"Number\">#</abbr>%s"
msgstr "<abbr title=\"Número\">Núm.</abbr> <abbr title=\"Registre de Turisme de Catalunya\">RTC</abbr> %s"
#: web/templates/public/booking/form.gohtml:29
msgctxt "title"
msgid "Accommodation"
msgstr "Allotjaments"
#: web/templates/public/booking/form.gohtml:43
msgctxt "title"
msgid "Booking Period"
msgstr "Període de reserva"
#: web/templates/public/booking/form.gohtml:46
msgctxt "input"
msgid "Arrival date"
msgstr "Data darribada"
#: web/templates/public/booking/form.gohtml:57
msgctxt "input"
msgid "Departure date"
msgstr "Data de sortida"
#: web/templates/public/booking/form.gohtml:72
msgctxt "title"
msgid "Guests"
msgstr "Hostes"
#: web/templates/public/booking/form.gohtml:76
msgctxt "input"
msgid "Adults aged 17 or older"
msgstr "Adults de 17 anys o més"
#: web/templates/public/booking/form.gohtml:86
msgctxt "input"
msgid "Teenagers from 11 to 16 years old"
msgstr "Adolescents dentre 11 i 16 anys"
#: web/templates/public/booking/form.gohtml:96
msgctxt "input"
msgid "Children from 2 to 10 years old"
msgstr "Nens dentre 2 i 10 anys)"
#: web/templates/public/booking/form.gohtml:106
msgctxt "input"
msgid "Dogs"
msgstr "Gossos"
#: web/templates/public/booking/form.gohtml:127
msgctxt "input"
msgid "Area preferences (optional)"
msgstr "Preferències dàrea (opcional)"
#: web/templates/public/booking/form.gohtml:129
msgid "Campground map"
msgstr "Mapa del càmping"
#: web/templates/public/booking/form.gohtml:156
msgctxt "title"
msgid "Customer Details"
msgstr "Detalls del client"
#: web/templates/public/booking/form.gohtml:159
msgctxt "input"
msgid "Full name"
msgstr "Nom i cognoms"
#: web/templates/public/booking/form.gohtml:168
msgctxt "input"
msgid "Address (optional)"
msgstr "Adreça (opcional)"
#: web/templates/public/booking/form.gohtml:177
msgctxt "input"
msgid "Postcode (optional)"
msgstr "Codi postal (opcional)"
#: web/templates/public/booking/form.gohtml:186
msgctxt "input"
msgid "Town or village (optional)"
msgstr "Població (opcional)"
#: web/templates/public/booking/form.gohtml:195
#: web/templates/admin/taxDetails.gohtml:101
msgctxt "input"
msgid "Country"
msgstr "País"
#: web/templates/public/booking/form.gohtml:198
msgid "Choose a country"
msgstr "Esculli un país"
#: web/templates/public/booking/form.gohtml:206
#: web/templates/admin/login.gohtml:27 web/templates/admin/profile.gohtml:38
#: web/templates/admin/taxDetails.gohtml:53
msgctxt "input"
msgid "Email"
msgstr "Correu-e"
#: web/templates/public/booking/form.gohtml:215
#: web/templates/admin/taxDetails.gohtml:45
msgctxt "input"
msgid "Phone"
msgstr "Telèfon"
#: web/templates/public/booking/form.gohtml:226
msgctxt "input"
msgid "ACSI card? (optional)"
msgstr "Targeta ACSI? (opcional)"
#: web/templates/public/booking/form.gohtml:233
msgctxt "input"
msgid "I have read and I accept %[1]sthe reservation conditions%[2]s"
msgstr "He llegit i accepto %[1]sles condicions de reserves%[2]s"
#: web/templates/public/booking/cart.gohtml:14
msgctxt "cart"
msgid "Total"
msgstr "Total"
#: web/templates/admin/legal/form.gohtml:8
#: web/templates/admin/legal/form.gohtml:29
msgctxt "title"
@ -674,7 +704,7 @@ msgstr "Contingut"
#: web/templates/admin/campsite/form.gohtml:92
#: web/templates/admin/campsite/type/feature/form.gohtml:74
#: web/templates/admin/campsite/type/carousel/form.gohtml:59
#: web/templates/admin/campsite/type/form.gohtml:228
#: web/templates/admin/campsite/type/form.gohtml:277
#: web/templates/admin/campsite/type/option/form.gohtml:90
#: web/templates/admin/season/form.gohtml:73
#: web/templates/admin/services/form.gohtml:81
@ -696,7 +726,7 @@ msgstr "Actualitza"
#: web/templates/admin/campsite/form.gohtml:94
#: web/templates/admin/campsite/type/feature/form.gohtml:76
#: web/templates/admin/campsite/type/carousel/form.gohtml:61
#: web/templates/admin/campsite/type/form.gohtml:230
#: web/templates/admin/campsite/type/form.gohtml:279
#: web/templates/admin/campsite/type/option/form.gohtml:92
#: web/templates/admin/season/form.gohtml:75
#: web/templates/admin/services/form.gohtml:83
@ -1139,35 +1169,75 @@ msgctxt "input"
msgid "Dogs allowed"
msgstr "Es permeten gossos"
#: web/templates/admin/campsite/type/form.gohtml:147
msgctxt "header"
msgid "Season"
msgstr "Temporada"
#: web/templates/admin/campsite/type/form.gohtml:148
msgctxt "header"
msgid "Price per night"
msgstr "Preu per nit"
#: web/templates/admin/campsite/type/form.gohtml:149
msgctxt "header"
msgid "Price per adult (> 16)"
msgstr "Preu per adult (> 16)"
#: web/templates/admin/campsite/type/form.gohtml:150
msgctxt "header"
msgid "Price per teenager (1116)"
msgstr "Preu per adolescent (1116)"
#: web/templates/admin/campsite/type/form.gohtml:151
msgctxt "header"
msgid "Price per child (210)"
msgstr "Preu per nen (210)"
#: web/templates/admin/campsite/type/form.gohtml:161
#: web/templates/admin/campsite/type/option/form.gohtml:76
msgctxt "input"
msgid "Price per night"
msgstr "Preu per nit"
#: web/templates/admin/campsite/type/form.gohtml:161
#: web/templates/admin/campsite/type/form.gohtml:172
msgctxt "input"
msgid "Price per adult (> 16)"
msgstr "Per per adult (> 16)"
#: web/templates/admin/campsite/type/form.gohtml:183
msgctxt "input"
msgid "Price per teenager (1116)"
msgstr "Preu per adolescent (1116)"
#: web/templates/admin/campsite/type/form.gohtml:194
msgctxt "input"
msgid "Price per child (210)"
msgstr "Preu per nene (210)"
#: web/templates/admin/campsite/type/form.gohtml:210
msgctxt "input"
msgid "Spiel"
msgstr "Introducció"
#: web/templates/admin/campsite/type/form.gohtml:174
#: web/templates/admin/campsite/type/form.gohtml:223
msgctxt "input"
msgid "Info"
msgstr "Informació"
#: web/templates/admin/campsite/type/form.gohtml:187
#: web/templates/admin/campsite/type/form.gohtml:236
msgctxt "input"
msgid "Facilities"
msgstr "Equipaments"
#: web/templates/admin/campsite/type/form.gohtml:200
#: web/templates/admin/campsite/type/form.gohtml:249
#: web/templates/admin/services/form.gohtml:66
#: web/templates/admin/surroundings/form.gohtml:54
msgctxt "input"
msgid "Description"
msgstr "Descripció"
#: web/templates/admin/campsite/type/form.gohtml:213
#: web/templates/admin/campsite/type/form.gohtml:262
msgctxt "input"
msgid "Additional Information"
msgstr "Informació addicional"
@ -1864,7 +1934,7 @@ msgid "No booking found."
msgstr "No sha trobat cap reserva."
#: pkg/legal/admin.go:258 pkg/app/user.go:249 pkg/campsite/types/option.go:357
#: pkg/campsite/types/feature.go:272 pkg/campsite/types/admin.go:528
#: pkg/campsite/types/feature.go:272 pkg/campsite/types/admin.go:555
#: pkg/campsite/feature.go:269 pkg/season/admin.go:412
#: pkg/services/admin.go:316 pkg/surroundings/admin.go:340
#: pkg/amenity/feature.go:269 pkg/amenity/admin.go:283
@ -1872,7 +1942,7 @@ msgid "Name can not be empty."
msgstr "No podeu deixar el nom en blanc."
#: pkg/legal/admin.go:259 pkg/campsite/types/option.go:358
#: pkg/campsite/types/feature.go:273 pkg/campsite/types/admin.go:529
#: pkg/campsite/types/feature.go:273 pkg/campsite/types/admin.go:556
#: pkg/campsite/feature.go:270 pkg/amenity/feature.go:270
msgid "Name must have at least one letter."
msgstr "El nom ha de tenir com a mínim una lletra."
@ -1902,12 +1972,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:217
#: pkg/booking/public.go:312
#: pkg/booking/public.go:351
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:218
#: pkg/booking/public.go:313
#: pkg/booking/public.go:352
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."
@ -1964,15 +2034,15 @@ msgstr "El valor del màxim ha de ser un número enter."
msgid "Maximum must be equal or greater than minimum."
msgstr "El valor del màxim ha de ser igual o superir al del mínim."
#: pkg/campsite/types/option.go:374 pkg/campsite/types/admin.go:554
#: pkg/campsite/types/option.go:374 pkg/campsite/types/admin.go:581
msgid "Price per night can not be empty."
msgstr "No podeu deixar el preu per nit en blanc."
#: pkg/campsite/types/option.go:375 pkg/campsite/types/admin.go:555
#: pkg/campsite/types/option.go:375 pkg/campsite/types/admin.go:582
msgid "Price per night must be a decimal number."
msgstr "El preu per nit ha de ser un número decimal."
#: pkg/campsite/types/option.go:376 pkg/campsite/types/admin.go:556
#: pkg/campsite/types/option.go:376 pkg/campsite/types/admin.go:583
msgid "Price per night must be zero or greater."
msgstr "El preu per nit ha de ser com a mínim zero."
@ -1981,69 +2051,120 @@ msgstr "El preu per nit ha de ser com a mínim zero."
msgid "Selected icon is not valid."
msgstr "La icona escollida no és vàlida."
#: pkg/campsite/types/admin.go:310
#: pkg/campsite/types/admin.go:313
msgctxt "input"
msgid "Cover image"
msgstr "Imatge de portada"
#: pkg/campsite/types/admin.go:311
#: pkg/campsite/types/admin.go:314
msgctxt "action"
msgid "Set campsite type cover"
msgstr "Estableix la portada del tipus dallotjament"
#: pkg/campsite/types/admin.go:531
#: pkg/campsite/types/admin.go:558
msgid "Check-in can not be empty."
msgstr "No podeu deixar lentrada en blanc."
#: pkg/campsite/types/admin.go:532
#: pkg/campsite/types/admin.go:559
msgid "Check-out can not be empty."
msgstr "No podeu deixar la sortida en blanc."
#: pkg/campsite/types/admin.go:533
#: pkg/campsite/types/admin.go:560
msgid "Cover image can not be empty."
msgstr "No podeu deixar la imatge de portada en blanc."
#: pkg/campsite/types/admin.go:534
#: pkg/campsite/types/admin.go:561
msgid "Cover image must be an image media type."
msgstr "La imatge de portada ha de ser un mèdia de tipus imatge."
#: pkg/campsite/types/admin.go:538
#: pkg/campsite/types/admin.go:565
msgid "Maximum number of campers can not be empty."
msgstr "No podeu deixar el número màxim de persones en blanc."
#: pkg/campsite/types/admin.go:539
#: pkg/campsite/types/admin.go:566
msgid "Maximum number of campers must be an integer number."
msgstr "El número màxim de persones ha de ser enter."
#: pkg/campsite/types/admin.go:540
#: pkg/campsite/types/admin.go:567
msgid "Maximum number of campers must be one or greater."
msgstr "El número màxim de persones no pot ser zero."
#: pkg/campsite/types/admin.go:543
#: pkg/campsite/types/admin.go:570
msgid "Minimum number of nights can not be empty."
msgstr "No podeu deixar el número mínim de nits en blanc."
#: pkg/campsite/types/admin.go:544
#: pkg/campsite/types/admin.go:571
msgid "Minimum number of nights must be an integer."
msgstr "El número mínim de nits ha de ser enter."
#: pkg/campsite/types/admin.go:545
#: pkg/campsite/types/admin.go:572
msgid "Minimum number of nights must be one or greater."
msgstr "El número mínim de nits no pot ser zero."
#: pkg/campsite/types/admin.go:548
#: pkg/campsite/types/admin.go:575
msgid "Maximum number of nights can not be empty."
msgstr "No podeu deixar el número màxim de nits en blanc."
#: pkg/campsite/types/admin.go:549
#: pkg/campsite/types/admin.go:576
msgid "Maximum number of nights must be an integer."
msgstr "El número màxim de nits ha de ser enter."
#: pkg/campsite/types/admin.go:550
#: pkg/campsite/types/admin.go:577
msgid "Maximum number of nights must be equal or greater than the minimum."
msgstr "El valor del número màxim de nits ha de ser igual o superir al del mínim."
#: pkg/campsite/admin.go:275 pkg/booking/public.go:321
#: pkg/campsite/types/admin.go:586
msgid "Price per adult can not be empty."
msgstr "No podeu deixar el preu per adult en blanc."
#: pkg/campsite/types/admin.go:587
msgid "Price per adult must be a decimal number."
msgstr "El preu per adult ha de ser un número decimal."
#: pkg/campsite/types/admin.go:588
msgid "Price per adult must be zero or greater."
msgstr "El preu per adult ha de ser com a mínim zero."
#: pkg/campsite/types/admin.go:591
msgid "Price per teenager can not be empty."
msgstr "No podeu deixar el preu per adolescent en blanc."
#: pkg/campsite/types/admin.go:592
msgid "Price per teenager must be a decimal number."
msgstr "El preu per adolescent ha de ser un número decimal."
#: pkg/campsite/types/admin.go:593
msgid "Price per teenager must be zero or greater."
msgstr "El preu per adolescent ha de ser com a mínim zero."
#: pkg/campsite/types/admin.go:596
msgid "Price per child can not be empty."
msgstr "No podeu deixar el preu per nen en blanc."
#: pkg/campsite/types/admin.go:597
msgid "Price per child must be a decimal number."
msgstr "El preu per nen ha de ser un número decimal."
#: pkg/campsite/types/admin.go:598
msgid "Price per child must be zero or greater."
msgstr "El preu per nen ha de ser com a mínim zero."
#: pkg/campsite/types/public.go:229
msgctxt "header"
msgid "Adults"
msgstr "Adults"
#: pkg/campsite/types/public.go:235
msgctxt "header"
msgid "Teenagers (aged 11 to 16)"
msgstr "Adolescents (entre 11 i 16 anys)"
#: pkg/campsite/types/public.go:241
msgctxt "header"
msgid "Children (aged 2 to 10)"
msgstr "Mainada (entre 2 i 10 anys)"
#: pkg/campsite/admin.go:275 pkg/booking/public.go:360
msgid "Selected campsite type is not valid."
msgstr "El tipus dallotjament escollit no és vàlid."
@ -2199,7 +2320,7 @@ 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:200 pkg/booking/public.go:299
#: pkg/company/admin.go:200 pkg/booking/public.go:338
msgid "Selected country is not valid."
msgstr "El país escollit no és vàlid."
@ -2219,11 +2340,11 @@ 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:212 pkg/booking/public.go:315
#: pkg/company/admin.go:212 pkg/booking/public.go:354
msgid "Phone can not be empty."
msgstr "No podeu deixar el telèfon en blanc."
#: pkg/company/admin.go:213 pkg/booking/public.go:316
#: pkg/company/admin.go:213 pkg/booking/public.go:355
msgid "This phone number is not valid."
msgstr "Aquest número de telèfon no és vàlid."
@ -2243,7 +2364,7 @@ msgstr "No podeu deixar la província en blanc."
msgid "Postal code can not be empty."
msgstr "No podeu deixar el codi postal en blanc."
#: pkg/company/admin.go:227 pkg/booking/public.go:308
#: pkg/company/admin.go:227 pkg/booking/public.go:347
msgid "This postal code is not valid."
msgstr "Aquest codi postal no és vàlid."
@ -2283,6 +2404,31 @@ 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:171
msgctxt "cart"
msgid "Night"
msgstr "Nit"
#: pkg/booking/cart.go:172
msgctxt "cart"
msgid "Adult"
msgstr "Adult"
#: pkg/booking/cart.go:173
msgctxt "cart"
msgid "Teenager"
msgstr "Adolescent"
#: pkg/booking/cart.go:174
msgctxt "cart"
msgid "Child"
msgstr "Nen"
#: pkg/booking/cart.go:193
msgctxt "cart"
msgid "Tourist tax"
msgstr "Impost turístic"
#: pkg/booking/admin.go:149
msgctxt "filename"
msgid "bookings.ods"
@ -2340,54 +2486,102 @@ msgstr "La integració escollida no és vàlida."
msgid "The merchant key is not valid."
msgstr "Aquesta clau del comerç no és vàlid."
#: pkg/booking/public.go:303
#: pkg/booking/public.go:342
msgid "Full name can not be empty."
msgstr "No podeu deixar el nom i els cognoms en blanc."
#: pkg/booking/public.go:304
#: pkg/booking/public.go:343
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/public.go:322
#: pkg/booking/public.go:361
msgid "Arrival date can not be empty"
msgstr "No podeu deixar la data darribada en blanc."
#: pkg/booking/public.go:323
#: pkg/booking/public.go:362
msgid "Arrival date must be a valid date."
msgstr "La data darribada ha de ser una data vàlida."
#: pkg/booking/public.go:327
#: pkg/booking/public.go:366
msgid "Departure date can not be empty"
msgstr "No podeu deixar la data de sortida en blanc."
#: pkg/booking/public.go:328
#: pkg/booking/public.go:367
msgid "Departure date must be a valid date."
msgstr "La data de sortida ha de ser una data vàlida."
#: pkg/booking/public.go:329
#: pkg/booking/public.go:368
msgid "The departure date must be after the arrival date."
msgstr "La data de sortida ha de ser posterior a la darribada."
#: pkg/booking/public.go:332
#: pkg/booking/public.go:372
msgid "Number of adults can not be empty"
msgstr "No podeu deixar el número dadults en blanc."
#: pkg/booking/public.go:373
msgid "Number of adults must be an integer."
msgstr "El número dadults ha de ser enter."
#: pkg/booking/public.go:374
msgid "There must be at least one adult."
msgstr "Hi ha dhaver com a mínim un adult."
#: pkg/booking/public.go:377
msgid "Number of teenagers can not be empty"
msgstr "No podeu deixar el número dadolescents en blanc."
#: pkg/booking/public.go:378
msgid "Number of teenagers must be an integer."
msgstr "El número dadolescents ha de ser enter."
#: pkg/booking/public.go:379
msgid "Number of teenagers can not be negative."
msgstr "El número dadolescents no pot ser negatiu."
#: pkg/booking/public.go:382
msgid "Number of children can not be empty"
msgstr "No podeu deixar el número de nens en blanc."
#: pkg/booking/public.go:383
msgid "Number of children must be an integer."
msgstr "El número de nens ha de ser enter."
#: pkg/booking/public.go:384
msgid "Number of children can not be negative."
msgstr "El número de nens no pot ser negatiu."
#: pkg/booking/public.go:387
msgid "Number of dogs can not be empty"
msgstr "No podeu deixar el número de gossos en blanc."
#: pkg/booking/public.go:388
msgid "Number of dogs must be an integer."
msgstr "El número de gossos ha de ser enter."
#: pkg/booking/public.go:389
msgid "Number of dogs can not be negative."
msgstr "El número de gossos no pot ser negatiu."
#: pkg/booking/public.go:392
msgid "It is mandatory to agree to the reservation conditions."
msgstr "És obligatori acceptar les condicions de reserves."
#: pkg/booking/public.go:335
#: pkg/booking/public.go:395
#, c-format
msgid "%s can not be empty"
msgstr "No podeu deixar %s en blanc."
#: pkg/booking/public.go:336
#: pkg/booking/public.go:396
#, c-format
msgid "%s must be an integer."
msgstr "%s ha de ser un número enter."
#: pkg/booking/public.go:337
#: pkg/booking/public.go:397
#, 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:338
#: pkg/booking/public.go:398
#, c-format
msgid "%s must be at most %d."
msgstr "El valor de %s ha de ser com a màxim %d."
@ -2458,55 +2652,15 @@ msgstr "El valor de %s ha de ser com a màxim %d."
#~ msgid "Party Details"
#~ msgstr "Dades dels visitants"
#~ msgctxt "input"
#~ msgid "Adults"
#~ msgstr "Adults"
#~ msgctxt "input"
#~ msgid "Teenagers (from 11 to 16 years old)"
#~ msgstr "Adolescents (entre 11 i 16 anys)"
#~ msgctxt "input"
#~ msgid "Children (up to 10 years old)"
#~ msgstr "Nens (fins a 10 anys)"
#~ msgctxt "input"
#~ msgid "Dogs"
#~ msgstr "Gossos"
#~ msgid "Number of adults can not be empty"
#~ msgstr "No podeu deixar el número dadults en blanc."
#~ msgid "Number of adults must be an integer."
#~ msgstr "El número dadults ha de ser enter."
#~ msgid "Number of adults must be one or greater."
#~ msgstr "El número dadults no pot ser zero."
#~ msgid "Number of teenagers can not be empty"
#~ msgstr "No podeu deixar el número dadolescents en blanc."
#~ msgid "Number of teenagers must be an integer."
#~ msgstr "El número dadolescents ha de ser enter."
#~ msgid "Number of teenagers must be zero or greater."
#~ msgstr "El número dadolescents ha de ser com a mínim zero."
#~ msgid "Number of children can not be empty"
#~ msgstr "No podeu deixar el número de nens en blanc."
#~ msgid "Number of children must be an integer."
#~ msgstr "El número de nens ha de ser enter."
#~ msgid "Number of children must be zero or greater."
#~ msgstr "El número de nens ha de ser com a mínim zero."
#~ msgid "Number of dogs can not be empty"
#~ msgstr "No podeu deixar el número de gossos en blanc."
#~ msgid "Number of dogs must be an integer."
#~ msgstr "El número de gossos ha de ser enter."
#~ msgid "Number of dogs must be zero or greater."
#~ msgstr "El número de gossos ha de ser com a mínim zero."

556
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-02-02 21:57+0100\n"
"POT-Creation-Date: 2024-02-04 06:23+0100\n"
"PO-Revision-Date: 2023-07-22 23:46+0200\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Spanish <es@tp.org.es>\n"
@ -94,7 +94,7 @@ msgid "The campsite offers many different services."
msgstr "El camping dispone de varios servicios."
#: web/templates/public/amenity.gohtml:39
#: web/templates/public/campsite/type.gohtml:114
#: web/templates/public/campsite/type.gohtml:113
#: web/templates/public/campsite/page.gohtml:39
msgctxt "title"
msgid "Features"
@ -164,7 +164,7 @@ msgid "Check-out Date"
msgstr "Fecha de salida"
#: web/templates/public/campsite/type.gohtml:56
#: web/templates/public/booking.gohtml:171
#: web/templates/public/booking/cart.gohtml:25
msgctxt "action"
msgid "Book"
msgstr "Reservar"
@ -182,61 +182,60 @@ msgctxt "title"
msgid "Prices"
msgstr "Precios"
#: web/templates/public/campsite/type.gohtml:87
#: web/templates/public/campsite/type.gohtml:93
msgid "%s: %s €/night"
msgstr "%s: %s €/noche"
#: web/templates/public/campsite/type.gohtml:88
msgid "%s: %s/night"
msgstr "%s: %s/noche"
#: web/templates/public/campsite/type.gohtml:89
msgid "%s €/night"
msgstr "%s €/noche"
#: web/templates/public/campsite/type.gohtml:90
msgid "%s/night"
msgstr "%s/noche"
#: web/templates/public/campsite/type.gohtml:96
#: web/templates/public/campsite/type.gohtml:95
msgid "*Minimum %d nights per stay"
msgstr "*Mínimo %d noches por estancia"
#: web/templates/public/campsite/type.gohtml:101
#: web/templates/public/campsite/type.gohtml:100
msgid "10 % VAT included."
msgstr "IVA del 10 % incluido."
#: web/templates/public/campsite/type.gohtml:102
#: web/templates/public/campsite/type.gohtml:101
msgid "Tourist tax: %s/night per person aged 17 or older."
msgstr "Impuesto turístico: %s/noche por persona mayor de de 16 años."
msgstr "Impuesto turístico: %s/noche por persona mayor de 16 años."
#: web/templates/public/campsite/type.gohtml:104
#: web/templates/public/campsite/type.gohtml:103
msgid "Dogs: %s/night, tied, accompanied, and minimal barking."
msgstr "Perros: %s/noche, atados, acompañados y con mínimo de ladrido."
#: web/templates/public/campsite/type.gohtml:106
#: web/templates/public/campsite/type.gohtml:105
msgid "No dogs allowed."
msgstr "No se permiten perros"
#: web/templates/public/campsite/type.gohtml:125
#: web/templates/public/campsite/type.gohtml:124
msgctxt "title"
msgid "Info"
msgstr "Información"
#: web/templates/public/campsite/type.gohtml:129
#: web/templates/public/campsite/type.gohtml:128
msgctxt "title"
msgid "Facilities"
msgstr "Equipamiento"
#: web/templates/public/campsite/type.gohtml:133
#: web/templates/public/campsite/type.gohtml:132
msgctxt "title"
msgid "Description"
msgstr "Descripción"
#: web/templates/public/campsite/type.gohtml:137
#: web/templates/public/campsite/type.gohtml:136
msgctxt "title"
msgid "Additional Information"
msgstr "Información adicional"
#: web/templates/public/campsite/type.gohtml:140
#: web/templates/public/campsite/type.gohtml:139
msgctxt "time"
msgid "Check-in"
msgstr "Entrada"
#: web/templates/public/campsite/type.gohtml:144
#: web/templates/public/campsite/type.gohtml:143
msgctxt "time"
msgid "Check-out"
msgstr "Salida"
@ -482,99 +481,6 @@ msgctxt "legend"
msgid "Faucet"
msgstr "Grifo"
#: web/templates/public/booking.gohtml:7 web/templates/public/booking.gohtml:12
#: web/templates/public/layout.gohtml:70
msgctxt "title"
msgid "Booking"
msgstr "Reserva"
#: web/templates/public/booking.gohtml:16
msgctxt "title"
msgid "Customer Details"
msgstr "Detalles del cliente"
#: web/templates/public/booking.gohtml:19
msgctxt "input"
msgid "Full name"
msgstr "Nombre y apellidos"
#: web/templates/public/booking.gohtml:28
msgctxt "input"
msgid "Address (optional)"
msgstr "Dirección (opcional)"
#: web/templates/public/booking.gohtml:37
msgctxt "input"
msgid "Postcode (optional)"
msgstr "Código postal (opcional)"
#: web/templates/public/booking.gohtml:46
msgctxt "input"
msgid "Town or village (optional)"
msgstr "Población (opcional)"
#: web/templates/public/booking.gohtml:55
#: web/templates/admin/taxDetails.gohtml:101
msgctxt "input"
msgid "Country"
msgstr "País"
#: web/templates/public/booking.gohtml:58
msgid "Choose a country"
msgstr "Escoja un país"
#: web/templates/public/booking.gohtml:66 web/templates/admin/login.gohtml:27
#: web/templates/admin/profile.gohtml:38
#: web/templates/admin/taxDetails.gohtml:53
msgctxt "input"
msgid "Email"
msgstr "Correo-e"
#: web/templates/public/booking.gohtml:75
#: web/templates/admin/taxDetails.gohtml:45
msgctxt "input"
msgid "Phone"
msgstr "Teléfono"
#: web/templates/public/booking.gohtml:84
msgctxt "title"
msgid "Accommodation"
msgstr "Alojamientos"
#: web/templates/public/booking.gohtml:101
msgctxt "input"
msgid "Area preferences (optional)"
msgstr "Preferencias de área (opcional)"
#: web/templates/public/booking.gohtml:108
msgid "Campground map"
msgstr "Mapa del camping"
#: web/templates/public/booking.gohtml:126
msgctxt "title"
msgid "Booking Period"
msgstr "Periodo de reserva"
#: web/templates/public/booking.gohtml:129
msgctxt "input"
msgid "Arrival date"
msgstr "Fecha de llegada"
#: web/templates/public/booking.gohtml:139
msgctxt "input"
msgid "Departure date"
msgstr "Fecha de salida"
#: web/templates/public/booking.gohtml:151
msgctxt "input"
msgid "ACSI card? (optional)"
msgstr "¿Tarjeta ACSI? (opcional)"
#: web/templates/public/booking.gohtml:158
msgctxt "input"
msgid "I have read and I accept %[1]sthe reservation conditions%[2]s"
msgstr "He leído y acepto %[1]slas condiciones de reserva%[2]s"
#: web/templates/public/layout.gohtml:12 web/templates/public/layout.gohtml:48
msgid "Campsite Montagut"
msgstr "Camping Montagut"
@ -608,6 +514,13 @@ msgctxt "title"
msgid "Campsites"
msgstr "Alojamientos"
#: web/templates/public/layout.gohtml:70
#: web/templates/public/booking/form.gohtml:7
#: web/templates/public/booking/form.gohtml:16
msgctxt "title"
msgid "Booking"
msgstr "Reserva"
#: web/templates/public/layout.gohtml:91
msgctxt "title"
msgid "Sections"
@ -622,6 +535,123 @@ msgstr "Apertura"
msgid "<abbr title=\"Catalonia Tourism Registry\">RTC</abbr> <abbr title=\"Number\">#</abbr>%s"
msgstr "<abbr title=\"Número\">Nº</abbr> <abbr title=\"Registro de Turismo de Cataluña\">RTC</abbr> %s"
#: web/templates/public/booking/form.gohtml:29
msgctxt "title"
msgid "Accommodation"
msgstr "Alojamientos"
#: web/templates/public/booking/form.gohtml:43
msgctxt "title"
msgid "Booking Period"
msgstr "Periodo de reserva"
#: web/templates/public/booking/form.gohtml:46
msgctxt "input"
msgid "Arrival date"
msgstr "Fecha de llegada"
#: web/templates/public/booking/form.gohtml:57
msgctxt "input"
msgid "Departure date"
msgstr "Fecha de salida"
#: web/templates/public/booking/form.gohtml:72
msgctxt "title"
msgid "Guests"
msgstr "Huéspedes"
#: web/templates/public/booking/form.gohtml:76
msgctxt "input"
msgid "Adults aged 17 or older"
msgstr "Adultos de 17 años o más"
#: web/templates/public/booking/form.gohtml:86
msgctxt "input"
msgid "Teenagers from 11 to 16 years old"
msgstr "Adolescentes de 11 a 16 años"
#: web/templates/public/booking/form.gohtml:96
msgctxt "input"
msgid "Children from 2 to 10 years old"
msgstr "Niños de 2 a 10 años"
#: web/templates/public/booking/form.gohtml:106
msgctxt "input"
msgid "Dogs"
msgstr "Perros"
#: web/templates/public/booking/form.gohtml:127
msgctxt "input"
msgid "Area preferences (optional)"
msgstr "Preferencias de área (opcional)"
#: web/templates/public/booking/form.gohtml:129
msgid "Campground map"
msgstr "Mapa del camping"
#: web/templates/public/booking/form.gohtml:156
msgctxt "title"
msgid "Customer Details"
msgstr "Detalles del cliente"
#: web/templates/public/booking/form.gohtml:159
msgctxt "input"
msgid "Full name"
msgstr "Nombre y apellidos"
#: web/templates/public/booking/form.gohtml:168
msgctxt "input"
msgid "Address (optional)"
msgstr "Dirección (opcional)"
#: web/templates/public/booking/form.gohtml:177
msgctxt "input"
msgid "Postcode (optional)"
msgstr "Código postal (opcional)"
#: web/templates/public/booking/form.gohtml:186
msgctxt "input"
msgid "Town or village (optional)"
msgstr "Población (opcional)"
#: web/templates/public/booking/form.gohtml:195
#: web/templates/admin/taxDetails.gohtml:101
msgctxt "input"
msgid "Country"
msgstr "País"
#: web/templates/public/booking/form.gohtml:198
msgid "Choose a country"
msgstr "Escoja un país"
#: web/templates/public/booking/form.gohtml:206
#: web/templates/admin/login.gohtml:27 web/templates/admin/profile.gohtml:38
#: web/templates/admin/taxDetails.gohtml:53
msgctxt "input"
msgid "Email"
msgstr "Correo-e"
#: web/templates/public/booking/form.gohtml:215
#: web/templates/admin/taxDetails.gohtml:45
msgctxt "input"
msgid "Phone"
msgstr "Teléfono"
#: web/templates/public/booking/form.gohtml:226
msgctxt "input"
msgid "ACSI card? (optional)"
msgstr "¿Tarjeta ACSI? (opcional)"
#: web/templates/public/booking/form.gohtml:233
msgctxt "input"
msgid "I have read and I accept %[1]sthe reservation conditions%[2]s"
msgstr "He leído y acepto %[1]slas condiciones de reserva%[2]s"
#: web/templates/public/booking/cart.gohtml:14
msgctxt "cart"
msgid "Total"
msgstr "Total"
#: web/templates/admin/legal/form.gohtml:8
#: web/templates/admin/legal/form.gohtml:29
msgctxt "title"
@ -674,7 +704,7 @@ msgstr "Contenido"
#: web/templates/admin/campsite/form.gohtml:92
#: web/templates/admin/campsite/type/feature/form.gohtml:74
#: web/templates/admin/campsite/type/carousel/form.gohtml:59
#: web/templates/admin/campsite/type/form.gohtml:228
#: web/templates/admin/campsite/type/form.gohtml:277
#: web/templates/admin/campsite/type/option/form.gohtml:90
#: web/templates/admin/season/form.gohtml:73
#: web/templates/admin/services/form.gohtml:81
@ -696,7 +726,7 @@ msgstr "Actualizar"
#: web/templates/admin/campsite/form.gohtml:94
#: web/templates/admin/campsite/type/feature/form.gohtml:76
#: web/templates/admin/campsite/type/carousel/form.gohtml:61
#: web/templates/admin/campsite/type/form.gohtml:230
#: web/templates/admin/campsite/type/form.gohtml:279
#: web/templates/admin/campsite/type/option/form.gohtml:92
#: web/templates/admin/season/form.gohtml:75
#: web/templates/admin/services/form.gohtml:83
@ -1139,35 +1169,75 @@ msgctxt "input"
msgid "Dogs allowed"
msgstr "Se permiten perros"
#: web/templates/admin/campsite/type/form.gohtml:147
msgctxt "header"
msgid "Season"
msgstr "Temporada"
#: web/templates/admin/campsite/type/form.gohtml:148
msgctxt "header"
msgid "Price per night"
msgstr "Precio por noche"
#: web/templates/admin/campsite/type/form.gohtml:149
msgctxt "header"
msgid "Price per adult (> 16)"
msgstr "Precio por adulto (> 16)"
#: web/templates/admin/campsite/type/form.gohtml:150
msgctxt "header"
msgid "Price per teenager (1116)"
msgstr "Precio por adolescente (1116)"
#: web/templates/admin/campsite/type/form.gohtml:151
msgctxt "header"
msgid "Price per child (210)"
msgstr "Precio por niño (210)"
#: web/templates/admin/campsite/type/form.gohtml:161
#: web/templates/admin/campsite/type/option/form.gohtml:76
msgctxt "input"
msgid "Price per night"
msgstr "Precio por noche"
#: web/templates/admin/campsite/type/form.gohtml:161
#: web/templates/admin/campsite/type/form.gohtml:172
msgctxt "input"
msgid "Price per adult (> 16)"
msgstr "Precio por adulto (> 16)"
#: web/templates/admin/campsite/type/form.gohtml:183
msgctxt "input"
msgid "Price per teenager (1116)"
msgstr "Precio por adolescente (1116)"
#: web/templates/admin/campsite/type/form.gohtml:194
msgctxt "input"
msgid "Price per child (210)"
msgstr "Precio por niño (210)"
#: web/templates/admin/campsite/type/form.gohtml:210
msgctxt "input"
msgid "Spiel"
msgstr "Introducción"
#: web/templates/admin/campsite/type/form.gohtml:174
#: web/templates/admin/campsite/type/form.gohtml:223
msgctxt "input"
msgid "Info"
msgstr "Información"
#: web/templates/admin/campsite/type/form.gohtml:187
#: web/templates/admin/campsite/type/form.gohtml:236
msgctxt "input"
msgid "Facilities"
msgstr "Equipamento"
#: web/templates/admin/campsite/type/form.gohtml:200
#: web/templates/admin/campsite/type/form.gohtml:249
#: web/templates/admin/services/form.gohtml:66
#: web/templates/admin/surroundings/form.gohtml:54
msgctxt "input"
msgid "Description"
msgstr "Descripción"
#: web/templates/admin/campsite/type/form.gohtml:213
#: web/templates/admin/campsite/type/form.gohtml:262
msgctxt "input"
msgid "Additional Information"
msgstr "Información adicional"
@ -1864,7 +1934,7 @@ msgid "No booking found."
msgstr "No se ha encontrado ninguna reserva."
#: pkg/legal/admin.go:258 pkg/app/user.go:249 pkg/campsite/types/option.go:357
#: pkg/campsite/types/feature.go:272 pkg/campsite/types/admin.go:528
#: pkg/campsite/types/feature.go:272 pkg/campsite/types/admin.go:555
#: pkg/campsite/feature.go:269 pkg/season/admin.go:412
#: pkg/services/admin.go:316 pkg/surroundings/admin.go:340
#: pkg/amenity/feature.go:269 pkg/amenity/admin.go:283
@ -1872,7 +1942,7 @@ msgid "Name can not be empty."
msgstr "No podéis dejar el nombre en blanco."
#: pkg/legal/admin.go:259 pkg/campsite/types/option.go:358
#: pkg/campsite/types/feature.go:273 pkg/campsite/types/admin.go:529
#: pkg/campsite/types/feature.go:273 pkg/campsite/types/admin.go:556
#: pkg/campsite/feature.go:270 pkg/amenity/feature.go:270
msgid "Name must have at least one letter."
msgstr "El nombre tiene que tener como mínimo una letra."
@ -1902,12 +1972,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:217
#: pkg/booking/public.go:312
#: pkg/booking/public.go:351
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:218
#: pkg/booking/public.go:313
#: pkg/booking/public.go:352
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."
@ -1964,15 +2034,15 @@ msgstr "El valor del máximo tiene que ser un número entero."
msgid "Maximum must be equal or greater than minimum."
msgstr "El valor del máximo tiene que ser igual o mayor al del mínimo."
#: pkg/campsite/types/option.go:374 pkg/campsite/types/admin.go:554
#: pkg/campsite/types/option.go:374 pkg/campsite/types/admin.go:581
msgid "Price per night can not be empty."
msgstr "No podéis dejar el precio por noche en blanco."
#: pkg/campsite/types/option.go:375 pkg/campsite/types/admin.go:555
#: pkg/campsite/types/option.go:375 pkg/campsite/types/admin.go:582
msgid "Price per night must be a decimal number."
msgstr "El precio por noche tiene que ser un número decimal."
#: pkg/campsite/types/option.go:376 pkg/campsite/types/admin.go:556
#: pkg/campsite/types/option.go:376 pkg/campsite/types/admin.go:583
msgid "Price per night must be zero or greater."
msgstr "El precio por noche tiene que ser como mínimo cero."
@ -1981,69 +2051,120 @@ msgstr "El precio por noche tiene que ser como mínimo cero."
msgid "Selected icon is not valid."
msgstr "El icono escogido no es válido."
#: pkg/campsite/types/admin.go:310
#: pkg/campsite/types/admin.go:313
msgctxt "input"
msgid "Cover image"
msgstr "Imagen de portada"
#: pkg/campsite/types/admin.go:311
#: pkg/campsite/types/admin.go:314
msgctxt "action"
msgid "Set campsite type cover"
msgstr "Establecer la portada del tipo de alojamiento"
#: pkg/campsite/types/admin.go:531
#: pkg/campsite/types/admin.go:558
msgid "Check-in can not be empty."
msgstr "No podéis dejar la entrada en blanco."
#: pkg/campsite/types/admin.go:532
#: pkg/campsite/types/admin.go:559
msgid "Check-out can not be empty."
msgstr "No podéis dejar la salida en blanco."
#: pkg/campsite/types/admin.go:533
#: pkg/campsite/types/admin.go:560
msgid "Cover image can not be empty."
msgstr "No podéis dejar la imagen de portada en blanco."
#: pkg/campsite/types/admin.go:534
#: pkg/campsite/types/admin.go:561
msgid "Cover image must be an image media type."
msgstr "La imagen de portada tiene que ser un medio de tipo imagen."
#: pkg/campsite/types/admin.go:538
#: pkg/campsite/types/admin.go:565
msgid "Maximum number of campers can not be empty."
msgstr "No podéis dejar el número máximo de personas en blanco."
#: pkg/campsite/types/admin.go:539
#: pkg/campsite/types/admin.go:566
msgid "Maximum number of campers must be an integer number."
msgstr "El número máximo de personas tiene que ser entero."
#: pkg/campsite/types/admin.go:540
#: pkg/campsite/types/admin.go:567
msgid "Maximum number of campers must be one or greater."
msgstr "El número máximo de personas no puede ser cero."
#: pkg/campsite/types/admin.go:543
#: pkg/campsite/types/admin.go:570
msgid "Minimum number of nights can not be empty."
msgstr "No podéis dejar el número mínimo de noches en blanco."
#: pkg/campsite/types/admin.go:544
#: pkg/campsite/types/admin.go:571
msgid "Minimum number of nights must be an integer."
msgstr "El número mínimo de noches tiene que ser entero."
#: pkg/campsite/types/admin.go:545
#: pkg/campsite/types/admin.go:572
msgid "Minimum number of nights must be one or greater."
msgstr "El número mínimo de noches no puede ser cero."
#: pkg/campsite/types/admin.go:548
#: pkg/campsite/types/admin.go:575
msgid "Maximum number of nights can not be empty."
msgstr "No podéis dejar el número máximo de noches en blanco."
#: pkg/campsite/types/admin.go:549
#: pkg/campsite/types/admin.go:576
msgid "Maximum number of nights must be an integer."
msgstr "El número máximo de noches tiene que ser entero."
#: pkg/campsite/types/admin.go:550
#: pkg/campsite/types/admin.go:577
msgid "Maximum number of nights must be equal or greater than the minimum."
msgstr "El valor del número máximo de noches tiene que ser igual o mayor al del mínimo."
#: pkg/campsite/admin.go:275 pkg/booking/public.go:321
#: pkg/campsite/types/admin.go:586
msgid "Price per adult can not be empty."
msgstr "No podéis dejar el precio por adulto en blanco."
#: pkg/campsite/types/admin.go:587
msgid "Price per adult must be a decimal number."
msgstr "El precio por adulto tiene que ser un número decimal."
#: pkg/campsite/types/admin.go:588
msgid "Price per adult must be zero or greater."
msgstr "El precio por adulto tiene que ser como mínimo cero."
#: pkg/campsite/types/admin.go:591
msgid "Price per teenager can not be empty."
msgstr "No podéis dejar el precio por adolescente en blanco."
#: pkg/campsite/types/admin.go:592
msgid "Price per teenager must be a decimal number."
msgstr "El precio por adolescente tiene que ser un número decimal."
#: pkg/campsite/types/admin.go:593
msgid "Price per teenager must be zero or greater."
msgstr "El precio por adolescente tiene que ser como mínimo cero."
#: pkg/campsite/types/admin.go:596
msgid "Price per child can not be empty."
msgstr "No podéis dejar el precio por niño en blanco."
#: pkg/campsite/types/admin.go:597
msgid "Price per child must be a decimal number."
msgstr "El precio por niño tiene que ser un número decimal."
#: pkg/campsite/types/admin.go:598
msgid "Price per child must be zero or greater."
msgstr "El precio por niño tiene que ser como mínimo cero."
#: pkg/campsite/types/public.go:229
msgctxt "header"
msgid "Adults"
msgstr "Adultos"
#: pkg/campsite/types/public.go:235
msgctxt "header"
msgid "Teenagers (aged 11 to 16)"
msgstr "Adolescentes (de 11 a 16 años)"
#: pkg/campsite/types/public.go:241
msgctxt "header"
msgid "Children (aged 2 to 10)"
msgstr "Niños (de 2 a 10 años)"
#: pkg/campsite/admin.go:275 pkg/booking/public.go:360
msgid "Selected campsite type is not valid."
msgstr "El tipo de alojamiento escogido no es válido."
@ -2199,7 +2320,7 @@ 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:200 pkg/booking/public.go:299
#: pkg/company/admin.go:200 pkg/booking/public.go:338
msgid "Selected country is not valid."
msgstr "El país escogido no es válido."
@ -2219,11 +2340,11 @@ 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:212 pkg/booking/public.go:315
#: pkg/company/admin.go:212 pkg/booking/public.go:354
msgid "Phone can not be empty."
msgstr "No podéis dejar el teléfono en blanco."
#: pkg/company/admin.go:213 pkg/booking/public.go:316
#: pkg/company/admin.go:213 pkg/booking/public.go:355
msgid "This phone number is not valid."
msgstr "Este teléfono no es válido."
@ -2243,7 +2364,7 @@ msgstr "No podéis dejar la provincia en blanco."
msgid "Postal code can not be empty."
msgstr "No podéis dejar el código postal en blanco."
#: pkg/company/admin.go:227 pkg/booking/public.go:308
#: pkg/company/admin.go:227 pkg/booking/public.go:347
msgid "This postal code is not valid."
msgstr "Este código postal no es válido."
@ -2283,6 +2404,31 @@ 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:171
msgctxt "cart"
msgid "Night"
msgstr "Noche"
#: pkg/booking/cart.go:172
msgctxt "cart"
msgid "Adult"
msgstr "Adulto"
#: pkg/booking/cart.go:173
msgctxt "cart"
msgid "Teenager"
msgstr "Adolescente"
#: pkg/booking/cart.go:174
msgctxt "cart"
msgid "Child"
msgstr "Niño"
#: pkg/booking/cart.go:193
msgctxt "cart"
msgid "Tourist tax"
msgstr "Impuesto turístico"
#: pkg/booking/admin.go:149
msgctxt "filename"
msgid "bookings.ods"
@ -2340,54 +2486,102 @@ msgstr "La integración escogida no es válida."
msgid "The merchant key is not valid."
msgstr "Esta clave del comercio no es válida."
#: pkg/booking/public.go:303
#: pkg/booking/public.go:342
msgid "Full name can not be empty."
msgstr "No podéis dejar el nombre y los apellidos en blanco."
#: pkg/booking/public.go:304
#: pkg/booking/public.go:343
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/public.go:322
#: pkg/booking/public.go:361
msgid "Arrival date can not be empty"
msgstr "No podéis dejar la fecha de llegada en blanco."
#: pkg/booking/public.go:323
#: pkg/booking/public.go:362
msgid "Arrival date must be a valid date."
msgstr "La fecha de llegada tiene que ser una fecha válida."
#: pkg/booking/public.go:327
#: pkg/booking/public.go:366
msgid "Departure date can not be empty"
msgstr "No podéis dejar la fecha de partida en blanco."
#: pkg/booking/public.go:328
#: pkg/booking/public.go:367
msgid "Departure date must be a valid date."
msgstr "La fecha de partida tiene que ser una fecha válida."
#: pkg/booking/public.go:329
#: pkg/booking/public.go:368
msgid "The departure date must be after the arrival date."
msgstr "La fecha de partida tiene que ser posterior a la de llegada."
#: pkg/booking/public.go:332
#: pkg/booking/public.go:372
msgid "Number of adults can not be empty"
msgstr "No podéis dejar el número de adultos blanco."
#: pkg/booking/public.go:373
msgid "Number of adults must be an integer."
msgstr "El número de adultos tiene que ser entero."
#: pkg/booking/public.go:374
msgid "There must be at least one adult."
msgstr "Tiene que haber como mínimo un adulto."
#: pkg/booking/public.go:377
msgid "Number of teenagers can not be empty"
msgstr "No podéis dejar el número de adolescentes en blanco."
#: pkg/booking/public.go:378
msgid "Number of teenagers must be an integer."
msgstr "El número de adolescentes tiene que ser entero."
#: pkg/booking/public.go:379
msgid "Number of teenagers can not be negative."
msgstr "El número de adolescentes no puede ser negativo."
#: pkg/booking/public.go:382
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:383
msgid "Number of children must be an integer."
msgstr "El número de niños tiene que ser entero."
#: pkg/booking/public.go:384
msgid "Number of children can not be negative."
msgstr "El número de niños no puede ser negativo."
#: pkg/booking/public.go:387
msgid "Number of dogs can not be empty"
msgstr "No podéis dejar el número de perros en blanco."
#: pkg/booking/public.go:388
msgid "Number of dogs must be an integer."
msgstr "El número de perros tiene que ser entero."
#: pkg/booking/public.go:389
msgid "Number of dogs can not be negative."
msgstr "El número de perros no puede ser negativo."
#: pkg/booking/public.go:392
msgid "It is mandatory to agree to the reservation conditions."
msgstr "Es obligatorio aceptar las condiciones de reserva."
#: pkg/booking/public.go:335
#: pkg/booking/public.go:395
#, c-format
msgid "%s can not be empty"
msgstr "No podéis dejar %s en blanco."
#: pkg/booking/public.go:336
#: pkg/booking/public.go:396
#, c-format
msgid "%s must be an integer."
msgstr "%s tiene que ser un número entero."
#: pkg/booking/public.go:337
#: pkg/booking/public.go:397
#, c-format
msgid "%s must be %d or greater."
msgstr "%s tiene que ser como mínimo %d."
#: pkg/booking/public.go:338
#: pkg/booking/public.go:398
#, c-format
msgid "%s must be at most %d."
msgstr "%s tiene que ser como máximo %d"
@ -2458,55 +2652,15 @@ msgstr "%s tiene que ser como máximo %d"
#~ msgid "Party Details"
#~ msgstr "Datos de los visitantes"
#~ msgctxt "input"
#~ msgid "Adults"
#~ msgstr "Adultos"
#~ msgctxt "input"
#~ msgid "Teenagers (from 11 to 16 years old)"
#~ msgstr "Adolescentes (de 11 a 16 años)"
#~ msgctxt "input"
#~ msgid "Children (up to 10 years old)"
#~ msgstr "Niños (hasta 10 años)"
#~ msgctxt "input"
#~ msgid "Dogs"
#~ msgstr "Perros"
#~ msgid "Number of adults can not be empty"
#~ msgstr "No podéis dejar el número de adultos blanco."
#~ msgid "Number of adults must be an integer."
#~ msgstr "El número de adultos tiene que ser entero."
#~ msgid "Number of adults must be one or greater."
#~ msgstr "El número de adultos no puede ser cero."
#~ msgid "Number of teenagers can not be empty"
#~ msgstr "No podéis dejar el número de adolescentes en blanco."
#~ msgid "Number of teenagers must be an integer."
#~ msgstr "El número de adolescentes tiene que ser entero."
#~ msgid "Number of teenagers must be zero or greater."
#~ msgstr "El número de adolescentes tiene que ser como mínimo cero."
#~ msgid "Number of children can not be empty"
#~ msgstr "No podéis dejar el número de niños en blanco."
#~ msgid "Number of children must be an integer."
#~ msgstr "El número de niños tiene que ser entero."
#~ msgid "Number of children must be zero or greater."
#~ msgstr "El número de niños tiene que ser como mínimo cero."
#~ msgid "Number of dogs can not be empty"
#~ msgstr "No podéis dejar el número de perros en blanco."
#~ msgid "Number of dogs must be an integer."
#~ msgstr "El número de perros tiene que ser entero."
#~ msgid "Number of dogs must be zero or greater."
#~ msgstr "El número de perros tiene que ser como mínimo cero."

514
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-02-02 21:57+0100\n"
"POT-Creation-Date: 2024-02-04 06:23+0100\n"
"PO-Revision-Date: 2023-12-20 10:13+0100\n"
"Last-Translator: Oriol Carbonell <info@oriolcarbonell.cat>\n"
"Language-Team: French <traduc@traduc.org>\n"
@ -95,7 +95,7 @@ msgid "The campsite offers many different services."
msgstr "Le camping propose de nombreux services différents."
#: web/templates/public/amenity.gohtml:39
#: web/templates/public/campsite/type.gohtml:114
#: web/templates/public/campsite/type.gohtml:113
#: web/templates/public/campsite/page.gohtml:39
msgctxt "title"
msgid "Features"
@ -165,7 +165,7 @@ msgid "Check-out Date"
msgstr "Date de départ"
#: web/templates/public/campsite/type.gohtml:56
#: web/templates/public/booking.gohtml:171
#: web/templates/public/booking/cart.gohtml:25
msgctxt "action"
msgid "Book"
msgstr "Réserver"
@ -183,61 +183,60 @@ msgctxt "title"
msgid "Prices"
msgstr "Prix"
#: web/templates/public/campsite/type.gohtml:87
#: web/templates/public/campsite/type.gohtml:93
msgid "%s: %s €/night"
msgstr "%s : %s €/nuit"
#: web/templates/public/campsite/type.gohtml:88
msgid "%s: %s/night"
msgstr "%s : %s/nuit"
#: web/templates/public/campsite/type.gohtml:89
msgid "%s €/night"
msgstr "%s €/nuit"
#: web/templates/public/campsite/type.gohtml:90
msgid "%s/night"
msgstr "%s/nuit"
#: web/templates/public/campsite/type.gohtml:96
#: web/templates/public/campsite/type.gohtml:95
msgid "*Minimum %d nights per stay"
msgstr "*Minimum %d nuits par séjour"
#: web/templates/public/campsite/type.gohtml:101
#: web/templates/public/campsite/type.gohtml:100
msgid "10 % VAT included."
msgstr "10 % TVA incluse."
#: web/templates/public/campsite/type.gohtml:102
#: web/templates/public/campsite/type.gohtml:101
msgid "Tourist tax: %s/night per person aged 17 or older."
msgstr "Taxe touristique: %s/nuit par personne de plus de 16 ans."
#: web/templates/public/campsite/type.gohtml:104
#: web/templates/public/campsite/type.gohtml:103
msgid "Dogs: %s/night, tied, accompanied, and minimal barking."
msgstr "Chiens : %s/nuit, attachés, accompagnés et aboiements minimes."
#: web/templates/public/campsite/type.gohtml:106
#: web/templates/public/campsite/type.gohtml:105
msgid "No dogs allowed."
msgstr "Chiens interdits."
#: web/templates/public/campsite/type.gohtml:125
#: web/templates/public/campsite/type.gohtml:124
msgctxt "title"
msgid "Info"
msgstr "Info"
#: web/templates/public/campsite/type.gohtml:129
#: web/templates/public/campsite/type.gohtml:128
msgctxt "title"
msgid "Facilities"
msgstr "Installations"
#: web/templates/public/campsite/type.gohtml:133
#: web/templates/public/campsite/type.gohtml:132
msgctxt "title"
msgid "Description"
msgstr "Description"
#: web/templates/public/campsite/type.gohtml:137
#: web/templates/public/campsite/type.gohtml:136
msgctxt "title"
msgid "Additional Information"
msgstr "Informations Complémentaires"
#: web/templates/public/campsite/type.gohtml:140
#: web/templates/public/campsite/type.gohtml:139
msgctxt "time"
msgid "Check-in"
msgstr "Arrivée"
#: web/templates/public/campsite/type.gohtml:144
#: web/templates/public/campsite/type.gohtml:143
msgctxt "time"
msgid "Check-out"
msgstr "Départ"
@ -483,99 +482,6 @@ msgctxt "legend"
msgid "Faucet"
msgstr "Robinet"
#: web/templates/public/booking.gohtml:7 web/templates/public/booking.gohtml:12
#: web/templates/public/layout.gohtml:70
msgctxt "title"
msgid "Booking"
msgstr "Réservation"
#: web/templates/public/booking.gohtml:16
msgctxt "title"
msgid "Customer Details"
msgstr "Détails du client"
#: web/templates/public/booking.gohtml:19
msgctxt "input"
msgid "Full name"
msgstr "Nom et prénom"
#: web/templates/public/booking.gohtml:28
msgctxt "input"
msgid "Address (optional)"
msgstr "Adresse (Facultatif)"
#: web/templates/public/booking.gohtml:37
msgctxt "input"
msgid "Postcode (optional)"
msgstr "Code postal (Facultatif)"
#: web/templates/public/booking.gohtml:46
msgctxt "input"
msgid "Town or village (optional)"
msgstr "Ville (Facultatif)"
#: web/templates/public/booking.gohtml:55
#: web/templates/admin/taxDetails.gohtml:101
msgctxt "input"
msgid "Country"
msgstr "Pays"
#: web/templates/public/booking.gohtml:58
msgid "Choose a country"
msgstr "Choisissez un pays"
#: web/templates/public/booking.gohtml:66 web/templates/admin/login.gohtml:27
#: web/templates/admin/profile.gohtml:38
#: web/templates/admin/taxDetails.gohtml:53
msgctxt "input"
msgid "Email"
msgstr "E-mail"
#: web/templates/public/booking.gohtml:75
#: web/templates/admin/taxDetails.gohtml:45
msgctxt "input"
msgid "Phone"
msgstr "Téléphone"
#: web/templates/public/booking.gohtml:84
msgctxt "title"
msgid "Accommodation"
msgstr "Hébergement"
#: web/templates/public/booking.gohtml:101
msgctxt "input"
msgid "Area preferences (optional)"
msgstr "Préférences de zone (facultatif)"
#: web/templates/public/booking.gohtml:108
msgid "Campground map"
msgstr "Plan du camping"
#: web/templates/public/booking.gohtml:126
msgctxt "title"
msgid "Booking Period"
msgstr "Période de réservation"
#: web/templates/public/booking.gohtml:129
msgctxt "input"
msgid "Arrival date"
msgstr "Date darrivée"
#: web/templates/public/booking.gohtml:139
msgctxt "input"
msgid "Departure date"
msgstr "Date de depart"
#: web/templates/public/booking.gohtml:151
msgctxt "input"
msgid "ACSI card? (optional)"
msgstr "Carte ACSI ? (Facultatif)"
#: web/templates/public/booking.gohtml:158
msgctxt "input"
msgid "I have read and I accept %[1]sthe reservation conditions%[2]s"
msgstr "Jai lu et jaccepte %[1]sles conditions de réservation%[2]s"
#: web/templates/public/layout.gohtml:12 web/templates/public/layout.gohtml:48
msgid "Campsite Montagut"
msgstr "Camping Montagut"
@ -609,6 +515,13 @@ msgctxt "title"
msgid "Campsites"
msgstr "Locatifs"
#: web/templates/public/layout.gohtml:70
#: web/templates/public/booking/form.gohtml:7
#: web/templates/public/booking/form.gohtml:16
msgctxt "title"
msgid "Booking"
msgstr "Réservation"
#: web/templates/public/layout.gohtml:91
msgctxt "title"
msgid "Sections"
@ -623,6 +536,123 @@ msgstr "Ouverture"
msgid "<abbr title=\"Catalonia Tourism Registry\">RTC</abbr> <abbr title=\"Number\">#</abbr>%s"
msgstr "<abbr title=\"Registre du tourisme de Catalogne\"># RTC</abbr> %s"
#: web/templates/public/booking/form.gohtml:29
msgctxt "title"
msgid "Accommodation"
msgstr "Hébergement"
#: web/templates/public/booking/form.gohtml:43
msgctxt "title"
msgid "Booking Period"
msgstr "Période de réservation"
#: web/templates/public/booking/form.gohtml:46
msgctxt "input"
msgid "Arrival date"
msgstr "Date darrivée"
#: web/templates/public/booking/form.gohtml:57
msgctxt "input"
msgid "Departure date"
msgstr "Date de depart"
#: web/templates/public/booking/form.gohtml:72
msgctxt "title"
msgid "Guests"
msgstr "Personnes logeant"
#: web/templates/public/booking/form.gohtml:76
msgctxt "input"
msgid "Adults aged 17 or older"
msgstr "Adultes âgés 17 ans ou plus"
#: web/templates/public/booking/form.gohtml:86
msgctxt "input"
msgid "Teenagers from 11 to 16 years old"
msgstr "Adolescents de 11 à 16 ans"
#: web/templates/public/booking/form.gohtml:96
msgctxt "input"
msgid "Children from 2 to 10 years old"
msgstr "Enfants de 2 à 10 ans"
#: web/templates/public/booking/form.gohtml:106
msgctxt "input"
msgid "Dogs"
msgstr "Chiens"
#: web/templates/public/booking/form.gohtml:127
msgctxt "input"
msgid "Area preferences (optional)"
msgstr "Préférences de zone (facultatif)"
#: web/templates/public/booking/form.gohtml:129
msgid "Campground map"
msgstr "Plan du camping"
#: web/templates/public/booking/form.gohtml:156
msgctxt "title"
msgid "Customer Details"
msgstr "Détails du client"
#: web/templates/public/booking/form.gohtml:159
msgctxt "input"
msgid "Full name"
msgstr "Nom et prénom"
#: web/templates/public/booking/form.gohtml:168
msgctxt "input"
msgid "Address (optional)"
msgstr "Adresse (Facultatif)"
#: web/templates/public/booking/form.gohtml:177
msgctxt "input"
msgid "Postcode (optional)"
msgstr "Code postal (Facultatif)"
#: web/templates/public/booking/form.gohtml:186
msgctxt "input"
msgid "Town or village (optional)"
msgstr "Ville (Facultatif)"
#: web/templates/public/booking/form.gohtml:195
#: web/templates/admin/taxDetails.gohtml:101
msgctxt "input"
msgid "Country"
msgstr "Pays"
#: web/templates/public/booking/form.gohtml:198
msgid "Choose a country"
msgstr "Choisissez un pays"
#: web/templates/public/booking/form.gohtml:206
#: web/templates/admin/login.gohtml:27 web/templates/admin/profile.gohtml:38
#: web/templates/admin/taxDetails.gohtml:53
msgctxt "input"
msgid "Email"
msgstr "E-mail"
#: web/templates/public/booking/form.gohtml:215
#: web/templates/admin/taxDetails.gohtml:45
msgctxt "input"
msgid "Phone"
msgstr "Téléphone"
#: web/templates/public/booking/form.gohtml:226
msgctxt "input"
msgid "ACSI card? (optional)"
msgstr "Carte ACSI ? (Facultatif)"
#: web/templates/public/booking/form.gohtml:233
msgctxt "input"
msgid "I have read and I accept %[1]sthe reservation conditions%[2]s"
msgstr "Jai lu et jaccepte %[1]sles conditions de réservation%[2]s"
#: web/templates/public/booking/cart.gohtml:14
msgctxt "cart"
msgid "Total"
msgstr "Totale"
#: web/templates/admin/legal/form.gohtml:8
#: web/templates/admin/legal/form.gohtml:29
msgctxt "title"
@ -675,7 +705,7 @@ msgstr "Contenu"
#: web/templates/admin/campsite/form.gohtml:92
#: web/templates/admin/campsite/type/feature/form.gohtml:74
#: web/templates/admin/campsite/type/carousel/form.gohtml:59
#: web/templates/admin/campsite/type/form.gohtml:228
#: web/templates/admin/campsite/type/form.gohtml:277
#: web/templates/admin/campsite/type/option/form.gohtml:90
#: web/templates/admin/season/form.gohtml:73
#: web/templates/admin/services/form.gohtml:81
@ -697,7 +727,7 @@ msgstr "Mettre à jour"
#: web/templates/admin/campsite/form.gohtml:94
#: web/templates/admin/campsite/type/feature/form.gohtml:76
#: web/templates/admin/campsite/type/carousel/form.gohtml:61
#: web/templates/admin/campsite/type/form.gohtml:230
#: web/templates/admin/campsite/type/form.gohtml:279
#: web/templates/admin/campsite/type/option/form.gohtml:92
#: web/templates/admin/season/form.gohtml:75
#: web/templates/admin/services/form.gohtml:83
@ -1140,35 +1170,75 @@ msgctxt "input"
msgid "Dogs allowed"
msgstr "Chiens acceptés"
#: web/templates/admin/campsite/type/form.gohtml:147
msgctxt "header"
msgid "Season"
msgstr "Saison"
#: web/templates/admin/campsite/type/form.gohtml:148
msgctxt "header"
msgid "Price per night"
msgstr "Prix par nuit"
#: web/templates/admin/campsite/type/form.gohtml:149
msgctxt "header"
msgid "Price per adult (> 16)"
msgstr "Prix per adulte (> 16)"
#: web/templates/admin/campsite/type/form.gohtml:150
msgctxt "header"
msgid "Price per teenager (1116)"
msgstr "Prix per adolescent (1116)"
#: web/templates/admin/campsite/type/form.gohtml:151
msgctxt "header"
msgid "Price per child (210)"
msgstr "Prix per enfant (210)"
#: web/templates/admin/campsite/type/form.gohtml:161
#: web/templates/admin/campsite/type/option/form.gohtml:76
msgctxt "input"
msgid "Price per night"
msgstr "Prix par nuit"
#: web/templates/admin/campsite/type/form.gohtml:161
#: web/templates/admin/campsite/type/form.gohtml:172
msgctxt "input"
msgid "Price per adult (> 16)"
msgstr "Prix per adulte (> 16)"
#: web/templates/admin/campsite/type/form.gohtml:183
msgctxt "input"
msgid "Price per teenager (1116)"
msgstr "Prix per adolescent (1116)"
#: web/templates/admin/campsite/type/form.gohtml:194
msgctxt "input"
msgid "Price per child (210)"
msgstr "Prix per enfant (210)"
#: web/templates/admin/campsite/type/form.gohtml:210
msgctxt "input"
msgid "Spiel"
msgstr "Boniment"
#: web/templates/admin/campsite/type/form.gohtml:174
#: web/templates/admin/campsite/type/form.gohtml:223
msgctxt "input"
msgid "Info"
msgstr "Info"
#: web/templates/admin/campsite/type/form.gohtml:187
#: web/templates/admin/campsite/type/form.gohtml:236
msgctxt "input"
msgid "Facilities"
msgstr "Installations"
#: web/templates/admin/campsite/type/form.gohtml:200
#: web/templates/admin/campsite/type/form.gohtml:249
#: web/templates/admin/services/form.gohtml:66
#: web/templates/admin/surroundings/form.gohtml:54
msgctxt "input"
msgid "Description"
msgstr "Description"
#: web/templates/admin/campsite/type/form.gohtml:213
#: web/templates/admin/campsite/type/form.gohtml:262
msgctxt "input"
msgid "Additional Information"
msgstr "Informations Complémentaires"
@ -1865,7 +1935,7 @@ msgid "No booking found."
msgstr "Aucune réservation trouvée."
#: pkg/legal/admin.go:258 pkg/app/user.go:249 pkg/campsite/types/option.go:357
#: pkg/campsite/types/feature.go:272 pkg/campsite/types/admin.go:528
#: pkg/campsite/types/feature.go:272 pkg/campsite/types/admin.go:555
#: pkg/campsite/feature.go:269 pkg/season/admin.go:412
#: pkg/services/admin.go:316 pkg/surroundings/admin.go:340
#: pkg/amenity/feature.go:269 pkg/amenity/admin.go:283
@ -1873,7 +1943,7 @@ msgid "Name can not be empty."
msgstr "Le nom ne peut pas être laissé vide."
#: pkg/legal/admin.go:259 pkg/campsite/types/option.go:358
#: pkg/campsite/types/feature.go:273 pkg/campsite/types/admin.go:529
#: pkg/campsite/types/feature.go:273 pkg/campsite/types/admin.go:556
#: pkg/campsite/feature.go:270 pkg/amenity/feature.go:270
msgid "Name must have at least one letter."
msgstr "Le nom doit comporter au moins une lettre."
@ -1903,12 +1973,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:217
#: pkg/booking/public.go:312
#: pkg/booking/public.go:351
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:218
#: pkg/booking/public.go:313
#: pkg/booking/public.go:352
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."
@ -1965,15 +2035,15 @@ msgstr "Le maximum doit être un nombre entier."
msgid "Maximum must be equal or greater than minimum."
msgstr "Le maximum doit être égal ou supérieur au minimum."
#: pkg/campsite/types/option.go:374 pkg/campsite/types/admin.go:554
#: pkg/campsite/types/option.go:374 pkg/campsite/types/admin.go:581
msgid "Price per night can not be empty."
msgstr "Le prix par nuit ne peut pas être vide."
#: pkg/campsite/types/option.go:375 pkg/campsite/types/admin.go:555
#: pkg/campsite/types/option.go:375 pkg/campsite/types/admin.go:582
msgid "Price per night must be a decimal number."
msgstr "Le prix par nuit doit être un nombre décimal."
#: pkg/campsite/types/option.go:376 pkg/campsite/types/admin.go:556
#: pkg/campsite/types/option.go:376 pkg/campsite/types/admin.go:583
msgid "Price per night must be zero or greater."
msgstr "Le prix par nuit doit être égal ou supérieur."
@ -1982,69 +2052,120 @@ msgstr "Le prix par nuit doit être égal ou supérieur."
msgid "Selected icon is not valid."
msgstr "Licône sélectionnée nest pas valide."
#: pkg/campsite/types/admin.go:310
#: pkg/campsite/types/admin.go:313
msgctxt "input"
msgid "Cover image"
msgstr "Image de couverture"
#: pkg/campsite/types/admin.go:311
#: pkg/campsite/types/admin.go:314
msgctxt "action"
msgid "Set campsite type cover"
msgstr "Définir une couverture type camping"
#: pkg/campsite/types/admin.go:531
#: pkg/campsite/types/admin.go:558
msgid "Check-in can not be empty."
msgstr "Larrivée ne peut pas être vide."
#: pkg/campsite/types/admin.go:532
#: pkg/campsite/types/admin.go:559
msgid "Check-out can not be empty."
msgstr "Le départ ne peut pas être vide."
#: pkg/campsite/types/admin.go:533
#: pkg/campsite/types/admin.go:560
msgid "Cover image can not be empty."
msgstr "Limage de couverture ne peut pas être vide."
#: pkg/campsite/types/admin.go:534
#: pkg/campsite/types/admin.go:561
msgid "Cover image must be an image media type."
msgstr "Limage de couverture doit être de type média dimage."
#: pkg/campsite/types/admin.go:538
#: pkg/campsite/types/admin.go:565
msgid "Maximum number of campers can not be empty."
msgstr "Le nombre maximum de campeurs ne peut pas être vide."
#: pkg/campsite/types/admin.go:539
#: pkg/campsite/types/admin.go:566
msgid "Maximum number of campers must be an integer number."
msgstr "Le nombre maximum de campeurs doit être un nombre entier."
#: pkg/campsite/types/admin.go:540
#: pkg/campsite/types/admin.go:567
msgid "Maximum number of campers must be one or greater."
msgstr "Le nombre maximum de campeurs doit être égal ou supérieur à un campeur."
#: pkg/campsite/types/admin.go:543
#: pkg/campsite/types/admin.go:570
msgid "Minimum number of nights can not be empty."
msgstr "Le nombre minimum de nuits ne peut pas être vide."
#: pkg/campsite/types/admin.go:544
#: pkg/campsite/types/admin.go:571
msgid "Minimum number of nights must be an integer."
msgstr "Le nombre minimum de nuits doit être un entier."
#: pkg/campsite/types/admin.go:545
#: pkg/campsite/types/admin.go:572
msgid "Minimum number of nights must be one or greater."
msgstr "Le nombre minimum de nuits doit être supérieur ou égal à une nuit."
#: pkg/campsite/types/admin.go:548
#: pkg/campsite/types/admin.go:575
msgid "Maximum number of nights can not be empty."
msgstr "Le nombre maximale de nuits ne peut pas être vide."
#: pkg/campsite/types/admin.go:549
#: pkg/campsite/types/admin.go:576
msgid "Maximum number of nights must be an integer."
msgstr "Le nombre maximale de nuits doit être un entier."
#: pkg/campsite/types/admin.go:550
#: pkg/campsite/types/admin.go:577
msgid "Maximum number of nights must be equal or greater than the minimum."
msgstr "Le nombre maximale de nuits doit être égal ou supérieur au minimum."
#: pkg/campsite/admin.go:275 pkg/booking/public.go:321
#: pkg/campsite/types/admin.go:586
msgid "Price per adult can not be empty."
msgstr "Le prix par adulte ne peut pas être vide."
#: pkg/campsite/types/admin.go:587
msgid "Price per adult must be a decimal number."
msgstr "Le prix par adulte doit être un nombre décimal."
#: pkg/campsite/types/admin.go:588
msgid "Price per adult must be zero or greater."
msgstr "Le prix par adulte doit être égal ou supérieur."
#: pkg/campsite/types/admin.go:591
msgid "Price per teenager can not be empty."
msgstr "Le prix par adolescent ne peut pas être vide."
#: pkg/campsite/types/admin.go:592
msgid "Price per teenager must be a decimal number."
msgstr "Le prix par adolescent doit être un nombre décimal."
#: pkg/campsite/types/admin.go:593
msgid "Price per teenager must be zero or greater."
msgstr "Le prix par adolescent doit être égal ou supérieur."
#: pkg/campsite/types/admin.go:596
msgid "Price per child can not be empty."
msgstr "Le prix par enfant ne peut pas être vide."
#: pkg/campsite/types/admin.go:597
msgid "Price per child must be a decimal number."
msgstr "Le prix par enfant doit être un nombre décimal."
#: pkg/campsite/types/admin.go:598
msgid "Price per child must be zero or greater."
msgstr "Le prix par enfant doit être égal ou supérieur."
#: pkg/campsite/types/public.go:229
msgctxt "header"
msgid "Adults"
msgstr "Adultes"
#: pkg/campsite/types/public.go:235
msgctxt "header"
msgid "Teenagers (aged 11 to 16)"
msgstr "Adolescents (de 11 à 16 anys)"
#: pkg/campsite/types/public.go:241
msgctxt "header"
msgid "Children (aged 2 to 10)"
msgstr "Enfants (de 2 à 10 anys)"
#: pkg/campsite/admin.go:275 pkg/booking/public.go:360
msgid "Selected campsite type is not valid."
msgstr "Le type demplacement sélectionné nest pas valide."
@ -2200,7 +2321,7 @@ 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:200 pkg/booking/public.go:299
#: pkg/company/admin.go:200 pkg/booking/public.go:338
msgid "Selected country is not valid."
msgstr "Le pays sélectionné nest pas valide."
@ -2220,11 +2341,11 @@ 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:212 pkg/booking/public.go:315
#: pkg/company/admin.go:212 pkg/booking/public.go:354
msgid "Phone can not be empty."
msgstr "Le téléphone ne peut pas être vide."
#: pkg/company/admin.go:213 pkg/booking/public.go:316
#: pkg/company/admin.go:213 pkg/booking/public.go:355
msgid "This phone number is not valid."
msgstr "Ce numéro de téléphone nest pas valide."
@ -2244,7 +2365,7 @@ msgstr "La province ne peut pas être vide."
msgid "Postal code can not be empty."
msgstr "Le code postal ne peut pas être vide."
#: pkg/company/admin.go:227 pkg/booking/public.go:308
#: pkg/company/admin.go:227 pkg/booking/public.go:347
msgid "This postal code is not valid."
msgstr "Ce code postal nest pas valide."
@ -2284,6 +2405,31 @@ 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:171
msgctxt "cart"
msgid "Night"
msgstr "Nuit"
#: pkg/booking/cart.go:172
msgctxt "cart"
msgid "Adult"
msgstr "Adulte"
#: pkg/booking/cart.go:173
msgctxt "cart"
msgid "Teenager"
msgstr "Adolescent"
#: pkg/booking/cart.go:174
msgctxt "cart"
msgid "Child"
msgstr "Enfant"
#: pkg/booking/cart.go:193
msgctxt "cart"
msgid "Tourist tax"
msgstr "Taxe touristique"
#: pkg/booking/admin.go:149
msgctxt "filename"
msgid "bookings.ods"
@ -2341,54 +2487,102 @@ msgstr "Lintégration sélectionnée nest pas valide."
msgid "The merchant key is not valid."
msgstr "La clé marchand nest pas valide."
#: pkg/booking/public.go:303
#: pkg/booking/public.go:342
msgid "Full name can not be empty."
msgstr "Le nom complet ne peut pas être vide."
#: pkg/booking/public.go:304
#: pkg/booking/public.go:343
msgid "Full name must have at least one letter."
msgstr "Le nom complet doit comporter au moins une lettre."
#: pkg/booking/public.go:322
#: pkg/booking/public.go:361
msgid "Arrival date can not be empty"
msgstr "La date darrivée ne peut pas être vide"
#: pkg/booking/public.go:323
#: pkg/booking/public.go:362
msgid "Arrival date must be a valid date."
msgstr "La date darrivée doit être une date valide."
#: pkg/booking/public.go:327
#: pkg/booking/public.go:366
msgid "Departure date can not be empty"
msgstr "La date de départ ne peut pas être vide"
#: pkg/booking/public.go:328
#: pkg/booking/public.go:367
msgid "Departure date must be a valid date."
msgstr "La date de départ doit être une date valide."
#: pkg/booking/public.go:329
#: pkg/booking/public.go:368
msgid "The departure date must be after the arrival date."
msgstr "La date de départ doit être postérieure à la date darrivée."
#: pkg/booking/public.go:332
#: pkg/booking/public.go:372
msgid "Number of adults can not be empty"
msgstr "Le nombre dadultes ne peut pas être vide."
#: pkg/booking/public.go:373
msgid "Number of adults must be an integer."
msgstr "Le nombre dadultes doit être un entier."
#: pkg/booking/public.go:374
msgid "There must be at least one adult."
msgstr "Il doit y avoir au moins un adulte."
#: pkg/booking/public.go:377
msgid "Number of teenagers can not be empty"
msgstr "Le nombre dadolescents ne peut pas être vide."
#: pkg/booking/public.go:378
msgid "Number of teenagers must be an integer."
msgstr "Le nombre dadolescents doit être un entier."
#: pkg/booking/public.go:379
msgid "Number of teenagers can not be negative."
msgstr "Le nombre dadolescents ne peut pas être négatif."
#: pkg/booking/public.go:382
msgid "Number of children can not be empty"
msgstr "Le nombre denfants ne peut pas être vide."
#: pkg/booking/public.go:383
msgid "Number of children must be an integer."
msgstr "Le nombre denfants doit être un entier."
#: pkg/booking/public.go:384
msgid "Number of children can not be negative."
msgstr "Le nombre denfants ne peut pas être négatif."
#: pkg/booking/public.go:387
msgid "Number of dogs can not be empty"
msgstr "Le nombre de chiens ne peut pas être vide."
#: pkg/booking/public.go:388
msgid "Number of dogs must be an integer."
msgstr "Le nombre de chiens nuits être un entier."
#: pkg/booking/public.go:389
msgid "Number of dogs can not be negative."
msgstr "Le nombre de chiens ne peut pas être négatif."
#: pkg/booking/public.go:392
msgid "It is mandatory to agree to the reservation conditions."
msgstr "Il est obligatoire daccepter les conditions de réservation."
#: pkg/booking/public.go:335
#: pkg/booking/public.go:395
#, c-format
msgid "%s can not be empty"
msgstr "%s ne peut pas être vide"
#: pkg/booking/public.go:336
#: pkg/booking/public.go:396
#, c-format
msgid "%s must be an integer."
msgstr "%s doit être un entier."
#: pkg/booking/public.go:337
#: pkg/booking/public.go:397
#, c-format
msgid "%s must be %d or greater."
msgstr "%s doit être %d ou plus."
#: pkg/booking/public.go:338
#: pkg/booking/public.go:398
#, c-format
msgid "%s must be at most %d."
msgstr "%s doit être tout au plus %d."

View File

@ -0,0 +1,11 @@
-- Revert camper:campsite_type_cost__per_age from pg
begin;
alter table camper.campsite_type_cost
drop column if exists cost_per_adult
, drop column if exists cost_per_teenager
, drop column if exists cost_per_child
;
commit;

View File

@ -8,7 +8,7 @@ begin;
set search_path to camper, public;
drop function if exists set_campsite_type_cost(uuid, integer, text, integer);
drop function if exists set_campsite_type_cost(uuid, integer, text, text, text, text);
create or replace function set_campsite_type_cost(slug uuid, season_id integer, min_nights integer, cost_per_night text, decimal_places integer default 2) returns void as
$$

View File

@ -227,5 +227,6 @@ edit_campsite_type [edit_campsite_type@v2 campsite_type__overflow_allowed campsi
campsite_type__bookable_nights [campsite_type campsite_type_cost] 2024-01-31T19:45:46Z jordi fita mas <jordi@tandem.blog> # Add bookable_nights to campsite_type
add_campsite_type [add_campsite_type@v3 campsite_type__bookable_nights] 2024-01-31T20:46:47Z jordi fita mas <jordi@tandem.blog> # Add bookable_nights param to add_campsite_type
edit_campsite_type [edit_campsite_type@v3 campsite_type__bookable_nights] 2024-01-31T20:51:40Z jordi fita mas <jordi@tandem.blog> # Add bookable_nights param to edit_campsite_type
set_campsite_type_cost [set_campsite_type_cost@v3 campsite_type__bookable_nights] 2024-01-31T21:02:06Z jordi fita mas <jordi@tandem.blog> # Remove min_nights parameter from set_campsite_type_cost
campsite_type_cost__per_age [campsite_type_cost] 2024-02-03T20:48:54Z jordi fita mas <jordi@tandem.blog> # Add cost_per_adult, cost_per_teenager, and cost_per_child fields to campsite_type_cost
set_campsite_type_cost [set_campsite_type_cost@v3 campsite_type__bookable_nights campsite_type_cost__per_age] 2024-01-31T21:02:06Z jordi fita mas <jordi@tandem.blog> # Remove min_nights parameter from set_campsite_type_cost, and add per_adult, per_teenager, and per_child parameters
campsite_type_cost__-min_nights [campsite_type__bookable_nights set_campsite_type_cost] 2024-01-31T21:46:33Z jordi fita mas <jordi@tandem.blog> # Remove min_nights field from campsite_type_cost relation

View File

@ -5,7 +5,7 @@ reset client_min_messages;
begin;
select plan(42);
select plan(57);
set search_path to camper, public;
@ -36,6 +36,21 @@ select col_type_is('campsite_type_cost', 'cost_per_night', 'integer');
select col_not_null('campsite_type_cost', 'cost_per_night');
select col_hasnt_default('campsite_type_cost', 'cost_per_night');
select has_column('campsite_type_cost', 'cost_per_adult');
select col_type_is('campsite_type_cost', 'cost_per_adult', 'integer');
select col_not_null('campsite_type_cost', 'cost_per_adult');
select col_hasnt_default('campsite_type_cost', 'cost_per_adult');
select has_column('campsite_type_cost', 'cost_per_teenager');
select col_type_is('campsite_type_cost', 'cost_per_teenager', 'integer');
select col_not_null('campsite_type_cost', 'cost_per_teenager');
select col_hasnt_default('campsite_type_cost', 'cost_per_teenager');
select has_column('campsite_type_cost', 'cost_per_child');
select col_type_is('campsite_type_cost', 'cost_per_child', 'integer');
select col_not_null('campsite_type_cost', 'cost_per_child');
select col_hasnt_default('campsite_type_cost', 'cost_per_child');
set client_min_messages to warning;
truncate campsite_type_cost cascade;
@ -90,9 +105,9 @@ values (26, 2, 'Low')
, (29, 4, 'Mid')
;
insert into campsite_type_cost (campsite_type_id, season_id, cost_per_night)
values (16, 26, 2)
, (18, 28, 4)
insert into campsite_type_cost (campsite_type_id, season_id, cost_per_night, cost_per_adult, cost_per_teenager, cost_per_child)
values (16, 26, 2, 3, 1, 0)
, (18, 28, 4, 5, 6, 7)
;
prepare campsite_season_data as
@ -113,7 +128,7 @@ reset role;
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2');
select lives_ok(
$$ insert into campsite_type_cost(campsite_type_id, season_id, cost_per_night) values (16, 27, 3) $$,
$$ insert into campsite_type_cost(campsite_type_id, season_id, cost_per_night, cost_per_adult, cost_per_teenager, cost_per_child) values (16, 27, 3, 4, 5, 6) $$,
'Admin from company 2 should be able to insert a new campsite type season to that company.'
);
@ -154,19 +169,19 @@ select bag_eq(
);
select throws_ok(
$$ insert into campsite_type_cost (campsite_type_id, season_id, cost_per_night) values (18, 29, 5) $$,
$$ insert into campsite_type_cost (campsite_type_id, season_id, cost_per_night, cost_per_adult, cost_per_teenager, cost_per_child) values (18, 29, 5, 6, 7, 8) $$,
'42501', 'new row violates row-level security policy for table "campsite_type_cost"',
'Admin from company 2 should NOT be able to insert new campsite type costs to company 4.'
);
select throws_ok(
$$ insert into campsite_type_cost (campsite_type_id, season_id, cost_per_night) values (18, 27, 5) $$,
$$ insert into campsite_type_cost (campsite_type_id, season_id, cost_per_night, cost_per_adult, cost_per_teenager, cost_per_child) values (18, 27, 5, 6, 7, 8) $$,
'42501', 'new row violates row-level security policy for table "campsite_type_cost"',
'Admin from company 2 should NOT be able to insert new row with a campsite type from company 4.'
);
select throws_ok(
$$ insert into campsite_type_cost (campsite_type_id, season_id, cost_per_night) values (16, 29, 5) $$,
$$ insert into campsite_type_cost (campsite_type_id, season_id, cost_per_night, cost_per_adult, cost_per_teenager, cost_per_child) values (16, 29, 5, 6, 7, 8) $$,
'42501', 'new row violates row-level security policy for table "campsite_type_cost"',
'Admin from company 2 should NOT be able to insert new row with a season from company 4.'
);
@ -220,11 +235,29 @@ select bag_eq(
);
select throws_ok(
$$ insert into campsite_type_cost (campsite_type_id, season_id, cost_per_night) values (16, 27, -1) $$,
$$ insert into campsite_type_cost (campsite_type_id, season_id, cost_per_night, cost_per_adult, cost_per_teenager, cost_per_child) values (16, 27, -1, 2, 3, 4) $$,
'23514', 'new row for relation "campsite_type_cost" violates check constraint "cost_not_negative"',
'Should not be able to insert campsite type costs with negative cost per night.'
);
select throws_ok(
$$ insert into campsite_type_cost (campsite_type_id, season_id, cost_per_night, cost_per_adult, cost_per_teenager, cost_per_child) values (16, 27, 1, -1, 3, 4) $$,
'23514', 'new row for relation "campsite_type_cost" violates check constraint "per_adult_not_negative"',
'Should not be able to insert campsite type costs with negative cost per adult.'
);
select throws_ok(
$$ insert into campsite_type_cost (campsite_type_id, season_id, cost_per_night, cost_per_adult, cost_per_teenager, cost_per_child) values (16, 27, 1, 2, -1, 4) $$,
'23514', 'new row for relation "campsite_type_cost" violates check constraint "per_teenager_not_negative"',
'Should not be able to insert campsite type costs with negative cost per teenager.'
);
select throws_ok(
$$ insert into campsite_type_cost (campsite_type_id, season_id, cost_per_night, cost_per_adult, cost_per_teenager, cost_per_child) values (16, 27, 1, 2, 3, -1) $$,
'23514', 'new row for relation "campsite_type_cost" violates check constraint "per_child_not_negative"',
'Should not be able to insert campsite type costs with negative cost per child.'
);
reset role;

View File

@ -9,15 +9,15 @@ select plan(13);
set search_path to camper, public;
select has_function('camper', 'set_campsite_type_cost', array['uuid', 'integer', 'text', 'integer']);
select function_lang_is('camper', 'set_campsite_type_cost', array['uuid', 'integer', 'text', 'integer'], 'sql');
select function_returns('camper', 'set_campsite_type_cost', array['uuid', 'integer', 'text', 'integer'], 'void');
select isnt_definer('camper', 'set_campsite_type_cost', array['uuid', 'integer', 'text', 'integer']);
select volatility_is('camper', 'set_campsite_type_cost', array['uuid', 'integer', 'text', 'integer'], 'volatile');
select function_privs_are('camper', 'set_campsite_type_cost', array ['uuid', 'integer', 'text', 'integer'], 'guest', array[]::text[]);
select function_privs_are('camper', 'set_campsite_type_cost', array ['uuid', 'integer', 'text', 'integer'], 'employee', array[]::text[]);
select function_privs_are('camper', 'set_campsite_type_cost', array ['uuid', 'integer', 'text', 'integer'], 'admin', array['EXECUTE']);
select function_privs_are('camper', 'set_campsite_type_cost', array ['uuid', 'integer', 'text', 'integer'], 'authenticator', array[]::text[]);
select has_function('camper', 'set_campsite_type_cost', array['uuid', 'integer', 'text', 'text', 'text', 'text']);
select function_lang_is('camper', 'set_campsite_type_cost', array['uuid', 'integer', 'text', 'text', 'text', 'text'], 'sql');
select function_returns('camper', 'set_campsite_type_cost', array['uuid', 'integer', 'text', 'text', 'text', 'text'], 'void');
select isnt_definer('camper', 'set_campsite_type_cost', array['uuid', 'integer', 'text', 'text', 'text', 'text']);
select volatility_is('camper', 'set_campsite_type_cost', array['uuid', 'integer', 'text', 'text', 'text', 'text'], 'volatile');
select function_privs_are('camper', 'set_campsite_type_cost', array ['uuid', 'integer', 'text', 'text', 'text', 'text'], 'guest', array[]::text[]);
select function_privs_are('camper', 'set_campsite_type_cost', array ['uuid', 'integer', 'text', 'text', 'text', 'text'], 'employee', array[]::text[]);
select function_privs_are('camper', 'set_campsite_type_cost', array ['uuid', 'integer', 'text', 'text', 'text', 'text'], 'admin', array['EXECUTE']);
select function_privs_are('camper', 'set_campsite_type_cost', array ['uuid', 'integer', 'text', 'text', 'text', 'text'], 'authenticator', array[]::text[]);
set client_min_messages to warning;
truncate campsite_type_cost cascade;
@ -51,31 +51,31 @@ values (4, 1, 'High')
, (6, 1, 'Low')
;
insert into campsite_type_cost (campsite_type_id, season_id, cost_per_night)
values (3, 4, 44)
, (3, 5, 55)
insert into campsite_type_cost (campsite_type_id, season_id, cost_per_night, cost_per_adult, cost_per_teenager, cost_per_child)
values (3, 4, 44, 55, 66, 77)
, (3, 5, 55, 66, 77, 88)
;
select lives_ok(
$$ select set_campsite_type_cost('87452b88-b48f-48d3-bb6c-0296de64164e', 4, '12.34') $$,
$$ select set_campsite_type_cost('87452b88-b48f-48d3-bb6c-0296de64164e', 4, '12.34', '56.78', '90.12', '34.56') $$,
'Should be able to edit the cost for high season'
);
select lives_ok(
$$ select set_campsite_type_cost('87452b88-b48f-48d3-bb6c-0296de64164e', 5, '0.0') $$,
$$ select set_campsite_type_cost('87452b88-b48f-48d3-bb6c-0296de64164e', 5, '0.0', '0.0', '0.0', '0.0') $$,
'Should be able to set the cost for mid season to zero'
);
select lives_ok(
$$ select set_campsite_type_cost('87452b88-b48f-48d3-bb6c-0296de64164e', 6, '3.21') $$,
$$ select set_campsite_type_cost('87452b88-b48f-48d3-bb6c-0296de64164e', 6, '3.21', '0.98', '7.65', '4.32') $$,
'Should be able to set the cost for low season, adding it.'
);
select bag_eq(
$$ select campsite_type_id, season_id, cost_per_night from campsite_type_cost $$,
$$ values (3, 4, 1234)
, (3, 5, 0)
, (3, 6, 321)
$$ select campsite_type_id, season_id, cost_per_night, cost_per_adult, cost_per_teenager, cost_per_child from campsite_type_cost $$,
$$ values (3, 4, 1234, 5678, 9012, 3456)
, (3, 5, 0, 0, 0, 0)
, (3, 6, 321, 98, 765, 432)
$$,
'Should have updated all campsite type costs.'
);

View File

@ -0,0 +1,12 @@
-- Verify camper:campsite_type_cost__per_age on pg
begin;
select cost_per_adult
, cost_per_teenager
, cost_per_child
from camper.campsite_type_cost
where false
;
rollback;

View File

@ -2,6 +2,6 @@
begin;
select has_function_privilege('camper.set_campsite_type_cost(uuid, integer, text, integer)', 'execute');
select has_function_privilege('camper.set_campsite_type_cost(uuid, integer, text, text, text, text)', 'execute');
rollback;

View File

@ -1301,7 +1301,7 @@ input[type="checkbox"] {
border: 2px solid #777;
height: 1.5em !important;
width: 1.5em !important;
margin: 0 10px 0 30px;
margin-right: .625em;
border-radius: 100%;
vertical-align: text-bottom;
position: relative;
@ -1348,11 +1348,46 @@ input[type="checkbox"]:focus {
outline-offset: 2px;
}
#booking {
display: flex;
align-items: start;
gap: 1rem;
}
#booking fieldset {
padding: 0;
}
#booking > fieldset {
flex: 1;
}
#booking > footer {
flex: .33;
position: sticky;
top: 13rem;
opacity: 0;
visibility: hidden;
transition: opacity .5s ease;
}
#booking > footer.is-visible {
opacity: 1;
visibility: visible;
}
#booking br {
display: none;
}
#booking fieldset {
border: none;
}
#booking > fieldset {
display: revert;
}
#booking legend {
font-weight: bold;
}
@ -1363,42 +1398,58 @@ input[type="checkbox"]:focus {
padding: 20px;
}
#booking fieldset {
#booking > fieldset > fieldset {
margin-bottom: 1em;
padding-bottom: 1em;
border-bottom: 3px solid black;
}
#booking fieldset:first-of-type,
#booking fieldset.campsite-options,
#booking fieldset:last-of-type {
#booking > fieldset > fieldset:last-of-type {
border-bottom: none;
}
#booking .accommodation,
#booking .customer-details,
#booking .booking-period {
display: grid;
row-gap: 1em;
column-gap: 1em;
gap: 1em;
}
#booking fieldset.campsite-options:not(.is-visible) {
visibility: hidden;
max-height: 0;
overflow: hidden;
margin: 0;
padding: 0;
border: none;
line-height: 0;
opacity: 0;
#booking .accommodation {
grid-template-columns: repeat(auto-fit, minmax(15em, 1fr));
}
#booking fieldset:first-of-type {
#booking .accommodation label {
white-space: nowrap;
}
#booking .customer-details {
grid-template-columns: repeat(3, 1fr);
}
#booking fieldset.campsite-options {
transition: opacity .25s ease-in;
grid-template-columns: repeat(4, 1fr);
#booking .customer-details .full-row {
grid-column: span 3;
}
#booking fieldset:last-of-type {
border-bottom: none;
#booking .campsite-options {
flex-direction: column;
}
#booking .campsite-options label {
display: grid;
grid-template-columns: 1fr 1fr;
align-items: center;
}
#booking .campsite-options label span a {
display: block;
}
#booking .campsite-options input[type="number"] {
width: 8em;
}
#booking .booking-period {
grid-template-columns: repeat(2, 1fr);
}
@ -1407,10 +1458,58 @@ input[type="checkbox"]:focus {
text-decoration: var(--accent) wavy underline;
}
/*<editor-fold desc="booking cart">*/
#booking .cart {
flex-direction: column;
gap: .5rem;
}
#booking .cart div {
flex: 1;
min-height: unset;
display: flex;
gap: .5rem;
padding: 0;
justify-content: space-between;
}
#booking .cart div:hover {
background: none;
}
#booking .cart dt {
font-size: inherit;
font-weight: inherit;
margin: 0;
padding: 0;
border: none;
background: none;
}
#booking .cart .total {
font-weight: bold;
}
/*</editor-fold>*/
@media (max-width: 48rem) {
#booking fieldset {
#booking {
flex-direction: column;
align-items: stretch;
}
#booking > fieldset > fieldset:not(.accommodation) {
grid-template-columns: 1fr !important;
}
#booking .customer-details .full-row {
grid-column: span 1;
}
#booking > footer {
position: unset;
}
}
/* booking card */

View File

@ -141,19 +141,68 @@
{{ with .Prices }}
<fieldset>
<legend>{{( pgettext "Prices" "title" )}}</legend>
{{ range . }}
<fieldset>
<legend>{{ .SeasonName }}</legend>
{{ with .PricePerNight -}}
<label>
{{( pgettext "Price per night" "input")}}<br>
<input type="number" name="{{ .Name }}" value="{{ .Val }}" min="0" step="0.01"
required {{ template "error-attrs" . }}><br>
{{ template "error-message" . }}
</label>
{{- end }}
</fieldset>
{{- end }}
<table>
<thead>
<tr>
<th scope="col">{{( pgettext "Season" "header" )}}</th>
<th scope="col">{{( pgettext "Price per night" "header" )}}</th>
<th scope="col">{{( pgettext "Price per adult (> 16)" "header" )}}</th>
<th scope="col">{{( pgettext "Price per teenager (1116)" "header" )}}</th>
<th scope="col">{{( pgettext "Price per child (210)" "header" )}}</th>
</tr>
</thead>
<tbody>
{{ range . -}}
<tr>
<th scope="row">{{ .SeasonName }}</th>
{{ with .PricePerNight -}}
<td>
<label>
<span class="sr-only">{{( pgettext "Price per night" "input")}}<br></span>
<input type="number" name="{{ .Name }}" value="{{ .Val }}" min="0"
step="0.01"
required {{ template "error-attrs" . }}><br>
{{ template "error-message" . }}
</label>
</td>
{{- end }}
{{ with .PricePerAdult -}}
<td>
<label>
<span class="sr-only">{{( pgettext "Price per adult (> 16)" "input")}}<br></span>
<input type="number" name="{{ .Name }}" value="{{ .Val }}" min="0"
step="0.01"
required {{ template "error-attrs" . }}><br>
{{ template "error-message" . }}
</label>
</td>
{{- end }}
{{ with .PricePerTeenager -}}
<td>
<label>
<span class="sr-only">{{( pgettext "Price per teenager (1116)" "input")}}<br></span>
<input type="number" name="{{ .Name }}" value="{{ .Val }}" min="0"
step="0.01"
required {{ template "error-attrs" . }}><br>
{{ template "error-message" . }}
</label>
</td>
{{- end }}
{{ with .PricePerChild -}}
<td>
<label>
<span class="sr-only">{{( pgettext "Price per child (210)" "input")}}<br></span>
<input type="number" name="{{ .Name }}" value="{{ .Val }}" min="0"
step="0.01"
required {{ template "error-attrs" . }}><br>
{{ template "error-message" . }}
</label>
</td>
{{- end }}
</tr>
{{- end }}
</tbody>
</table>
</fieldset>
{{- end }}
{{ with .Spiel -}}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,245 @@
<!--
SPDX-FileCopyrightText: 2023 jordi fita mas <jordi@tandem.blog>
SPDX-FileCopyrightText: 2023 Oriol Carbonell <info@oriolcarbonell.cat>
SPDX-License-Identifier: AGPL-3.0-only
-->
{{ define "title" -}}
{{( pgettext "Booking" "title" )}}
{{- end }}
{{ define "head" -}}
{{ template "alpineScript" }}
{{- end }}
{{ define "content" -}}
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/booking.publicPage*/ -}}
<h2>{{( pgettext "Booking" "title" )}}</h2>
{{ with .Form -}}
<form id="booking" action="/{{ currentLocale }}/booking" method="post"
x-data="{campsiteType: '', arrivalDate: '', departureDate: '', hasCampsite() { return this.campsiteType !== '' }, hasDates() { return this.arrivalDate !== '' && this.departureDate !== ''} }"
x-init="campsiteType = (document.querySelector('[x-model=campsiteType]:checked') || {}).value || ''"
>
<fieldset
data-hx-get="/{{ currentLocale }}/booking/cart"
data-hx-include="#booking"
data-hx-trigger="load,change"
data-hx-target="#booking footer"
>
<fieldset class="accommodation">
<legend>{{( pgettext "Accommodation" "title" )}}</legend>
{{ range .CampsiteType.Options -}}
<label><input type="radio" name="{{ $.Form.CampsiteType.Name }}" value="{{ .Value }}"
x-model="campsiteType"
{{ if $.Form.CampsiteType.IsSelected .Value }}checked{{ end }}
> {{ .Label }}</label><br>
{{- end }}
{{ template "error-message" .CampsiteType }}
</fieldset>
<fieldset class="booking-period"
x-cloak
x-show="hasCampsite()"
x-transition.duration.250ms
>
<legend>{{( pgettext "Booking Period" "title" )}}</legend>
{{ with .ArrivalDate -}}
<label>
{{( pgettext "Arrival date" "input" )}}<br>
<input type="date" required
min="{{ today }}"
name="{{ .Name }}" value="{{ .Val }}" {{ template "error-attrs" . }}
x-model.fill="arrivalDate"
><br>
</label>
{{ template "error-message" . }}
{{- end }}
{{ with .DepartureDate -}}
<label>
{{( pgettext "Departure date" "input" )}}<br>
<input type="date" required
min="{{ tomorrow }}"
name="{{ .Name }}" value="{{ .Val }}" {{ template "error-attrs" . }}
x-model.fill="departureDate"
><br>
</label>
{{ template "error-message" . }}
{{- end }}
</fieldset>
<fieldset class="guests campsite-options"
x-cloak
x-show="hasCampsite() && hasDates()"
x-transition.duration.250ms
>
<legend>{{( pgettext "Guests" "title" )}}</legend>
{{ with .NumberAdults -}}
<label>
{{( pgettext "Adults aged 17 or older" "input" )}}<br>
<input type="number" required
name="{{ .Name }}" value="{{ .Val }}" min="1"
{{ template "error-attrs" . }}
><br>
</label>
{{ template "error-message" . }}
{{- end }}
{{ with .NumberTeenagers -}}
<label>
{{( pgettext "Teenagers from 11 to 16 years old" "input" )}}<br>
<input type="number" required
name="{{ .Name }}" value="{{ .Val }}" min="0"
{{ template "error-attrs" . }}
><br>
</label>
{{ template "error-message" . }}
{{- end }}
{{ with .NumberChildren -}}
<label>
{{( pgettext "Children from 2 to 10 years old" "input" )}}<br>
<input type="number" required
name="{{ .Name }}" value="{{ .Val }}" min="0"
{{ template "error-attrs" . }}
><br>
</label>
{{ template "error-message" . }}
{{- end }}
{{ with .NumberDogs -}}
<label>
{{( pgettext "Dogs" "input" )}}<br>
<input type="number" required
name="{{ .Name }}" value="{{ .Val }}" min="0"
{{ template "error-attrs" . }}
><br>
</label>
{{ template "error-message" . }}
{{- end }}
</fieldset>
{{ range $campsiteType := .CampsiteType.Options -}}
{{ $options := index $.Form.CampsiteTypeOptions .Value }}
{{ $zonePreferences := index $.Form.ZonePreferences .Value }}
{{ if or $options $zonePreferences }}
<fieldset class="campsite-options"
x-cloak
x-show="campsiteType === '{{ $campsiteType.Value }}' && hasDates()"
x-transition.duration.250ms>
<legend>{{ .Label }}</legend>
{{ with $zonePreferences -}}
<label>
<span>
{{( pgettext "Area preferences (optional)" "input" )}}
<a href="/{{ currentLocale }}/campground?zones"
target="_blank">{{( gettext "Campground map" )}}</a>
</span><br>
<input type="text"
name="{{ .Name }}" value="{{ .Val }}" {{ template "error-attrs" . }}
><br>
</label>
{{ template "error-message" . }}
{{- end }}
{{ range $options -}}
<label>
{{ .Label }}<br>
<input type="number" required
name="{{ .Input.Name }}" value="{{ .Input.Val }}"
min="{{ .Min }}" max="{{ .Max }}"
{{ template "error-attrs" .Input }}
><br>
</label>
{{ template "error-message" .Input }}
{{- end }}
</fieldset>
{{- end }}
{{- end }}
<fieldset class="customer-details"
x-cloak
x-show="hasCampsite() && hasDates()"
x-transition.duration.250ms
>
<legend>{{( pgettext "Customer Details" "title" )}}</legend>
{{ with .FullName -}}
<label>
{{( pgettext "Full name" "input" )}}<br>
<input type="text" required autocomplete="name" minlength="2"
name="{{ .Name }}" value="{{ .Val }}" {{ template "error-attrs" . }}
><br>
</label>
{{ template "error-message" . }}
{{- end }}
{{ with .Address -}}
<label>
{{( pgettext "Address (optional)" "input" )}}<br>
<input type="text" autocomplete="billing street-address"
name="{{ .Name }}" value="{{ .Val }}" {{ template "error-attrs" . }}
><br>
</label>
{{ template "error-message" . }}
{{- end }}
{{ with .PostalCode -}}
<label>
{{( pgettext "Postcode (optional)" "input" )}}<br>
<input type="text" autocomplete="billing postal-code"
name="{{ .Name }}" value="{{ .Val }}" {{ template "error-attrs" . }}
><br>
</label>
{{ template "error-message" . }}
{{- end }}
{{ with .City -}}
<label>
{{( pgettext "Town or village (optional)" "input" )}}<br>
<input type="text"
name="{{ .Name }}" value="{{ .Val }}" {{ template "error-attrs" . }}
><br>
</label>
{{ template "error-message" . }}
{{- end }}
{{ with .Country -}}
<label>
{{( pgettext "Country" "input" )}}<br>
<select name="{{ .Name }}"
required autocomplete="country">
<option>{{( gettext "Choose a country" )}}</option>
{{ template "error-attrs" . }}>{{ template "list-options" . }}
</select><br>
</label>
{{ template "error-message" . }}
{{- end }}
{{ with .Email -}}
<label>
{{( pgettext "Email" "input" )}}<br>
<input type="email" required autocomplete="email"
name="{{ .Name }}" value="{{ .Val }}" {{ template "error-attrs" . }}
><br>
</label>
{{ template "error-message" . }}
{{- end }}
{{ with .Phone -}}
<label>
{{( pgettext "Phone" "input" )}}<br>
<input type="tel" required autocomplete="tel"
name="{{ .Name }}" value="{{ .Val }}" {{ template "error-attrs" . }}
><br>
</label>
{{ template "error-message" . }}
{{- end }}
{{ with .ACSICard -}}
<label class="full-row">
<input type="checkbox" name="{{ .Name }}" {{ if .Checked}}checked{{ end }}
{{ template "error-attrs" . }}
> {{( pgettext "ACSI card? (optional)" "input" )}}</label><br>
{{ template "error-message" . }}
{{- end }}
{{ with .Agreement -}}
<label class="full-row">
<input type="checkbox" required name="{{ .Name }}" {{ if .Checked}}checked{{ end }}
{{ template "error-attrs" . }}
> {{ printf ( pgettext "I have read and I accept %[1]sthe reservation conditions%[2]s" "input" ) (printf "<a href=\"/%s/legal/reservation\" rel=\"terms-of-service\" target=\"_blank\">" currentLocale) (print "</a>") | raw }}
</label><br>
{{ template "error-message" . }}
{{- end }}
</fieldset>
</fieldset>
<footer :class="hasCampsite() && hasDates() && 'is-visible'">
{{ template "cart.gohtml" $.Cart }}
</footer>
</form>
{{- end }}
<script src="/static/booking-dates.js"></script>
{{- end }}

View File

@ -82,15 +82,14 @@
</svg>
{{ .SeasonName }}
</dt>
<dd x-show="open">
{{- if .Options -}}
{{ printf (gettext "%s: %s €/night") $.Name .PricePerNight }}
{{- else -}}
{{ printf (gettext "%s €/night") .PricePerNight }}
{{- end -}}
</dd>
{{ range .Options }}
<dd x-show="open">{{ printf (gettext "%s: %s €/night") .OptionName .PricePerNight }}</dd>
<dd x-show="open">
{{- if .OptionName -}}
{{ printf (gettext "%s: %s/night") .OptionName (formatPrice .PricePerNight) }}
{{- else -}}
{{ printf (gettext "%s/night") (formatPrice .PricePerNight) }}
{{- end -}}
</dd>
{{- end }}
{{ if gt $.MinNights 1 -}}
<dd x-show="open">{{ printf (gettext "*Minimum %d nights per stay") $.MinNights }}</dd>