From 2c36e45663e484464f9ff98793a120165fea9b79 Mon Sep 17 00:00:00 2001 From: jordi fita mas Date: Sun, 4 Feb 2024 06:37:25 +0100 Subject: [PATCH] =?UTF-8?q?Compute=20and=20show=20the=20=E2=80=9Ccart?= =?UTF-8?q?=E2=80=9D=20for=20the=20booking=20form?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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
s progressively when visitors select options. --- demo/demo.sql | 172 +++--- deploy/campsite_type_cost__per_age.sql | 18 + deploy/set_campsite_type_cost.sql | 17 +- pkg/booking/cart.go | 201 +++++++ pkg/booking/public.go | 72 ++- pkg/campsite/types/admin.go | 50 +- pkg/campsite/types/public.go | 64 +- pkg/database/funcs.go | 4 +- po/ca.po | 556 +++++++++++------- po/es.po | 556 +++++++++++------- po/fr.po | 514 +++++++++++----- revert/campsite_type_cost__per_age.sql | 11 + revert/set_campsite_type_cost.sql | 2 +- sqitch.plan | 3 +- test/campsite_type_cost.sql | 51 +- test/set_campsite_type_cost.sql | 38 +- verify/campsite_type_cost__per_age.sql | 12 + verify/set_campsite_type_cost.sql | 2 +- web/static/public.css | 145 ++++- web/templates/admin/campsite/type/form.gohtml | 75 ++- web/templates/public/booking.gohtml | 203 ------- web/templates/public/booking/cart.gohtml | 25 + web/templates/public/booking/form.gohtml | 245 ++++++++ web/templates/public/campsite/type.gohtml | 15 +- 24 files changed, 2087 insertions(+), 964 deletions(-) create mode 100644 deploy/campsite_type_cost__per_age.sql create mode 100644 pkg/booking/cart.go create mode 100644 revert/campsite_type_cost__per_age.sql create mode 100644 verify/campsite_type_cost__per_age.sql delete mode 100644 web/templates/public/booking.gohtml create mode 100644 web/templates/public/booking/cart.gohtml create mode 100644 web/templates/public/booking/form.gohtml diff --git a/demo/demo.sql b/demo/demo.sql index a401164..d8ccf4f 100644 --- a/demo/demo.sql +++ b/demo/demo.sql @@ -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') diff --git a/deploy/campsite_type_cost__per_age.sql b/deploy/campsite_type_cost__per_age.sql new file mode 100644 index 0000000..f179c56 --- /dev/null +++ b/deploy/campsite_type_cost__per_age.sql @@ -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; diff --git a/deploy/set_campsite_type_cost.sql b/deploy/set_campsite_type_cost.sql index 5a53c7b..2c663ed 100644 --- a/deploy/set_campsite_type_cost.sql +++ b/deploy/set_campsite_type_cost.sql @@ -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; diff --git a/pkg/booking/cart.go b/pkg/booking/cart.go new file mode 100644 index 0000000..749ab4c --- /dev/null +++ b/pkg/booking/cart.go @@ -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 +} diff --git a/pkg/booking/public.go b/pkg/booking/public.go index 70f046f..411c4e3 100644 --- a/pkg/booking/public.go +++ b/pkg/booking/public.go @@ -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 { diff --git a/pkg/campsite/types/admin.go b/pkg/campsite/types/admin.go index 1baf919..96eae08 100644 --- a/pkg/campsite/types/admin.go +++ b/pkg/campsite/types/admin.go @@ -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 } diff --git a/pkg/campsite/types/public.go b/pkg/campsite/types/public.go index 78b122b..2cf4fc0 100644 --- a/pkg/campsite/types/public.go +++ b/pkg/campsite/types/public.go @@ -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), diff --git a/pkg/database/funcs.go b/pkg/database/funcs.go index 913a253..957afb6 100644 --- a/pkg/database/funcs.go +++ b/pkg/database/funcs.go @@ -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 } diff --git a/po/ca.po b/po/ca.po index 982f8d9..94c927d 100644 --- a/po/ca.po +++ b/po/ca.po @@ -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 \n" "Language-Team: Catalan \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 d’arribada" - -#: 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 "RTC #%s" msgstr "Núm. RTC %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 d’arribada" + +#: 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 d’entre 11 i 16 anys" + +#: web/templates/public/booking/form.gohtml:96 +msgctxt "input" +msgid "Children from 2 to 10 years old" +msgstr "Nens d’entre 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 (11–16)" +msgstr "Preu per adolescent (11–16)" + +#: web/templates/admin/campsite/type/form.gohtml:151 +msgctxt "header" +msgid "Price per child (2–10)" +msgstr "Preu per nen (2–10)" + +#: 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 (11–16)" +msgstr "Preu per adolescent (11–16)" + +#: web/templates/admin/campsite/type/form.gohtml:194 +msgctxt "input" +msgid "Price per child (2–10)" +msgstr "Preu per nene (2–10)" + +#: 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 s’ha 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 d’allotjament" -#: pkg/campsite/types/admin.go:531 +#: pkg/campsite/types/admin.go:558 msgid "Check-in can not be empty." msgstr "No podeu deixar l’entrada 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 d’allotjament escollit no és vàlid." @@ -2199,7 +2320,7 @@ msgstr "No podeu deixar l’adreça de l’enllaç 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 d’arribada en blanc." -#: pkg/booking/public.go:323 +#: pkg/booking/public.go:362 msgid "Arrival date must be a valid date." msgstr "La data d’arribada 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 d’arribada." -#: 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 d’adults en blanc." + +#: pkg/booking/public.go:373 +msgid "Number of adults must be an integer." +msgstr "El número d’adults ha de ser enter." + +#: pkg/booking/public.go:374 +msgid "There must be at least one adult." +msgstr "Hi ha d’haver 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 d’adolescents en blanc." + +#: pkg/booking/public.go:378 +msgid "Number of teenagers must be an integer." +msgstr "El número d’adolescents ha de ser enter." + +#: pkg/booking/public.go:379 +msgid "Number of teenagers can not be negative." +msgstr "El número d’adolescents 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 d’adults en blanc." - -#~ msgid "Number of adults must be an integer." -#~ msgstr "El número d’adults ha de ser enter." - #~ msgid "Number of adults must be one or greater." #~ msgstr "El número d’adults no pot ser zero." -#~ msgid "Number of teenagers can not be empty" -#~ msgstr "No podeu deixar el número d’adolescents en blanc." - -#~ msgid "Number of teenagers must be an integer." -#~ msgstr "El número d’adolescents ha de ser enter." - #~ msgid "Number of teenagers must be zero or greater." #~ msgstr "El número d’adolescents 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." diff --git a/po/es.po b/po/es.po index 282a0e0..490e14a 100644 --- a/po/es.po +++ b/po/es.po @@ -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 \n" "Language-Team: Spanish \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 "RTC #%s" msgstr " RTC %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 (11–16)" +msgstr "Precio por adolescente (11–16)" + +#: web/templates/admin/campsite/type/form.gohtml:151 +msgctxt "header" +msgid "Price per child (2–10)" +msgstr "Precio por niño (2–10)" + +#: 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 (11–16)" +msgstr "Precio por adolescente (11–16)" + +#: web/templates/admin/campsite/type/form.gohtml:194 +msgctxt "input" +msgid "Price per child (2–10)" +msgstr "Precio por niño (2–10)" + +#: 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." diff --git a/po/fr.po b/po/fr.po index f264133..e4ba7be 100644 --- a/po/fr.po +++ b/po/fr.po @@ -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 \n" "Language-Team: French \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 d’arrivé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 "J’ai lu et j’accepte %[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 "RTC #%s" msgstr "# RTC %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 d’arrivé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 "J’ai lu et j’accepte %[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 (11–16)" +msgstr "Prix per adolescent (11–16)" + +#: web/templates/admin/campsite/type/form.gohtml:151 +msgctxt "header" +msgid "Price per child (2–10)" +msgstr "Prix per enfant (2–10)" + +#: 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 (11–16)" +msgstr "Prix per adolescent (11–16)" + +#: web/templates/admin/campsite/type/form.gohtml:194 +msgctxt "input" +msgid "Price per child (2–10)" +msgstr "Prix per enfant (2–10)" + +#: 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 "L’image de la diapositive doit être de type média d’image." #: 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 "L’e-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 n’est 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 "L’icône sélectionnée n’est 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 "L’arrivé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 "L’image 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 "L’image de couverture doit être de type média d’image." -#: 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 d’emplacement sélectionné n’est pas valide." @@ -2200,7 +2321,7 @@ msgstr "L’addresse 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 n’est 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é n’est 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 n’est 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 n’est 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 n’est 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 "L’intégration sélectionnée n’est pas valide." msgid "The merchant key is not valid." msgstr "La clé marchand n’est 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 d’arrivé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 d’arrivé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 d’arrivée." -#: pkg/booking/public.go:332 +#: pkg/booking/public.go:372 +msgid "Number of adults can not be empty" +msgstr "Le nombre d’adultes ne peut pas être vide." + +#: pkg/booking/public.go:373 +msgid "Number of adults must be an integer." +msgstr "Le nombre d’adultes 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 d’adolescents ne peut pas être vide." + +#: pkg/booking/public.go:378 +msgid "Number of teenagers must be an integer." +msgstr "Le nombre d’adolescents doit être un entier." + +#: pkg/booking/public.go:379 +msgid "Number of teenagers can not be negative." +msgstr "Le nombre d’adolescents ne peut pas être négatif." + +#: pkg/booking/public.go:382 +msgid "Number of children can not be empty" +msgstr "Le nombre d’enfants ne peut pas être vide." + +#: pkg/booking/public.go:383 +msgid "Number of children must be an integer." +msgstr "Le nombre d’enfants doit être un entier." + +#: pkg/booking/public.go:384 +msgid "Number of children can not be negative." +msgstr "Le nombre d’enfants 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 d’accepter 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." diff --git a/revert/campsite_type_cost__per_age.sql b/revert/campsite_type_cost__per_age.sql new file mode 100644 index 0000000..8c6b5ff --- /dev/null +++ b/revert/campsite_type_cost__per_age.sql @@ -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; diff --git a/revert/set_campsite_type_cost.sql b/revert/set_campsite_type_cost.sql index feb16b2..0a830ee 100644 --- a/revert/set_campsite_type_cost.sql +++ b/revert/set_campsite_type_cost.sql @@ -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 $$ diff --git a/sqitch.plan b/sqitch.plan index ee8a8f0..98c861b 100644 --- a/sqitch.plan +++ b/sqitch.plan @@ -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 # 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 # 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 # 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 # 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 # 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 # 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 # Remove min_nights field from campsite_type_cost relation diff --git a/test/campsite_type_cost.sql b/test/campsite_type_cost.sql index 72a53ac..87b1b10 100644 --- a/test/campsite_type_cost.sql +++ b/test/campsite_type_cost.sql @@ -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; diff --git a/test/set_campsite_type_cost.sql b/test/set_campsite_type_cost.sql index a9f0bd8..5de7120 100644 --- a/test/set_campsite_type_cost.sql +++ b/test/set_campsite_type_cost.sql @@ -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.' ); diff --git a/verify/campsite_type_cost__per_age.sql b/verify/campsite_type_cost__per_age.sql new file mode 100644 index 0000000..872fad8 --- /dev/null +++ b/verify/campsite_type_cost__per_age.sql @@ -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; diff --git a/verify/set_campsite_type_cost.sql b/verify/set_campsite_type_cost.sql index f89f497..4270f56 100644 --- a/verify/set_campsite_type_cost.sql +++ b/verify/set_campsite_type_cost.sql @@ -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; diff --git a/web/static/public.css b/web/static/public.css index 49761ca..132a555 100644 --- a/web/static/public.css +++ b/web/static/public.css @@ -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; } +/**/ + +#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; +} + +/**/ + @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 */ diff --git a/web/templates/admin/campsite/type/form.gohtml b/web/templates/admin/campsite/type/form.gohtml index d7e3a46..c036f39 100644 --- a/web/templates/admin/campsite/type/form.gohtml +++ b/web/templates/admin/campsite/type/form.gohtml @@ -141,19 +141,68 @@ {{ with .Prices }}
{{( pgettext "Prices" "title" )}} - {{ range . }} -
- {{ .SeasonName }} - {{ with .PricePerNight -}} - - {{- end }} -
- {{- end }} + + + + + + + + + + + + {{ range . -}} + + + {{ with .PricePerNight -}} + + {{- end }} + {{ with .PricePerAdult -}} + + {{- end }} + {{ with .PricePerTeenager -}} + + {{- end }} + {{ with .PricePerChild -}} + + {{- end }} + + {{- end }} + +
{{( pgettext "Season" "header" )}}{{( pgettext "Price per night" "header" )}}{{( pgettext "Price per adult (> 16)" "header" )}}{{( pgettext "Price per teenager (11–16)" "header" )}}{{( pgettext "Price per child (2–10)" "header" )}}
{{ .SeasonName }} + + + + + + + +
{{- end }} {{ with .Spiel -}} diff --git a/web/templates/public/booking.gohtml b/web/templates/public/booking.gohtml deleted file mode 100644 index 9a123f5..0000000 --- a/web/templates/public/booking.gohtml +++ /dev/null @@ -1,203 +0,0 @@ - -{{ define "title" -}} - {{( pgettext "Booking" "title" )}} -{{- end }} - -{{ define "content" -}} - {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/booking.publicPage*/ -}} -

{{( pgettext "Booking" "title" )}}

- {{ with .Form -}} -
-
- {{( pgettext "Customer Details" "title" )}} - {{ with .FullName -}} - - {{ template "error-message" . }} - {{- end }} - {{ with .Address -}} - - {{ template "error-message" . }} - {{- end }} - {{ with .PostalCode -}} - - {{ template "error-message" . }} - {{- end }} - {{ with .City -}} - - {{ template "error-message" . }} - {{- end }} - {{ with .Country -}} - - {{ template "error-message" . }} - {{- end }} - {{ with .Email -}} - - {{ template "error-message" . }} - {{- end }} - {{ with .Phone -}} - - {{ template "error-message" . }} - {{- end }} -
-
- {{( pgettext "Accommodation" "title" )}} - {{ range .CampsiteType.Options -}} -
- {{- end }} - {{ template "error-message" .CampsiteType }} -
- {{ range $campsiteType := .CampsiteType.Options -}} - {{ $options := index $.Form.CampsiteTypeOptions .Value }} - {{ $zonePreferences := index $.Form.ZonePreferences .Value }} - {{ if or $options $zonePreferences }} -
- {{ .Label }} - {{ with $zonePreferences -}} -
- - {{ template "error-message" . }} - {{( gettext "Campground map" )}}
-
- {{- end }} - {{ range $options -}} - - {{ template "error-message" .Input }} - {{- end }} -
- {{- end }} - {{- end }} -
- {{( pgettext "Booking Period" "title" )}} - {{ with .ArrivalDate -}} - - {{ template "error-message" . }} - {{- end }} - {{ with .DepartureDate -}} - - {{ template "error-message" . }} - {{- end }} - {{ with .ACSICard -}} -
- {{ template "error-message" . }} - {{- end }} - {{ with .Agreement -}} -
- {{ template "error-message" . }} - {{- end }} -
-
-
- - VISA - MasterCard - Maestro - -
- -
-
- {{- end }} - - - -{{- end }} diff --git a/web/templates/public/booking/cart.gohtml b/web/templates/public/booking/cart.gohtml new file mode 100644 index 0000000..9dc7b17 --- /dev/null +++ b/web/templates/public/booking/cart.gohtml @@ -0,0 +1,25 @@ + +{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/booking.bookingCart */ -}} +
+ {{ range .Lines -}} +
+
{{ .Units}} x {{( pgettext .Concept "cart" )}}
+
{{ formatPrice .Subtotal }}
+
+ {{- end }} +
+
{{( pgettext "Total" "cart" )}}
+
{{ formatPrice .Total }}
+
+
+
+ + VISA + MasterCard + Maestro + +
+ diff --git a/web/templates/public/booking/form.gohtml b/web/templates/public/booking/form.gohtml new file mode 100644 index 0000000..c5360d7 --- /dev/null +++ b/web/templates/public/booking/form.gohtml @@ -0,0 +1,245 @@ + +{{ define "title" -}} + {{( pgettext "Booking" "title" )}} +{{- end }} + +{{ define "head" -}} + {{ template "alpineScript" }} +{{- end }} + +{{ define "content" -}} + {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/booking.publicPage*/ -}} +

{{( pgettext "Booking" "title" )}}

+ {{ with .Form -}} +
+
+
+ {{( pgettext "Accommodation" "title" )}} + {{ range .CampsiteType.Options -}} +
+ {{- end }} + {{ template "error-message" .CampsiteType }} +
+
+ {{( pgettext "Booking Period" "title" )}} + {{ with .ArrivalDate -}} + + {{ template "error-message" . }} + {{- end }} + {{ with .DepartureDate -}} + + {{ template "error-message" . }} + {{- end }} +
+
+ {{( pgettext "Guests" "title" )}} + + {{ with .NumberAdults -}} + + {{ template "error-message" . }} + {{- end }} + {{ with .NumberTeenagers -}} + + {{ template "error-message" . }} + {{- end }} + {{ with .NumberChildren -}} + + {{ template "error-message" . }} + {{- end }} + {{ with .NumberDogs -}} + + {{ template "error-message" . }} + {{- end }} +
+ {{ range $campsiteType := .CampsiteType.Options -}} + {{ $options := index $.Form.CampsiteTypeOptions .Value }} + {{ $zonePreferences := index $.Form.ZonePreferences .Value }} + {{ if or $options $zonePreferences }} +
+ {{ .Label }} + {{ with $zonePreferences -}} + + {{ template "error-message" . }} + {{- end }} + {{ range $options -}} + + {{ template "error-message" .Input }} + {{- end }} +
+ {{- end }} + {{- end }} +
+ {{( pgettext "Customer Details" "title" )}} + {{ with .FullName -}} + + {{ template "error-message" . }} + {{- end }} + {{ with .Address -}} + + {{ template "error-message" . }} + {{- end }} + {{ with .PostalCode -}} + + {{ template "error-message" . }} + {{- end }} + {{ with .City -}} + + {{ template "error-message" . }} + {{- end }} + {{ with .Country -}} + + {{ template "error-message" . }} + {{- end }} + {{ with .Email -}} + + {{ template "error-message" . }} + {{- end }} + {{ with .Phone -}} + + {{ template "error-message" . }} + {{- end }} + {{ with .ACSICard -}} +
+ {{ template "error-message" . }} + {{- end }} + {{ with .Agreement -}} +
+ {{ template "error-message" . }} + {{- end }} +
+
+
+ {{ template "cart.gohtml" $.Cart }} +
+
+ {{- end }} + +{{- end }} diff --git a/web/templates/public/campsite/type.gohtml b/web/templates/public/campsite/type.gohtml index 6753eff..27da167 100644 --- a/web/templates/public/campsite/type.gohtml +++ b/web/templates/public/campsite/type.gohtml @@ -82,15 +82,14 @@ {{ .SeasonName }} -
- {{- if .Options -}} - {{ printf (gettext "%s: %s €/night") $.Name .PricePerNight }} - {{- else -}} - {{ printf (gettext "%s €/night") .PricePerNight }} - {{- end -}} -
{{ range .Options }} -
{{ printf (gettext "%s: %s €/night") .OptionName .PricePerNight }}
+
+ {{- if .OptionName -}} + {{ printf (gettext "%s: %s/night") .OptionName (formatPrice .PricePerNight) }} + {{- else -}} + {{ printf (gettext "%s/night") (formatPrice .PricePerNight) }} + {{- end -}} +
{{- end }} {{ if gt $.MinNights 1 -}}
{{ printf (gettext "*Minimum %d nights per stay") $.MinNights }}