Add campsite type options, mainly for plots
This commit is contained in:
parent
0611e95ea1
commit
9293a341ef
|
@ -275,9 +275,9 @@ select set_season_range(93, '[2023-09-29, 2023-09-30]');
|
|||
select set_season_range(94, '[2023-10-01, 2023-10-12]');
|
||||
|
||||
insert into campsite_type_cost (campsite_type_id, season_id, cost_per_night, min_nights)
|
||||
values (72, 92, 20000, 2)
|
||||
, (72, 93, 16500, 2)
|
||||
, (72, 94, 12500, 2)
|
||||
values (72, 92, 20000, 1)
|
||||
, (72, 93, 16500, 1)
|
||||
, (72, 94, 12500, 1)
|
||||
, (73, 92, 20000, 2)
|
||||
, (73, 93, 16500, 2)
|
||||
, (73, 94, 12500, 2)
|
||||
|
@ -289,5 +289,74 @@ values (72, 92, 20000, 2)
|
|||
, (75, 94, 12500, 2)
|
||||
;
|
||||
|
||||
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]')
|
||||
, (72, 'Tenda gran', '[0, 3]')
|
||||
, (72, 'Caravana', '[0, 3]')
|
||||
, (72, 'Autocaravana', '[0, 3]')
|
||||
, (72, 'Furgoneta', '[0, 3]')
|
||||
, (72, 'Cotxe', '[0, 3]')
|
||||
, (72, 'Moto', '[0, 3]')
|
||||
, (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')
|
||||
;
|
||||
|
||||
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)
|
||||
;
|
||||
|
||||
commit;
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
-- Deploy camper:add_campsite_type_option to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: campsite_type_option
|
||||
-- requires: campsite_type
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace function add_campsite_type_option(type_slug uuid, name text, min integer, max integer) returns integer as
|
||||
$$
|
||||
insert into campsite_type_option (campsite_type_id, name, range)
|
||||
select campsite_type_id, add_campsite_type_option.name, int4range(min, max, '[]')
|
||||
from campsite_type
|
||||
where slug = type_slug
|
||||
returning campsite_type_option_id
|
||||
;
|
||||
$$
|
||||
language sql
|
||||
;
|
||||
|
||||
revoke execute on function add_campsite_type_option(uuid, text, integer, integer) from public;
|
||||
grant execute on function add_campsite_type_option(uuid, text, integer, integer) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,54 @@
|
|||
-- Deploy camper:campsite_type_option to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: campsite_type
|
||||
-- requires: user_profile
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table campsite_type_option (
|
||||
campsite_type_option_id integer generated by default as identity primary key,
|
||||
campsite_type_id integer not null references campsite_type,
|
||||
name text not null constraint name_not_empty check(length(trim(name)) > 0),
|
||||
range int4range not null constraint range_not_negative check(lower(range) >= 0)
|
||||
);
|
||||
|
||||
alter table campsite_type_option enable row level security;
|
||||
|
||||
grant select on table campsite_type_option to guest;
|
||||
grant select on table campsite_type_option to employee;
|
||||
grant select, insert, update, delete on table campsite_type_option to admin;
|
||||
|
||||
create policy guest_ok
|
||||
on campsite_type_option
|
||||
for select
|
||||
using (true)
|
||||
;
|
||||
|
||||
create policy insert_to_company
|
||||
on campsite_type_option
|
||||
for insert
|
||||
with check (
|
||||
exists (select 1 from campsite_type join user_profile using (company_id) where campsite_type.campsite_type_id = campsite_type_option.campsite_type_id)
|
||||
)
|
||||
;
|
||||
|
||||
create policy update_company
|
||||
on campsite_type_option
|
||||
for update
|
||||
using (
|
||||
exists (select 1 from campsite_type join user_profile using (company_id) where campsite_type.campsite_type_id = campsite_type_option.campsite_type_id)
|
||||
)
|
||||
;
|
||||
|
||||
create policy delete_from_company
|
||||
on campsite_type_option
|
||||
for delete
|
||||
using (
|
||||
exists (select 1 from campsite_type join user_profile using (company_id) where campsite_type.campsite_type_id = campsite_type_option.campsite_type_id)
|
||||
)
|
||||
;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,56 @@
|
|||
-- Deploy camper:campsite_type_option_cost to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: campsite_type
|
||||
-- requires: season
|
||||
-- requires: campsite_type_option
|
||||
-- requires: user_profile
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table campsite_type_option_cost (
|
||||
campsite_type_option_id integer not null references campsite_type_option,
|
||||
season_id integer not null references season,
|
||||
cost_per_night integer not null constraint cost_not_negative check(cost_per_night >= 0),
|
||||
primary key (campsite_type_option_id, season_id)
|
||||
);
|
||||
|
||||
grant select on table campsite_type_option_cost to guest;
|
||||
grant select on table campsite_type_option_cost to employee;
|
||||
grant select, insert, update, delete on table campsite_type_option_cost to admin;
|
||||
|
||||
alter table campsite_type_option_cost enable row level security;
|
||||
|
||||
create policy guest_ok
|
||||
on campsite_type_option_cost
|
||||
for select
|
||||
using (true)
|
||||
;
|
||||
|
||||
create policy insert_to_company
|
||||
on campsite_type_option_cost
|
||||
for insert
|
||||
with check (
|
||||
exists (select 1 from campsite_type_option join campsite_type using (campsite_type_id) join season using (company_id) join user_profile using (company_id) where campsite_type_option.campsite_type_option_id = campsite_type_option_cost.campsite_type_option_id and season.season_id = campsite_type_option_cost.season_id)
|
||||
)
|
||||
;
|
||||
|
||||
create policy update_company
|
||||
on campsite_type_option_cost
|
||||
for update
|
||||
using (
|
||||
exists (select 1 from campsite_type_option join campsite_type using (campsite_type_id) join season using (company_id) join user_profile using (company_id) where campsite_type_option.campsite_type_option_id = campsite_type_option_cost.campsite_type_option_id and season.season_id = campsite_type_option_cost.season_id)
|
||||
)
|
||||
;
|
||||
|
||||
create policy delete_from_company
|
||||
on campsite_type_option_cost
|
||||
for delete
|
||||
using (
|
||||
exists (select 1 from campsite_type_option join campsite_type using (campsite_type_id) join season using (company_id) join user_profile using (company_id) where campsite_type_option.campsite_type_option_id = campsite_type_option_cost.campsite_type_option_id and season.season_id = campsite_type_option_cost.season_id)
|
||||
)
|
||||
;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,22 @@
|
|||
-- Deploy camper:campsite_type_option_i18n to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: campsite_type_option
|
||||
-- requires: language
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table campsite_type_option_i18n (
|
||||
campsite_type_option_id integer not null references campsite_type_option,
|
||||
lang_tag text not null references language,
|
||||
name text not null,
|
||||
primary key (campsite_type_option_id, lang_tag)
|
||||
);
|
||||
|
||||
grant select on table campsite_type_option_i18n to guest;
|
||||
grant select on table campsite_type_option_i18n to employee;
|
||||
grant select, insert, update, delete on table campsite_type_option_i18n to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,25 @@
|
|||
-- Deploy camper:edit_campsite_type_option to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: campsite_type_option
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace function edit_campsite_type_option(option_id integer, name text, min integer, max integer) returns integer as
|
||||
$$
|
||||
update campsite_type_option
|
||||
set name = edit_campsite_type_option.name
|
||||
, range = int4range(min, max, '[]')
|
||||
where campsite_type_option_id = option_id
|
||||
returning campsite_type_option_id
|
||||
;
|
||||
$$
|
||||
language sql
|
||||
;
|
||||
|
||||
revoke execute on function edit_campsite_type_option(integer, text, integer, integer) from public;
|
||||
grant execute on function edit_campsite_type_option(integer, text, integer, integer) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,25 @@
|
|||
-- Deploy camper:set_campsite_type_option_cost to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: campsite_type_option_cost
|
||||
-- requires: parse_price
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace function set_campsite_type_option_cost(option_id integer, season_id integer, cost_per_night text, decimal_places integer default 2) returns void as
|
||||
$$
|
||||
insert into campsite_type_option_cost (campsite_type_option_id, season_id, cost_per_night)
|
||||
values (option_id, season_id, parse_price(cost_per_night, decimal_places))
|
||||
on conflict (campsite_type_option_id, season_id) do update
|
||||
set cost_per_night = excluded.cost_per_night
|
||||
;
|
||||
$$
|
||||
language sql
|
||||
;
|
||||
|
||||
revoke execute on function set_campsite_type_option_cost(integer, integer, text, integer) from public;
|
||||
grant execute on function set_campsite_type_option_cost(integer, integer, text, integer) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,24 @@
|
|||
-- Deploy camper:translate_campsite_type_option to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: campsite_type_option_i18n
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace function translate_campsite_type_option(option_id integer, lang_tag text, name text) returns void as
|
||||
$$
|
||||
insert into campsite_type_option_i18n (campsite_type_option_id, lang_tag, name)
|
||||
values (option_id, lang_tag, name)
|
||||
on conflict (campsite_type_option_id, lang_tag) do update
|
||||
set name = excluded.name
|
||||
;
|
||||
$$
|
||||
language sql
|
||||
;
|
||||
|
||||
revoke execute on function translate_campsite_type_option(integer, text, text) from public;
|
||||
grant execute on function translate_campsite_type_option(integer, text, text) to admin;
|
||||
|
||||
commit;
|
|
@ -91,6 +91,8 @@ func (h *AdminHandler) typeHandler(user *auth.User, company *auth.Company, conn
|
|||
default:
|
||||
httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPut)
|
||||
}
|
||||
case "options":
|
||||
h.optionsHandler(user, company, conn, f.Slug).ServeHTTP(w, r)
|
||||
default:
|
||||
loc, ok := h.locales.Get(head)
|
||||
if !ok {
|
||||
|
@ -197,11 +199,11 @@ func addType(w http.ResponseWriter, r *http.Request, user *auth.User, company *a
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setPrices(ctx, tx, slug, f.Prices)
|
||||
return setTypePrices(ctx, tx, slug, f.Prices)
|
||||
})
|
||||
}
|
||||
|
||||
func setPrices(ctx context.Context, tx *database.Tx, slug string, prices map[int]*typePriceForm) error {
|
||||
func setTypePrices(ctx context.Context, tx *database.Tx, slug string, prices map[int]*typePriceForm) error {
|
||||
for seasonID, p := range prices {
|
||||
if _, err := tx.Exec(ctx, "select set_campsite_type_cost($1, $2, $3, $4)", slug, seasonID, p.MinNights, p.PricePerNight); err != nil {
|
||||
return err
|
||||
|
@ -215,7 +217,7 @@ func editType(w http.ResponseWriter, r *http.Request, user *auth.User, company *
|
|||
if _, err := conn.Exec(ctx, "select edit_campsite_type($1, $2, $3, $4, $5, $6, $7)", f.Slug, f.Media, f.Name, f.Description, f.MaxCampers, f.DogsAllowed, f.Active); err != nil {
|
||||
return err
|
||||
}
|
||||
return setPrices(ctx, tx, f.Slug, f.Prices)
|
||||
return setTypePrices(ctx, tx, f.Slug, f.Prices)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -49,18 +49,9 @@ func (l10n *typeL10nForm) MustRender(w http.ResponseWriter, r *http.Request, use
|
|||
}
|
||||
|
||||
func editTypeL10n(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, l10n *typeL10nForm) {
|
||||
if err := l10n.Parse(r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
if ok, err := form.Handle(l10n, w, r, user); err != nil {
|
||||
return
|
||||
}
|
||||
if err := user.VerifyCSRFToken(r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if !l10n.Valid(user.Locale) {
|
||||
if !httplib.IsHTMxRequest(r) {
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
}
|
||||
} else if !ok {
|
||||
l10n.MustRender(w, r, user, company)
|
||||
return
|
||||
}
|
||||
|
@ -82,3 +73,58 @@ func (l10n *typeL10nForm) Valid(l *locale.Locale) bool {
|
|||
v.CheckRequired(&l10n.Name.Input, l.GettextNoop("Name can not be empty."))
|
||||
return v.AllOK
|
||||
}
|
||||
|
||||
type optionL10nForm struct {
|
||||
Locale *locale.Locale
|
||||
TypeSlug string
|
||||
ID int
|
||||
Name *form.L10nInput
|
||||
}
|
||||
|
||||
func newOptionL10nForm(f *optionForm, loc *locale.Locale) *optionL10nForm {
|
||||
return &optionL10nForm{
|
||||
Locale: loc,
|
||||
TypeSlug: f.TypeSlug,
|
||||
ID: f.ID,
|
||||
Name: f.Name.L10nInput(),
|
||||
}
|
||||
}
|
||||
|
||||
func (l10n *optionL10nForm) FillFromDatabase(ctx context.Context, conn *database.Conn) error {
|
||||
row := conn.QueryRow(ctx, `
|
||||
select coalesce(i18n.name, '') as l10n_name
|
||||
from campsite_type_option
|
||||
left join campsite_type_option_i18n as i18n on campsite_type_option.campsite_type_option_id = i18n.campsite_type_option_id and i18n.lang_tag = $1
|
||||
where campsite_type_option.campsite_type_option_id = $2
|
||||
`, l10n.Locale.Language, l10n.ID)
|
||||
return row.Scan(&l10n.Name.Val)
|
||||
}
|
||||
|
||||
func (l10n *optionL10nForm) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
|
||||
template.MustRenderAdmin(w, r, user, company, "campsite/option/l10n.gohtml", l10n)
|
||||
}
|
||||
|
||||
func editOptionL10n(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, l10n *optionL10nForm) {
|
||||
if ok, err := form.Handle(l10n, w, r, user); err != nil {
|
||||
return
|
||||
} else if !ok {
|
||||
l10n.MustRender(w, r, user, company)
|
||||
return
|
||||
}
|
||||
conn.MustExec(r.Context(), "select translate_campsite_type_option($1, $2, $3)", l10n.ID, l10n.Locale.Language, l10n.Name)
|
||||
httplib.Redirect(w, r, "/admin/campsites/types/"+l10n.TypeSlug+"/options", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func (l10n *optionL10nForm) Parse(r *http.Request) error {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
l10n.Name.FillValue(r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l10n *optionL10nForm) Valid(l *locale.Locale) bool {
|
||||
v := form.NewValidator(l)
|
||||
v.CheckRequired(&l10n.Name.Input, l.GettextNoop("Name can not be empty."))
|
||||
return v.AllOK
|
||||
}
|
||||
|
|
|
@ -0,0 +1,364 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 jordi fita mas <jfita@peritasoft.com>
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/jackc/pgx/v4"
|
||||
|
||||
"dev.tandem.ws/tandem/camper/pkg/auth"
|
||||
"dev.tandem.ws/tandem/camper/pkg/database"
|
||||
"dev.tandem.ws/tandem/camper/pkg/form"
|
||||
httplib "dev.tandem.ws/tandem/camper/pkg/http"
|
||||
"dev.tandem.ws/tandem/camper/pkg/locale"
|
||||
"dev.tandem.ws/tandem/camper/pkg/template"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) optionsHandler(user *auth.User, company *auth.Company, conn *database.Conn, typeSlug string) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var head string
|
||||
head, r.URL.Path = httplib.ShiftPath(r.URL.Path)
|
||||
|
||||
switch head {
|
||||
case "":
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
serveOptionIndex(w, r, user, company, conn, typeSlug)
|
||||
case http.MethodPost:
|
||||
addOption(w, r, user, company, conn, typeSlug)
|
||||
default:
|
||||
httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPost)
|
||||
}
|
||||
case "new":
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
f, err := newOptionForm(r.Context(), company, conn, typeSlug)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
f.MustRender(w, r, user, company)
|
||||
default:
|
||||
httplib.MethodNotAllowed(w, r, http.MethodGet)
|
||||
}
|
||||
default:
|
||||
id, err := strconv.Atoi(head)
|
||||
if err != nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
f, err := newOptionForm(r.Context(), company, conn, typeSlug)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := f.FillFromDatabase(r.Context(), conn, id); err != nil {
|
||||
if database.ErrorIsNotFound(err) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
h.optionHandler(user, company, conn, f).ServeHTTP(w, r)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) optionHandler(user *auth.User, company *auth.Company, conn *database.Conn, f *optionForm) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var head string
|
||||
head, r.URL.Path = httplib.ShiftPath(r.URL.Path)
|
||||
|
||||
switch head {
|
||||
case "":
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
f.MustRender(w, r, user, company)
|
||||
case http.MethodPut:
|
||||
editOption(w, r, user, company, conn, f)
|
||||
default:
|
||||
httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPut)
|
||||
}
|
||||
default:
|
||||
loc, ok := h.locales.Get(head)
|
||||
if !ok {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
l10n := newOptionL10nForm(f, loc)
|
||||
if err := l10n.FillFromDatabase(r.Context(), conn); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
l10n.MustRender(w, r, user, company)
|
||||
case http.MethodPut:
|
||||
editOptionL10n(w, r, user, company, conn, l10n)
|
||||
default:
|
||||
httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPut)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func serveOptionIndex(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, typeSlug string) {
|
||||
options, err := collectOptionEntries(r.Context(), conn, typeSlug)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
page := &optionIndex{
|
||||
TypeSlug: typeSlug,
|
||||
Options: options,
|
||||
}
|
||||
page.MustRender(w, r, user, company)
|
||||
}
|
||||
|
||||
func collectOptionEntries(ctx context.Context, conn *database.Conn, typeSlug string) ([]*optionEntry, error) {
|
||||
rows, err := conn.Query(ctx, `
|
||||
select '/admin/campsites/types/' || campsite_type.slug || '/options/' || campsite_type_option_id
|
||||
, option.name
|
||||
, array_agg((lang_tag, endonym, not exists (select 1 from campsite_type_option_i18n as i18n where i18n.campsite_type_option_id = option.campsite_type_option_id and i18n.lang_tag = language.lang_tag)) order by endonym)
|
||||
from campsite_type_option as option
|
||||
join campsite_type using (campsite_type_id)
|
||||
join company using (company_id)
|
||||
, language
|
||||
where lang_tag <> default_lang_tag
|
||||
and language.selectable
|
||||
and campsite_type.slug = $1
|
||||
group by campsite_type_option_id
|
||||
, campsite_type.slug
|
||||
, option.name
|
||||
order by name
|
||||
`, pgx.QueryResultFormats{pgx.BinaryFormatCode}, typeSlug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var options []*optionEntry
|
||||
for rows.Next() {
|
||||
option := &optionEntry{}
|
||||
var translations database.RecordArray
|
||||
if err = rows.Scan(&option.URL, &option.Name, &translations); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, el := range translations.Elements {
|
||||
option.Translations = append(option.Translations, &locale.Translation{
|
||||
URL: option.URL + "/" + el.Fields[0].Get().(string),
|
||||
Endonym: el.Fields[1].Get().(string),
|
||||
Missing: el.Fields[2].Get().(bool),
|
||||
})
|
||||
}
|
||||
options = append(options, option)
|
||||
}
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
||||
type optionEntry struct {
|
||||
URL string
|
||||
Name string
|
||||
Translations []*locale.Translation
|
||||
}
|
||||
|
||||
type optionIndex struct {
|
||||
TypeSlug string
|
||||
Options []*optionEntry
|
||||
}
|
||||
|
||||
func (page *optionIndex) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
|
||||
template.MustRenderAdmin(w, r, user, company, "campsite/option/index.gohtml", page)
|
||||
}
|
||||
|
||||
func addOption(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, typeSlug string) {
|
||||
f, err := newOptionForm(r.Context(), company, conn, typeSlug)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
processOptionForm(w, r, user, company, conn, f, func(ctx context.Context, tx *database.Tx) error {
|
||||
id, err := tx.GetInt(ctx, "select add_campsite_type_option($1, $2, $3, $4)", typeSlug, f.Name, f.Min, f.Max)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setOptionPrices(ctx, tx, id, f.Prices)
|
||||
})
|
||||
}
|
||||
|
||||
func editOption(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, f *optionForm) {
|
||||
processOptionForm(w, r, user, company, conn, f, func(ctx context.Context, tx *database.Tx) error {
|
||||
if _, err := conn.Exec(ctx, "select edit_campsite_type_option($1, $2, $3, $4)", f.ID, f.Name, f.Min, f.Max); err != nil {
|
||||
return err
|
||||
}
|
||||
return setOptionPrices(ctx, tx, f.ID, f.Prices)
|
||||
})
|
||||
}
|
||||
|
||||
func processOptionForm(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, f *optionForm, act func(ctx context.Context, tx *database.Tx) error) {
|
||||
if ok, err := form.Handle(f, w, r, user); err != nil {
|
||||
return
|
||||
} else if !ok {
|
||||
f.MustRender(w, r, user, company)
|
||||
return
|
||||
}
|
||||
|
||||
tx := conn.MustBegin(r.Context())
|
||||
if err := act(r.Context(), tx); err == nil {
|
||||
tx.Commit(r.Context())
|
||||
} else {
|
||||
tx.Rollback(r.Context())
|
||||
panic(err)
|
||||
}
|
||||
httplib.Redirect(w, r, "/admin/campsites/types/"+f.TypeSlug+"/options", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func setOptionPrices(ctx context.Context, tx *database.Tx, id int, prices map[int]*optionPriceForm) error {
|
||||
for seasonID, p := range prices {
|
||||
if _, err := tx.Exec(ctx, "select set_campsite_type_option_cost($1, $2, $3)", id, seasonID, p.PricePerNight); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type optionForm struct {
|
||||
ID int
|
||||
TypeSlug string
|
||||
Name *form.Input
|
||||
Min *form.Input
|
||||
Max *form.Input
|
||||
Prices map[int]*optionPriceForm
|
||||
}
|
||||
|
||||
type optionPriceForm struct {
|
||||
SeasonName string
|
||||
PricePerNight *form.Input
|
||||
}
|
||||
|
||||
func newOptionForm(ctx context.Context, company *auth.Company, conn *database.Conn, typeSlug string) (*optionForm, error) {
|
||||
f := &optionForm{
|
||||
TypeSlug: typeSlug,
|
||||
Name: &form.Input{
|
||||
Name: "name",
|
||||
},
|
||||
Min: &form.Input{
|
||||
Name: "min",
|
||||
Val: "0",
|
||||
},
|
||||
Max: &form.Input{
|
||||
Name: "max",
|
||||
Val: "1",
|
||||
},
|
||||
}
|
||||
|
||||
rows, err := conn.Query(ctx, "select season_id, name from season where active and company_id = $1", company.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
f.Prices = make(map[int]*optionPriceForm)
|
||||
for rows.Next() {
|
||||
var id int
|
||||
var name string
|
||||
if err = rows.Scan(&id, &name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Prices[id] = &optionPriceForm{
|
||||
SeasonName: name,
|
||||
PricePerNight: &form.Input{
|
||||
Name: fmt.Sprintf("season.%d.price_per_night", id),
|
||||
Val: "0",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *optionForm) FillFromDatabase(ctx context.Context, conn *database.Conn, id int) error {
|
||||
f.ID = id
|
||||
row := conn.QueryRow(ctx, `
|
||||
select name
|
||||
, lower(range)::text
|
||||
, (upper(range) - 1)::text
|
||||
from campsite_type_option
|
||||
where campsite_type_option_id = $1
|
||||
`, id)
|
||||
if err := row.Scan(&f.Name.Val, &f.Min.Val, &f.Max.Val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rows, err := conn.Query(ctx, `
|
||||
select season_id
|
||||
, to_price(cost_per_night)
|
||||
from campsite_type_option
|
||||
join campsite_type_option_cost using (campsite_type_option_id)
|
||||
where campsite_type_option_id = $1
|
||||
`, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var seasonID int
|
||||
var pricePerNight string
|
||||
if err := rows.Scan(&seasonID, &pricePerNight); err != nil {
|
||||
return err
|
||||
}
|
||||
if p, ok := f.Prices[seasonID]; ok {
|
||||
p.PricePerNight.Val = pricePerNight
|
||||
}
|
||||
}
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
func (f *optionForm) Parse(r *http.Request) error {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
f.Name.FillValue(r)
|
||||
f.Min.FillValue(r)
|
||||
f.Max.FillValue(r)
|
||||
for _, p := range f.Prices {
|
||||
p.PricePerNight.FillValue(r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *optionForm) Valid(l *locale.Locale) bool {
|
||||
v := form.NewValidator(l)
|
||||
if v.CheckRequired(f.Name, l.GettextNoop("Name can not be empty.")) {
|
||||
v.CheckMinLength(f.Name, 1, l.GettextNoop("Name must have at least one letter."))
|
||||
}
|
||||
minValidInt := false
|
||||
if v.CheckRequired(f.Min, l.GettextNoop("Minimum can not be empty.")) {
|
||||
if v.CheckValidInteger(f.Min, l.GettextNoop("Minimum must be an integer number.")) {
|
||||
minValidInt = true
|
||||
v.CheckMinInteger(f.Min, 0, l.GettextNoop("Minimum must be zero or greater."))
|
||||
}
|
||||
}
|
||||
if v.CheckRequired(f.Max, l.GettextNoop("Maximum can not be empty.")) {
|
||||
if v.CheckValidInteger(f.Min, l.GettextNoop("Maximum must be an integer number.")) && minValidInt {
|
||||
min, _ := strconv.Atoi(f.Min.Val)
|
||||
v.CheckMinInteger(f.Min, min, l.GettextNoop("Maximum must be equal or greater than minimum."))
|
||||
}
|
||||
}
|
||||
for _, p := range f.Prices {
|
||||
if v.CheckRequired(p.PricePerNight, l.GettextNoop("Price per night can not be empty.")) {
|
||||
if v.CheckValidDecimal(p.PricePerNight, l.GettextNoop("Price per night must be a decimal number.")) {
|
||||
v.CheckMinDecimal(p.PricePerNight, 0.0, l.GettextNoop("Price per night must be zero or greater."))
|
||||
}
|
||||
}
|
||||
}
|
||||
return v.AllOK
|
||||
}
|
||||
|
||||
func (f *optionForm) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
|
||||
template.MustRenderAdmin(w, r, user, company, "campsite/option/form.gohtml", f)
|
||||
}
|
|
@ -10,6 +10,8 @@ import (
|
|||
gotemplate "html/template"
|
||||
"net/http"
|
||||
|
||||
"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"
|
||||
|
@ -58,6 +60,12 @@ type typePrice struct {
|
|||
SeasonColor string
|
||||
MinNights int
|
||||
PricePerNight string
|
||||
Options []*optionPrice
|
||||
}
|
||||
|
||||
type optionPrice struct {
|
||||
OptionName string
|
||||
PricePerNight string
|
||||
}
|
||||
|
||||
func newPublicPage(ctx context.Context, conn *database.Conn, loc *locale.Locale, slug string) (*publicPage, error) {
|
||||
|
@ -78,25 +86,45 @@ func newPublicPage(ctx context.Context, conn *database.Conn, loc *locale.Locale,
|
|||
|
||||
rows, err := conn.Query(ctx, `
|
||||
select coalesce(i18n.name, season.name) as l10n_name
|
||||
, to_color(season.color)
|
||||
, coalesce(min_nights, 1)
|
||||
, to_price(coalesce(cost_per_night, 0))::text
|
||||
, to_color(season.color)::text
|
||||
, coalesce(cost.min_nights, 1)
|
||||
, to_price(coalesce(cost.cost_per_night, 0))
|
||||
, array_agg((coalesce(option_i18n.name, option.name), to_price(coalesce(option_cost.cost_per_night, 0)))) 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
|
||||
left join (
|
||||
campsite_type_cost as cost join campsite_type as type on cost.campsite_type_id = type.campsite_type_id and type.slug = $2
|
||||
) as cost on cost.season_id = season.season_id
|
||||
left join (
|
||||
select option.*
|
||||
from campsite_type_option as option
|
||||
join campsite_type as type on option.campsite_type_id = type.campsite_type_id and type.slug = $2
|
||||
) as option on true
|
||||
left join campsite_type_option_i18n as option_i18n on option_i18n.campsite_type_option_id = option.campsite_type_option_id and option_i18n.lang_tag = $1
|
||||
left join campsite_type_option_cost as option_cost on option_cost.campsite_type_option_id = option.campsite_type_option_id and option_cost.season_id = season.season_id
|
||||
where season.active
|
||||
`, loc.Language, slug)
|
||||
group by i18n.name
|
||||
, season.name
|
||||
, season.color
|
||||
, cost.min_nights
|
||||
, cost.cost_per_night
|
||||
`, pgx.QueryResultFormats{pgx.BinaryFormatCode}, loc.Language, slug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
price := &typePrice{}
|
||||
if err := rows.Scan(&price.SeasonName, &price.SeasonColor, &price.MinNights, &price.PricePerNight); err != nil {
|
||||
var options database.RecordArray
|
||||
if err := rows.Scan(&price.SeasonName, &price.SeasonColor, &price.MinNights, &price.PricePerNight, &options); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, el := range options.Elements {
|
||||
price.Options = append(price.Options, &optionPrice{
|
||||
OptionName: el.Fields[0].Get().(string),
|
||||
PricePerNight: el.Fields[1].Get().(string),
|
||||
})
|
||||
}
|
||||
page.Prices = append(page.Prices, price)
|
||||
}
|
||||
|
||||
|
|
227
po/ca.po
227
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: 2023-10-03 21:05+0200\n"
|
||||
"POT-Creation-Date: 2023-10-06 13:18+0200\n"
|
||||
"PO-Revision-Date: 2023-07-22 23:45+0200\n"
|
||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||
"Language-Team: Catalan <ca@dodds.net>\n"
|
||||
|
@ -20,6 +20,7 @@ msgstr ""
|
|||
|
||||
#: web/templates/public/services.gohtml:6
|
||||
#: web/templates/public/services.gohtml:15
|
||||
#: web/templates/public/layout.gohtml:42
|
||||
#: web/templates/admin/services/index.gohtml:53
|
||||
msgctxt "title"
|
||||
msgid "Services"
|
||||
|
@ -50,6 +51,7 @@ msgstr "Els nostres serveis"
|
|||
#: web/templates/public/home.gohtml:34
|
||||
#: web/templates/public/surroundings.gohtml:6
|
||||
#: web/templates/public/surroundings.gohtml:10
|
||||
#: web/templates/public/layout.gohtml:43
|
||||
msgctxt "title"
|
||||
msgid "Surroundings"
|
||||
msgstr "L’entorn"
|
||||
|
@ -75,6 +77,7 @@ msgid "Come and enjoy!"
|
|||
msgstr "Vine a gaudir!"
|
||||
|
||||
#: web/templates/public/campsite/type.gohtml:17
|
||||
#: web/templates/admin/campsite/option/form.gohtml:58
|
||||
#: web/templates/admin/campsite/type/form.gohtml:73
|
||||
msgctxt "title"
|
||||
msgid "Prices"
|
||||
|
@ -85,6 +88,10 @@ msgid "%s €/night"
|
|||
msgstr "%s €/nit"
|
||||
|
||||
#: web/templates/public/campsite/type.gohtml:28
|
||||
msgid "%s: %s €/night"
|
||||
msgstr "%s: %s €/nit"
|
||||
|
||||
#: web/templates/public/campsite/type.gohtml:31
|
||||
msgid "*Minimum %d nights per stay"
|
||||
msgstr "*Mínim %d nits per estada"
|
||||
|
||||
|
@ -147,7 +154,7 @@ msgid "There are several points where you can go by kayak, from sections of the
|
|||
msgstr "Hi ha diversos punts on poder anar amb caiac, des de trams del riu Ter com també a la costa…."
|
||||
|
||||
#: web/templates/public/layout.gohtml:11 web/templates/public/layout.gohtml:24
|
||||
#: web/templates/public/layout.gohtml:59
|
||||
#: web/templates/public/layout.gohtml:61
|
||||
msgid "Campsite Montagut"
|
||||
msgstr "Càmping Montagut"
|
||||
|
||||
|
@ -179,6 +186,7 @@ msgstr "Llegenda"
|
|||
|
||||
#: web/templates/admin/carousel/form.gohtml:47
|
||||
#: web/templates/admin/campsite/form.gohtml:70
|
||||
#: web/templates/admin/campsite/option/form.gohtml:78
|
||||
#: web/templates/admin/campsite/type/form.gohtml:108
|
||||
#: web/templates/admin/season/form.gohtml:64
|
||||
#: web/templates/admin/services/form.gohtml:69
|
||||
|
@ -189,6 +197,7 @@ msgstr "Actualitza"
|
|||
|
||||
#: web/templates/admin/carousel/form.gohtml:49
|
||||
#: web/templates/admin/campsite/form.gohtml:72
|
||||
#: web/templates/admin/campsite/option/form.gohtml:80
|
||||
#: web/templates/admin/campsite/type/form.gohtml:110
|
||||
#: web/templates/admin/season/form.gohtml:66
|
||||
#: web/templates/admin/services/form.gohtml:71
|
||||
|
@ -203,6 +212,7 @@ msgid "Translate Carousel Slide to %s"
|
|||
msgstr "Traducció de la diapositiva del carrusel a %s"
|
||||
|
||||
#: web/templates/admin/carousel/l10n.gohtml:21
|
||||
#: web/templates/admin/campsite/option/l10n.gohtml:21
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:21
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:33
|
||||
#: web/templates/admin/season/l10n.gohtml:21
|
||||
|
@ -212,6 +222,7 @@ msgid "Source:"
|
|||
msgstr "Origen:"
|
||||
|
||||
#: web/templates/admin/carousel/l10n.gohtml:23
|
||||
#: web/templates/admin/campsite/option/l10n.gohtml:23
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:23
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:36
|
||||
#: web/templates/admin/season/l10n.gohtml:23
|
||||
|
@ -222,6 +233,7 @@ msgid "Translation:"
|
|||
msgstr "Traducció:"
|
||||
|
||||
#: web/templates/admin/carousel/l10n.gohtml:32
|
||||
#: web/templates/admin/campsite/option/l10n.gohtml:32
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:45
|
||||
#: web/templates/admin/season/l10n.gohtml:32
|
||||
#: web/templates/admin/services/l10n.gohtml:45
|
||||
|
@ -261,6 +273,85 @@ msgctxt "input"
|
|||
msgid "Label"
|
||||
msgstr "Etiqueta"
|
||||
|
||||
#: web/templates/admin/campsite/option/form.gohtml:8
|
||||
#: web/templates/admin/campsite/option/form.gohtml:25
|
||||
msgctxt "title"
|
||||
msgid "Edit Campsite Type Option"
|
||||
msgstr "Edició de l’opció del tipus d’allotjament"
|
||||
|
||||
#: web/templates/admin/campsite/option/form.gohtml:10
|
||||
#: web/templates/admin/campsite/option/form.gohtml:27
|
||||
msgctxt "title"
|
||||
msgid "New Campsite Type Option"
|
||||
msgstr "Nova opció del tipus d’allotjament"
|
||||
|
||||
#: web/templates/admin/campsite/option/form.gohtml:34
|
||||
#: web/templates/admin/campsite/option/l10n.gohtml:20
|
||||
#: web/templates/admin/campsite/type/form.gohtml:46
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:20
|
||||
#: web/templates/admin/season/form.gohtml:46
|
||||
#: web/templates/admin/season/l10n.gohtml:20
|
||||
#: web/templates/admin/services/form.gohtml:52
|
||||
#: web/templates/admin/services/l10n.gohtml:20
|
||||
#: web/templates/admin/profile.gohtml:26
|
||||
msgctxt "input"
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
#: web/templates/admin/campsite/option/form.gohtml:42
|
||||
msgctxt "input"
|
||||
msgid "Minimum"
|
||||
msgstr "Mínim"
|
||||
|
||||
#: web/templates/admin/campsite/option/form.gohtml:50
|
||||
msgctxt "input"
|
||||
msgid "Maximum"
|
||||
msgstr "Màxim"
|
||||
|
||||
#: web/templates/admin/campsite/option/form.gohtml:64
|
||||
#: web/templates/admin/campsite/type/form.gohtml:79
|
||||
msgctxt "input"
|
||||
msgid "Price per night"
|
||||
msgstr "Preu per nit"
|
||||
|
||||
#: web/templates/admin/campsite/option/index.gohtml:6
|
||||
#: web/templates/admin/campsite/option/index.gohtml:12
|
||||
msgctxt "title"
|
||||
msgid "Campsite Type Options"
|
||||
msgstr "Opcions del tipus d’allotjament"
|
||||
|
||||
#: web/templates/admin/campsite/option/index.gohtml:11
|
||||
msgctxt "action"
|
||||
msgid "Add Option"
|
||||
msgstr "Afegeix opció"
|
||||
|
||||
#: web/templates/admin/campsite/option/index.gohtml:17
|
||||
#: web/templates/admin/campsite/type/index.gohtml:17
|
||||
#: web/templates/admin/season/index.gohtml:18
|
||||
msgctxt "header"
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
#: web/templates/admin/campsite/option/index.gohtml:18
|
||||
#: web/templates/admin/campsite/type/index.gohtml:18
|
||||
#: web/templates/admin/season/index.gohtml:19
|
||||
#: web/templates/admin/services/index.gohtml:19
|
||||
#: web/templates/admin/services/index.gohtml:60
|
||||
#: web/templates/admin/home/index.gohtml:19
|
||||
msgctxt "header"
|
||||
msgid "Translations"
|
||||
msgstr "Traduccions"
|
||||
|
||||
#: web/templates/admin/campsite/option/index.gohtml:39
|
||||
msgid "No campsite type options added yet."
|
||||
msgstr "No s’ha afegit cap opció al tipus d’allotjament encara."
|
||||
|
||||
#: web/templates/admin/campsite/option/l10n.gohtml:7
|
||||
#: web/templates/admin/campsite/option/l10n.gohtml:14
|
||||
msgctxt "title"
|
||||
msgid "Translate Campsite Type Option to %s"
|
||||
msgstr "Traducció de la opció del tipus d’allotjament a %s"
|
||||
|
||||
#: web/templates/admin/campsite/index.gohtml:6
|
||||
#: web/templates/admin/campsite/index.gohtml:12
|
||||
#: web/templates/admin/layout.gohtml:40 web/templates/admin/layout.gohtml:71
|
||||
|
@ -284,13 +375,13 @@ msgid "Type"
|
|||
msgstr "Tipus"
|
||||
|
||||
#: web/templates/admin/campsite/index.gohtml:28
|
||||
#: web/templates/admin/campsite/type/index.gohtml:35
|
||||
#: web/templates/admin/campsite/type/index.gohtml:37
|
||||
#: web/templates/admin/season/index.gohtml:39
|
||||
msgid "Yes"
|
||||
msgstr "Sí"
|
||||
|
||||
#: web/templates/admin/campsite/index.gohtml:28
|
||||
#: web/templates/admin/campsite/type/index.gohtml:35
|
||||
#: web/templates/admin/campsite/type/index.gohtml:37
|
||||
#: web/templates/admin/season/index.gohtml:39
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
|
@ -312,22 +403,11 @@ msgid "New Campsite Type"
|
|||
msgstr "Nou tipus d’allotjament"
|
||||
|
||||
#: web/templates/admin/campsite/type/form.gohtml:37
|
||||
#: web/templates/admin/campsite/type/index.gohtml:19
|
||||
#: web/templates/admin/campsite/type/index.gohtml:20
|
||||
msgctxt "campsite type"
|
||||
msgid "Active"
|
||||
msgstr "Actiu"
|
||||
|
||||
#: web/templates/admin/campsite/type/form.gohtml:46
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:20
|
||||
#: web/templates/admin/season/form.gohtml:46
|
||||
#: web/templates/admin/season/l10n.gohtml:20
|
||||
#: web/templates/admin/services/form.gohtml:52
|
||||
#: web/templates/admin/services/l10n.gohtml:20
|
||||
#: web/templates/admin/profile.gohtml:26
|
||||
msgctxt "input"
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
#: web/templates/admin/campsite/type/form.gohtml:57
|
||||
msgctxt "input"
|
||||
msgid "Maximum number of campers"
|
||||
|
@ -338,11 +418,6 @@ msgctxt "input"
|
|||
msgid "Dogs allowed"
|
||||
msgstr "Es permeten gossos"
|
||||
|
||||
#: web/templates/admin/campsite/type/form.gohtml:79
|
||||
msgctxt "input"
|
||||
msgid "Price per night"
|
||||
msgstr "Preu per nit"
|
||||
|
||||
#: web/templates/admin/campsite/type/form.gohtml:87
|
||||
msgctxt "input"
|
||||
msgid "Minimum number of nights"
|
||||
|
@ -368,22 +443,17 @@ msgctxt "action"
|
|||
msgid "Add Type"
|
||||
msgstr "Afegeix tipus"
|
||||
|
||||
#: web/templates/admin/campsite/type/index.gohtml:17
|
||||
#: web/templates/admin/season/index.gohtml:18
|
||||
#: web/templates/admin/campsite/type/index.gohtml:19
|
||||
msgctxt "header"
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
msgid "Options"
|
||||
msgstr "Opcions"
|
||||
|
||||
#: web/templates/admin/campsite/type/index.gohtml:18
|
||||
#: web/templates/admin/season/index.gohtml:19
|
||||
#: web/templates/admin/services/index.gohtml:19
|
||||
#: web/templates/admin/services/index.gohtml:60
|
||||
#: web/templates/admin/home/index.gohtml:19
|
||||
msgctxt "campsite type"
|
||||
msgid "Translations"
|
||||
msgstr "Traduccions"
|
||||
#: web/templates/admin/campsite/type/index.gohtml:36
|
||||
msgctxt "action"
|
||||
msgid "Edit Options"
|
||||
msgstr "Edita les opcions"
|
||||
|
||||
#: web/templates/admin/campsite/type/index.gohtml:41
|
||||
#: web/templates/admin/campsite/type/index.gohtml:43
|
||||
msgid "No campsite types added yet."
|
||||
msgstr "No s’ha afegit cap tipus d’allotjament encara."
|
||||
|
||||
|
@ -566,7 +636,7 @@ msgstr "Llegenda"
|
|||
#: web/templates/admin/services/index.gohtml:20
|
||||
#: web/templates/admin/services/index.gohtml:61
|
||||
#: web/templates/admin/home/index.gohtml:20
|
||||
msgctxt "campsite type"
|
||||
msgctxt "header"
|
||||
msgid "Actions"
|
||||
msgstr "Accions"
|
||||
|
||||
|
@ -846,8 +916,9 @@ msgctxt "language option"
|
|||
msgid "Automatic"
|
||||
msgstr "Automàtic"
|
||||
|
||||
#: pkg/app/user.go:249 pkg/campsite/types/l10n.go:82
|
||||
#: pkg/campsite/types/admin.go:387 pkg/season/l10n.go:69
|
||||
#: pkg/app/user.go:249 pkg/campsite/types/l10n.go:73
|
||||
#: pkg/campsite/types/l10n.go:128 pkg/campsite/types/option.go:336
|
||||
#: pkg/campsite/types/admin.go:389 pkg/season/l10n.go:69
|
||||
#: pkg/season/admin.go:382 pkg/services/l10n.go:73 pkg/services/admin.go:266
|
||||
msgid "Name can not be empty."
|
||||
msgstr "No podeu deixar el nom en blanc."
|
||||
|
@ -868,61 +939,85 @@ msgstr "El fitxer has de ser una imatge PNG o JPEG vàlida."
|
|||
msgid "Access forbidden"
|
||||
msgstr "Accés prohibit"
|
||||
|
||||
#: pkg/campsite/types/admin.go:278
|
||||
#: pkg/campsite/types/option.go:337 pkg/campsite/types/admin.go:390
|
||||
msgid "Name must have at least one letter."
|
||||
msgstr "El nom ha de tenir com a mínim una lletra."
|
||||
|
||||
#: pkg/campsite/types/option.go:340
|
||||
msgid "Minimum can not be empty."
|
||||
msgstr "No podeu deixar el mínim en blanc."
|
||||
|
||||
#: pkg/campsite/types/option.go:341
|
||||
msgid "Minimum must be an integer number."
|
||||
msgstr "El valor del mínim ha de ser un número enter."
|
||||
|
||||
#: pkg/campsite/types/option.go:343
|
||||
msgid "Minimum must be zero or greater."
|
||||
msgstr "El valor del mínim ha de ser com a mínim zero."
|
||||
|
||||
#: pkg/campsite/types/option.go:346
|
||||
msgid "Maximum can not be empty."
|
||||
msgstr "No podeu deixar el màxim en blanc."
|
||||
|
||||
#: pkg/campsite/types/option.go:347
|
||||
msgid "Maximum must be an integer number."
|
||||
msgstr "El valor del màxim ha de ser un número enter."
|
||||
|
||||
#: pkg/campsite/types/option.go:349
|
||||
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:353 pkg/campsite/types/admin.go:403
|
||||
msgid "Price per night can not be empty."
|
||||
msgstr "No podeu deixar el preu per nit en blanc."
|
||||
|
||||
#: pkg/campsite/types/option.go:354 pkg/campsite/types/admin.go:404
|
||||
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:355 pkg/campsite/types/admin.go:405
|
||||
msgid "Price per night must be zero or greater."
|
||||
msgstr "El preu per nit ha de ser com a mínim zero."
|
||||
|
||||
#: pkg/campsite/types/admin.go:280
|
||||
msgctxt "input"
|
||||
msgid "Cover image"
|
||||
msgstr "Imatge de portada"
|
||||
|
||||
#: pkg/campsite/types/admin.go:279
|
||||
#: pkg/campsite/types/admin.go:281
|
||||
msgctxt "action"
|
||||
msgid "Set campsite type cover"
|
||||
msgstr "Estableix la portada del tipus d’allotjament"
|
||||
|
||||
#: pkg/campsite/types/admin.go:388
|
||||
msgid "Name must have at least one letter."
|
||||
msgstr "El nom ha de tenir com a mínim una lletra."
|
||||
|
||||
#: pkg/campsite/types/admin.go:390
|
||||
#: pkg/campsite/types/admin.go:392
|
||||
msgid "Cover image can not be empty."
|
||||
msgstr "No podeu deixar la imatge de portada en blanc."
|
||||
|
||||
#: pkg/campsite/types/admin.go:391
|
||||
#: pkg/campsite/types/admin.go:393
|
||||
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:395
|
||||
#: pkg/campsite/types/admin.go:397
|
||||
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:396
|
||||
#: pkg/campsite/types/admin.go:398
|
||||
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:397
|
||||
#: pkg/campsite/types/admin.go:399
|
||||
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:401
|
||||
msgid "Price per night can not be empty."
|
||||
msgstr "No podeu deixar el preu per nit en blanc."
|
||||
|
||||
#: pkg/campsite/types/admin.go:402
|
||||
msgid "Price per night must be a decimal number."
|
||||
msgstr "El preu per nit ha de ser un número decimal."
|
||||
|
||||
#: pkg/campsite/types/admin.go:403
|
||||
msgid "Price per night must be zero or greater."
|
||||
msgstr "El preu per nit ha de ser com a mínim zero."
|
||||
|
||||
#: pkg/campsite/types/admin.go:406
|
||||
#: pkg/campsite/types/admin.go:408
|
||||
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:407
|
||||
#: pkg/campsite/types/admin.go:409
|
||||
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:408
|
||||
#: pkg/campsite/types/admin.go:410
|
||||
msgid "Minimum number of nights must be one or greater."
|
||||
msgstr "El número mínim de nits no pot ser zero."
|
||||
|
||||
|
@ -1097,7 +1192,11 @@ msgstr "No podeu deixar el fitxer del mèdia en blanc."
|
|||
|
||||
#: pkg/media/admin.go:324
|
||||
msgid "Filename can not be empty."
|
||||
msgstr "No podeu deixar el nom del fitxer."
|
||||
msgstr "No podeu deixar el nom del fitxer en blanc."
|
||||
|
||||
#~ msgctxt "campsite type"
|
||||
#~ msgid "Translations"
|
||||
#~ msgstr "Traduccions"
|
||||
|
||||
#~ msgid "Surroundings"
|
||||
#~ msgstr "Entorn"
|
||||
|
|
227
po/es.po
227
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: 2023-10-03 21:05+0200\n"
|
||||
"POT-Creation-Date: 2023-10-06 13:18+0200\n"
|
||||
"PO-Revision-Date: 2023-07-22 23:46+0200\n"
|
||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||
"Language-Team: Spanish <es@tp.org.es>\n"
|
||||
|
@ -20,6 +20,7 @@ msgstr ""
|
|||
|
||||
#: web/templates/public/services.gohtml:6
|
||||
#: web/templates/public/services.gohtml:15
|
||||
#: web/templates/public/layout.gohtml:42
|
||||
#: web/templates/admin/services/index.gohtml:53
|
||||
msgctxt "title"
|
||||
msgid "Services"
|
||||
|
@ -50,6 +51,7 @@ msgstr "Nuestros servicios"
|
|||
#: web/templates/public/home.gohtml:34
|
||||
#: web/templates/public/surroundings.gohtml:6
|
||||
#: web/templates/public/surroundings.gohtml:10
|
||||
#: web/templates/public/layout.gohtml:43
|
||||
msgctxt "title"
|
||||
msgid "Surroundings"
|
||||
msgstr "El entorno"
|
||||
|
@ -75,6 +77,7 @@ msgid "Come and enjoy!"
|
|||
msgstr "¡Ven a disfrutar!"
|
||||
|
||||
#: web/templates/public/campsite/type.gohtml:17
|
||||
#: web/templates/admin/campsite/option/form.gohtml:58
|
||||
#: web/templates/admin/campsite/type/form.gohtml:73
|
||||
msgctxt "title"
|
||||
msgid "Prices"
|
||||
|
@ -85,6 +88,10 @@ msgid "%s €/night"
|
|||
msgstr "%s €/noche"
|
||||
|
||||
#: web/templates/public/campsite/type.gohtml:28
|
||||
msgid "%s: %s €/night"
|
||||
msgstr ":%s: %s €/noche"
|
||||
|
||||
#: web/templates/public/campsite/type.gohtml:31
|
||||
msgid "*Minimum %d nights per stay"
|
||||
msgstr "*Mínimo %d noches por estancia"
|
||||
|
||||
|
@ -147,7 +154,7 @@ msgid "There are several points where you can go by kayak, from sections of the
|
|||
msgstr "Hay diversos puntos dónde podéis ir en kayak, desde tramos del río Ter como también en la costa…."
|
||||
|
||||
#: web/templates/public/layout.gohtml:11 web/templates/public/layout.gohtml:24
|
||||
#: web/templates/public/layout.gohtml:59
|
||||
#: web/templates/public/layout.gohtml:61
|
||||
msgid "Campsite Montagut"
|
||||
msgstr "Camping Montagut"
|
||||
|
||||
|
@ -179,6 +186,7 @@ msgstr "Leyenda"
|
|||
|
||||
#: web/templates/admin/carousel/form.gohtml:47
|
||||
#: web/templates/admin/campsite/form.gohtml:70
|
||||
#: web/templates/admin/campsite/option/form.gohtml:78
|
||||
#: web/templates/admin/campsite/type/form.gohtml:108
|
||||
#: web/templates/admin/season/form.gohtml:64
|
||||
#: web/templates/admin/services/form.gohtml:69
|
||||
|
@ -189,6 +197,7 @@ msgstr "Actualizar"
|
|||
|
||||
#: web/templates/admin/carousel/form.gohtml:49
|
||||
#: web/templates/admin/campsite/form.gohtml:72
|
||||
#: web/templates/admin/campsite/option/form.gohtml:80
|
||||
#: web/templates/admin/campsite/type/form.gohtml:110
|
||||
#: web/templates/admin/season/form.gohtml:66
|
||||
#: web/templates/admin/services/form.gohtml:71
|
||||
|
@ -203,6 +212,7 @@ msgid "Translate Carousel Slide to %s"
|
|||
msgstr "Traducción de la diapositiva de carrusel a %s"
|
||||
|
||||
#: web/templates/admin/carousel/l10n.gohtml:21
|
||||
#: web/templates/admin/campsite/option/l10n.gohtml:21
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:21
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:33
|
||||
#: web/templates/admin/season/l10n.gohtml:21
|
||||
|
@ -212,6 +222,7 @@ msgid "Source:"
|
|||
msgstr "Origen:"
|
||||
|
||||
#: web/templates/admin/carousel/l10n.gohtml:23
|
||||
#: web/templates/admin/campsite/option/l10n.gohtml:23
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:23
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:36
|
||||
#: web/templates/admin/season/l10n.gohtml:23
|
||||
|
@ -222,6 +233,7 @@ msgid "Translation:"
|
|||
msgstr "Traducción"
|
||||
|
||||
#: web/templates/admin/carousel/l10n.gohtml:32
|
||||
#: web/templates/admin/campsite/option/l10n.gohtml:32
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:45
|
||||
#: web/templates/admin/season/l10n.gohtml:32
|
||||
#: web/templates/admin/services/l10n.gohtml:45
|
||||
|
@ -261,6 +273,85 @@ msgctxt "input"
|
|||
msgid "Label"
|
||||
msgstr "Etiqueta"
|
||||
|
||||
#: web/templates/admin/campsite/option/form.gohtml:8
|
||||
#: web/templates/admin/campsite/option/form.gohtml:25
|
||||
msgctxt "title"
|
||||
msgid "Edit Campsite Type Option"
|
||||
msgstr "Edición de la opción del tipo de alojamiento"
|
||||
|
||||
#: web/templates/admin/campsite/option/form.gohtml:10
|
||||
#: web/templates/admin/campsite/option/form.gohtml:27
|
||||
msgctxt "title"
|
||||
msgid "New Campsite Type Option"
|
||||
msgstr "Nueva opción del tipo de alojamiento"
|
||||
|
||||
#: web/templates/admin/campsite/option/form.gohtml:34
|
||||
#: web/templates/admin/campsite/option/l10n.gohtml:20
|
||||
#: web/templates/admin/campsite/type/form.gohtml:46
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:20
|
||||
#: web/templates/admin/season/form.gohtml:46
|
||||
#: web/templates/admin/season/l10n.gohtml:20
|
||||
#: web/templates/admin/services/form.gohtml:52
|
||||
#: web/templates/admin/services/l10n.gohtml:20
|
||||
#: web/templates/admin/profile.gohtml:26
|
||||
msgctxt "input"
|
||||
msgid "Name"
|
||||
msgstr "Nombre"
|
||||
|
||||
#: web/templates/admin/campsite/option/form.gohtml:42
|
||||
msgctxt "input"
|
||||
msgid "Minimum"
|
||||
msgstr "Mínimo"
|
||||
|
||||
#: web/templates/admin/campsite/option/form.gohtml:50
|
||||
msgctxt "input"
|
||||
msgid "Maximum"
|
||||
msgstr "Màximo"
|
||||
|
||||
#: web/templates/admin/campsite/option/form.gohtml:64
|
||||
#: web/templates/admin/campsite/type/form.gohtml:79
|
||||
msgctxt "input"
|
||||
msgid "Price per night"
|
||||
msgstr "Precio por noche"
|
||||
|
||||
#: web/templates/admin/campsite/option/index.gohtml:6
|
||||
#: web/templates/admin/campsite/option/index.gohtml:12
|
||||
msgctxt "title"
|
||||
msgid "Campsite Type Options"
|
||||
msgstr "Opciones del tipo de alojamiento"
|
||||
|
||||
#: web/templates/admin/campsite/option/index.gohtml:11
|
||||
msgctxt "action"
|
||||
msgid "Add Option"
|
||||
msgstr "Añadir opción"
|
||||
|
||||
#: web/templates/admin/campsite/option/index.gohtml:17
|
||||
#: web/templates/admin/campsite/type/index.gohtml:17
|
||||
#: web/templates/admin/season/index.gohtml:18
|
||||
msgctxt "header"
|
||||
msgid "Name"
|
||||
msgstr "Nombre"
|
||||
|
||||
#: web/templates/admin/campsite/option/index.gohtml:18
|
||||
#: web/templates/admin/campsite/type/index.gohtml:18
|
||||
#: web/templates/admin/season/index.gohtml:19
|
||||
#: web/templates/admin/services/index.gohtml:19
|
||||
#: web/templates/admin/services/index.gohtml:60
|
||||
#: web/templates/admin/home/index.gohtml:19
|
||||
msgctxt "header"
|
||||
msgid "Translations"
|
||||
msgstr "Traducciones"
|
||||
|
||||
#: web/templates/admin/campsite/option/index.gohtml:39
|
||||
msgid "No campsite type options added yet."
|
||||
msgstr "No se ha añadido ninguna opció al tipo de alojamiento todavía."
|
||||
|
||||
#: web/templates/admin/campsite/option/l10n.gohtml:7
|
||||
#: web/templates/admin/campsite/option/l10n.gohtml:14
|
||||
msgctxt "title"
|
||||
msgid "Translate Campsite Type Option to %s"
|
||||
msgstr "Traducción de la opción del tipo de alojamiento a %s"
|
||||
|
||||
#: web/templates/admin/campsite/index.gohtml:6
|
||||
#: web/templates/admin/campsite/index.gohtml:12
|
||||
#: web/templates/admin/layout.gohtml:40 web/templates/admin/layout.gohtml:71
|
||||
|
@ -284,13 +375,13 @@ msgid "Type"
|
|||
msgstr "Tipo"
|
||||
|
||||
#: web/templates/admin/campsite/index.gohtml:28
|
||||
#: web/templates/admin/campsite/type/index.gohtml:35
|
||||
#: web/templates/admin/campsite/type/index.gohtml:37
|
||||
#: web/templates/admin/season/index.gohtml:39
|
||||
msgid "Yes"
|
||||
msgstr "Sí"
|
||||
|
||||
#: web/templates/admin/campsite/index.gohtml:28
|
||||
#: web/templates/admin/campsite/type/index.gohtml:35
|
||||
#: web/templates/admin/campsite/type/index.gohtml:37
|
||||
#: web/templates/admin/season/index.gohtml:39
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
|
@ -303,7 +394,7 @@ msgstr "No se ha añadido ningún alojamiento todavía."
|
|||
#: web/templates/admin/campsite/type/form.gohtml:25
|
||||
msgctxt "title"
|
||||
msgid "Edit Campsite Type"
|
||||
msgstr "Edición del tipo de alojamientos"
|
||||
msgstr "Edición del tipo de alojamiento"
|
||||
|
||||
#: web/templates/admin/campsite/type/form.gohtml:10
|
||||
#: web/templates/admin/campsite/type/form.gohtml:27
|
||||
|
@ -312,22 +403,11 @@ msgid "New Campsite Type"
|
|||
msgstr "Nuevo tipo de alojamiento"
|
||||
|
||||
#: web/templates/admin/campsite/type/form.gohtml:37
|
||||
#: web/templates/admin/campsite/type/index.gohtml:19
|
||||
#: web/templates/admin/campsite/type/index.gohtml:20
|
||||
msgctxt "campsite type"
|
||||
msgid "Active"
|
||||
msgstr "Activo"
|
||||
|
||||
#: web/templates/admin/campsite/type/form.gohtml:46
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:20
|
||||
#: web/templates/admin/season/form.gohtml:46
|
||||
#: web/templates/admin/season/l10n.gohtml:20
|
||||
#: web/templates/admin/services/form.gohtml:52
|
||||
#: web/templates/admin/services/l10n.gohtml:20
|
||||
#: web/templates/admin/profile.gohtml:26
|
||||
msgctxt "input"
|
||||
msgid "Name"
|
||||
msgstr "Nombre"
|
||||
|
||||
#: web/templates/admin/campsite/type/form.gohtml:57
|
||||
msgctxt "input"
|
||||
msgid "Maximum number of campers"
|
||||
|
@ -338,11 +418,6 @@ msgctxt "input"
|
|||
msgid "Dogs allowed"
|
||||
msgstr "Se permiten perros"
|
||||
|
||||
#: web/templates/admin/campsite/type/form.gohtml:79
|
||||
msgctxt "input"
|
||||
msgid "Price per night"
|
||||
msgstr "Precio por noche"
|
||||
|
||||
#: web/templates/admin/campsite/type/form.gohtml:87
|
||||
msgctxt "input"
|
||||
msgid "Minimum number of nights"
|
||||
|
@ -368,22 +443,17 @@ msgctxt "action"
|
|||
msgid "Add Type"
|
||||
msgstr "Añadir tipo"
|
||||
|
||||
#: web/templates/admin/campsite/type/index.gohtml:17
|
||||
#: web/templates/admin/season/index.gohtml:18
|
||||
#: web/templates/admin/campsite/type/index.gohtml:19
|
||||
msgctxt "header"
|
||||
msgid "Name"
|
||||
msgstr "Nombre"
|
||||
msgid "Options"
|
||||
msgstr "Opciones"
|
||||
|
||||
#: web/templates/admin/campsite/type/index.gohtml:18
|
||||
#: web/templates/admin/season/index.gohtml:19
|
||||
#: web/templates/admin/services/index.gohtml:19
|
||||
#: web/templates/admin/services/index.gohtml:60
|
||||
#: web/templates/admin/home/index.gohtml:19
|
||||
msgctxt "campsite type"
|
||||
msgid "Translations"
|
||||
msgstr "Traducciones"
|
||||
#: web/templates/admin/campsite/type/index.gohtml:36
|
||||
msgctxt "action"
|
||||
msgid "Edit Options"
|
||||
msgstr "Editar opciones"
|
||||
|
||||
#: web/templates/admin/campsite/type/index.gohtml:41
|
||||
#: web/templates/admin/campsite/type/index.gohtml:43
|
||||
msgid "No campsite types added yet."
|
||||
msgstr "No se ha añadido ningún tipo de alojamiento todavía."
|
||||
|
||||
|
@ -566,7 +636,7 @@ msgstr "Leyenda"
|
|||
#: web/templates/admin/services/index.gohtml:20
|
||||
#: web/templates/admin/services/index.gohtml:61
|
||||
#: web/templates/admin/home/index.gohtml:20
|
||||
msgctxt "campsite type"
|
||||
msgctxt "header"
|
||||
msgid "Actions"
|
||||
msgstr "Acciones"
|
||||
|
||||
|
@ -846,8 +916,9 @@ msgctxt "language option"
|
|||
msgid "Automatic"
|
||||
msgstr "Automático"
|
||||
|
||||
#: pkg/app/user.go:249 pkg/campsite/types/l10n.go:82
|
||||
#: pkg/campsite/types/admin.go:387 pkg/season/l10n.go:69
|
||||
#: pkg/app/user.go:249 pkg/campsite/types/l10n.go:73
|
||||
#: pkg/campsite/types/l10n.go:128 pkg/campsite/types/option.go:336
|
||||
#: pkg/campsite/types/admin.go:389 pkg/season/l10n.go:69
|
||||
#: pkg/season/admin.go:382 pkg/services/l10n.go:73 pkg/services/admin.go:266
|
||||
msgid "Name can not be empty."
|
||||
msgstr "No podéis dejar el nombre en blanco."
|
||||
|
@ -868,61 +939,85 @@ msgstr "El archivo tiene que ser una imagen PNG o JPEG válida."
|
|||
msgid "Access forbidden"
|
||||
msgstr "Acceso prohibido"
|
||||
|
||||
#: pkg/campsite/types/admin.go:278
|
||||
#: pkg/campsite/types/option.go:337 pkg/campsite/types/admin.go:390
|
||||
msgid "Name must have at least one letter."
|
||||
msgstr "El nombre tiene que tener como mínimo una letra."
|
||||
|
||||
#: pkg/campsite/types/option.go:340
|
||||
msgid "Minimum can not be empty."
|
||||
msgstr "No podéis dejar el mínimo en blanco."
|
||||
|
||||
#: pkg/campsite/types/option.go:341
|
||||
msgid "Minimum must be an integer number."
|
||||
msgstr "El valor de mínimo tiene que ser un número entero."
|
||||
|
||||
#: pkg/campsite/types/option.go:343
|
||||
msgid "Minimum must be zero or greater."
|
||||
msgstr "El valor de mínimo tiene que ser como mínimo cero."
|
||||
|
||||
#: pkg/campsite/types/option.go:346
|
||||
msgid "Maximum can not be empty."
|
||||
msgstr "No podéis dejar el máxmimo en blanco."
|
||||
|
||||
#: pkg/campsite/types/option.go:347
|
||||
msgid "Maximum must be an integer number."
|
||||
msgstr "El valor del máximo tiene que ser un número entero."
|
||||
|
||||
#: pkg/campsite/types/option.go:349
|
||||
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:353 pkg/campsite/types/admin.go:403
|
||||
msgid "Price per night can not be empty."
|
||||
msgstr "No podéis dejar el precio por noche en blanco."
|
||||
|
||||
#: pkg/campsite/types/option.go:354 pkg/campsite/types/admin.go:404
|
||||
msgid "Price per night must be a decimal number."
|
||||
msgstr "El precio por noche tien que ser un número decimal."
|
||||
|
||||
#: pkg/campsite/types/option.go:355 pkg/campsite/types/admin.go:405
|
||||
msgid "Price per night must be zero or greater."
|
||||
msgstr "El precio por noche tiene que ser como mínimo cero."
|
||||
|
||||
#: pkg/campsite/types/admin.go:280
|
||||
msgctxt "input"
|
||||
msgid "Cover image"
|
||||
msgstr "Imagen de portada"
|
||||
|
||||
#: pkg/campsite/types/admin.go:279
|
||||
#: pkg/campsite/types/admin.go:281
|
||||
msgctxt "action"
|
||||
msgid "Set campsite type cover"
|
||||
msgstr "Establecer la portada del tipo de alojamiento"
|
||||
|
||||
#: pkg/campsite/types/admin.go:388
|
||||
msgid "Name must have at least one letter."
|
||||
msgstr "El nombre tiene que tener como mínimo una letra."
|
||||
|
||||
#: pkg/campsite/types/admin.go:390
|
||||
#: pkg/campsite/types/admin.go:392
|
||||
msgid "Cover image can not be empty."
|
||||
msgstr "No podéis dejar la imagen de portada en blanco."
|
||||
|
||||
#: pkg/campsite/types/admin.go:391
|
||||
#: pkg/campsite/types/admin.go:393
|
||||
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:395
|
||||
#: pkg/campsite/types/admin.go:397
|
||||
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:396
|
||||
#: pkg/campsite/types/admin.go:398
|
||||
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:397
|
||||
#: pkg/campsite/types/admin.go:399
|
||||
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:401
|
||||
msgid "Price per night can not be empty."
|
||||
msgstr "No podéis dejar el precio por noche en blanco."
|
||||
|
||||
#: pkg/campsite/types/admin.go:402
|
||||
msgid "Price per night must be a decimal number."
|
||||
msgstr "El precio por noche tien que ser un número decimal."
|
||||
|
||||
#: pkg/campsite/types/admin.go:403
|
||||
msgid "Price per night must be zero or greater."
|
||||
msgstr "El precio por noche tiene que ser como mínimo cero."
|
||||
|
||||
#: pkg/campsite/types/admin.go:406
|
||||
#: pkg/campsite/types/admin.go:408
|
||||
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:407
|
||||
#: pkg/campsite/types/admin.go:409
|
||||
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:408
|
||||
#: pkg/campsite/types/admin.go:410
|
||||
msgid "Minimum number of nights must be one or greater."
|
||||
msgstr "El número mínimo de noches no puede ser cero."
|
||||
|
||||
|
@ -1099,6 +1194,10 @@ 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."
|
||||
|
||||
#~ msgctxt "campsite type"
|
||||
#~ msgid "Translations"
|
||||
#~ msgstr "Traducciones"
|
||||
|
||||
#~ msgid "Surroundings"
|
||||
#~ msgstr "Entorno"
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:add_campsite_type_option from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists camper.add_campsite_type_option(uuid, text, integer, integer);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:campsite_type_option from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.campsite_type_option;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:campsite_type_option_cost from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.campsite_type_option_cost;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:campsite_type_option_i18n from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.campsite_type_option_i18n;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:edit_campsite_type_option from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists camper.edit_campsite_type_option(integer, text, integer, integer);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:set_campsite_type_option_cost from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists camper.set_campsite_type_option_cost(integer, integer, text, integer);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:translate_campsite_type_option from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists camper.translate_campsite_type_option(integer, text, text);
|
||||
|
||||
commit;
|
|
@ -90,3 +90,10 @@ to_price [roles schema_camper] 2023-10-01T16:30:40Z jordi fita mas <jordi@tandem
|
|||
set_campsite_type_cost [roles schema_camper campsite_type_cost parse_price] 2023-10-01T17:51:23Z jordi fita mas <jordi@tandem.blog> # Add function to set the cost of a campsite type for a given season
|
||||
season_i18n [roles schema_camper season language] 2023-10-03T18:30:42Z jordi fita mas <jordi@tandem.blog> # Add relation for season translations
|
||||
translate_season [roles schema_camper season season_i18n] 2023-10-03T18:37:19Z jordi fita mas <jordi@tandem.blog> # Add function to translate seasons
|
||||
campsite_type_option [roles schema_camper campsite_type user_profile] 2023-10-05T16:19:03Z jordi fita mas <jordi@tandem.blog> # Add relation for campsite type “extra” options
|
||||
campsite_type_option_i18n [roles schema_camper campsite_type_option language] 2023-10-05T16:56:57Z jordi fita mas <jordi@tandem.blog> # Add the relation for campsite_type_option internationalization
|
||||
translate_campsite_type_option [roles schema_camper campsite_type_option_i18n] 2023-10-05T17:05:31Z jordi fita mas <jordi@tandem.blog> # Add function to translate campsite type options
|
||||
campsite_type_option_cost [roles schema_camper campsite_type season campsite_type_option user_profile] 2023-10-05T17:21:30Z jordi fita mas <jordi@tandem.blog> # Add relation for campsite type option cost
|
||||
set_campsite_type_option_cost [roles schema_camper campsite_type_option_cost parse_price] 2023-10-05T17:41:58Z jordi fita mas <jordi@tandem.blog> # Add function to set cost of campsite type option
|
||||
add_campsite_type_option [roles schema_camper campsite_type_option campsite_type] 2023-10-06T09:40:03Z jordi fita mas <jordi@tandem.blog> # Add function to create new campsite type options
|
||||
edit_campsite_type_option [roles schema_camper campsite_type_option] 2023-10-06T09:51:02Z jordi fita mas <jordi@tandem.blog> # Add function to edit campsite type options
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
-- Test add_campsite_type_option
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(13);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_function('camper', 'add_campsite_type_option', array['uuid', 'text', 'integer', 'integer']);
|
||||
select function_lang_is('camper', 'add_campsite_type_option', array['uuid', 'text', 'integer', 'integer'], 'sql');
|
||||
select function_returns('camper', 'add_campsite_type_option', array['uuid', 'text', 'integer', 'integer'], 'integer');
|
||||
select isnt_definer('camper', 'add_campsite_type_option', array['uuid', 'text', 'integer', 'integer']);
|
||||
select volatility_is('camper', 'add_campsite_type_option', array['uuid', 'text', 'integer', 'integer'], 'volatile');
|
||||
select function_privs_are('camper', 'add_campsite_type_option', array ['uuid', 'text', 'integer', 'integer'], 'guest', array[]::text[]);
|
||||
select function_privs_are('camper', 'add_campsite_type_option', array ['uuid', 'text', 'integer', 'integer'], 'employee', array[]::text[]);
|
||||
select function_privs_are('camper', 'add_campsite_type_option', array ['uuid', 'text', 'integer', 'integer'], 'admin', array['EXECUTE']);
|
||||
select function_privs_are('camper', 'add_campsite_type_option', array ['uuid', 'text', 'integer', 'integer'], 'authenticator', array[]::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate campsite_type_option_i18n cascade;
|
||||
truncate campsite_type_option cascade;
|
||||
truncate campsite_type cascade;
|
||||
truncate media cascade;
|
||||
truncate media_content cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca')
|
||||
;
|
||||
|
||||
insert into media_content (media_type, bytes)
|
||||
values ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffffff","a"};')
|
||||
;
|
||||
|
||||
insert into media (media_id, company_id, original_filename, content_hash)
|
||||
values (2, 1, 'cover2.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};'))
|
||||
;
|
||||
|
||||
insert into campsite_type (campsite_type_id, company_id, slug, media_id, name, description, active, dogs_allowed, max_campers)
|
||||
values (3, 1, '87452b88-b48f-48d3-bb6c-0296de64164e', 2, 'Type A', '<p>A</p>', true, false, 4)
|
||||
, (4, 1, '9ae5cf87-cd69-4541-b5a5-75f937cc9e58', 2, 'Type B', '<p>B</p>', true, false, 5)
|
||||
;
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_campsite_type_option('87452b88-b48f-48d3-bb6c-0296de64164e', 'Option 1', 0, 10) $$,
|
||||
'Should be able to add an option to the first campsite type'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_campsite_type_option('9ae5cf87-cd69-4541-b5a5-75f937cc9e58', 'Option 2', 5, 15) $$,
|
||||
'Should be able to add an option to the second campsite type'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select campsite_type_id, name, range from campsite_type_option $$,
|
||||
$$ values (3, 'Option 1', '[0, 10]'::int4range)
|
||||
, (4, 'Option 2', '[5, 15]'::int4range)
|
||||
$$,
|
||||
'Should have added all two campsite type options'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select * from campsite_type_option_i18n $$,
|
||||
'Should not have added any translation for campsite type options.'
|
||||
);
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,208 @@
|
|||
-- Test campsite_type_option
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(40);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_table('campsite_type_option');
|
||||
select has_pk('campsite_type_option');
|
||||
select table_privs_are('campsite_type_option', 'guest', array['SELECT']);
|
||||
select table_privs_are('campsite_type_option', 'employee', array['SELECT']);
|
||||
select table_privs_are('campsite_type_option', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('campsite_type_option', 'authenticator', array[]::text[]);
|
||||
|
||||
select has_column('campsite_type_option', 'campsite_type_option_id');
|
||||
select col_is_pk('campsite_type_option', 'campsite_type_option_id');
|
||||
select col_type_is('campsite_type_option', 'campsite_type_option_id', 'integer');
|
||||
select col_not_null('campsite_type_option', 'campsite_type_option_id');
|
||||
select col_hasnt_default('campsite_type_option', 'campsite_type_option_id');
|
||||
|
||||
select has_column('campsite_type_option', 'campsite_type_id');
|
||||
select col_is_fk('campsite_type_option', 'campsite_type_id');
|
||||
select fk_ok('campsite_type_option', 'campsite_type_id', 'campsite_type', 'campsite_type_id');
|
||||
select col_type_is('campsite_type_option', 'campsite_type_id', 'integer');
|
||||
select col_not_null('campsite_type_option', 'campsite_type_id');
|
||||
select col_hasnt_default('campsite_type_option', 'campsite_type_id');
|
||||
|
||||
select has_column('campsite_type_option', 'name');
|
||||
select col_type_is('campsite_type_option', 'name', 'text');
|
||||
select col_not_null('campsite_type_option', 'name');
|
||||
select col_hasnt_default('campsite_type_option', 'name');
|
||||
|
||||
select has_column('campsite_type_option', 'range');
|
||||
select col_type_is('campsite_type_option', 'range', 'int4range');
|
||||
select col_not_null('campsite_type_option', 'range');
|
||||
select col_hasnt_default('campsite_type_option', 'range');
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate campsite_type_option cascade;
|
||||
truncate campsite_type cascade;
|
||||
truncate media cascade;
|
||||
truncate media_content cascade;
|
||||
truncate company_host cascade;
|
||||
truncate company_user cascade;
|
||||
truncate company cascade;
|
||||
truncate auth."user" cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at)
|
||||
values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||
, (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||
;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca')
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 'ca')
|
||||
;
|
||||
|
||||
insert into company_user (company_id, user_id, role)
|
||||
values (2, 1, 'admin')
|
||||
, (4, 5, 'admin')
|
||||
;
|
||||
|
||||
insert into company_host (company_id, host)
|
||||
values (2, 'co2')
|
||||
, (4, 'co4')
|
||||
;
|
||||
|
||||
insert into media_content (media_type, bytes)
|
||||
values ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffffff","a"};')
|
||||
;
|
||||
|
||||
insert into media (media_id, company_id, original_filename, content_hash)
|
||||
values (6, 2, 'cover2.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};'))
|
||||
, (8, 4, 'cover4.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};'))
|
||||
;
|
||||
|
||||
insert into campsite_type (campsite_type_id, company_id, name, media_id, dogs_allowed, max_campers)
|
||||
values (16, 2, 'Wooden lodge', 6, false, 7)
|
||||
, (18, 4, 'Bungalow', 8, false, 6)
|
||||
;
|
||||
|
||||
insert into campsite_type_option (campsite_type_id, name, range)
|
||||
values (16, 'Option 16.1', '[2, 2]')
|
||||
, (18, 'Option 18.1', '[4, 8]')
|
||||
;
|
||||
|
||||
prepare campsite_option_data as
|
||||
select campsite_type_id, name
|
||||
from campsite_type_option
|
||||
;
|
||||
|
||||
set role guest;
|
||||
select bag_eq(
|
||||
'campsite_option_data',
|
||||
$$ values (16, 'Option 16.1')
|
||||
, (18, 'Option 18.1')
|
||||
$$,
|
||||
'Everyone should be able to list all campsite type options across all companies'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2');
|
||||
|
||||
select lives_ok(
|
||||
$$ insert into campsite_type_option(campsite_type_id, name, range) values (16, 'Option 16.2', '[3, 3]') $$,
|
||||
'Admin from company 2 should be able to insert a new campsite type option to that company.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'campsite_option_data',
|
||||
$$ values (16, 'Option 16.1')
|
||||
, (16, 'Option 16.2')
|
||||
, (18, 'Option 18.1')
|
||||
$$,
|
||||
'The new row should have been added'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ update campsite_type_option set name = 'Option 16-2' where campsite_type_id = 16 and name = 'Option 16.2' $$,
|
||||
'Admin from company 2 should be able to update campsite type option of that company.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'campsite_option_data',
|
||||
$$ values (16, 'Option 16.1')
|
||||
, (16, 'Option 16-2')
|
||||
, (18, 'Option 18.1')
|
||||
$$,
|
||||
'The row should have been updated.'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ delete from campsite_type_option where campsite_type_id = 16 and name = 'Option 16-2' $$,
|
||||
'Admin from company 2 should be able to delete campsite type option from that company.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'campsite_option_data',
|
||||
$$ values (16, 'Option 16.1')
|
||||
, (18, 'Option 18.1')
|
||||
$$,
|
||||
'The row should have been deleted.'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ insert into campsite_type_option (campsite_type_id, name, range) values (18, 'Option 18.2', '[5, 5]') $$,
|
||||
'42501', 'new row violates row-level security policy for table "campsite_type_option"',
|
||||
'Admin from company 2 should NOT be able to insert new campsite type options to company 4.'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ update campsite_type_option set name = 'Option 18-1' where campsite_type_id = 18 $$,
|
||||
'Admin from company 2 should not be able to update campsite types of company 4, but no error if campsite_type_id is not changed.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'campsite_option_data',
|
||||
$$ values (16, 'Option 16.1')
|
||||
, (18, 'Option 18.1')
|
||||
$$,
|
||||
'No row should have been changed.'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ update campsite_type_option set campsite_type_id = 18 where campsite_type_id = 16 $$,
|
||||
'42501', 'new row violates row-level security policy for table "campsite_type_option"',
|
||||
'Admin from company 2 should NOT be able to move campsite type option to one of company 4'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ delete from campsite_type_option where campsite_type_id = 18 $$,
|
||||
'Admin from company 2 should NOT be able to delete campsite type from company 4, but not error is thrown'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'campsite_option_data',
|
||||
$$ values (16, 'Option 16.1')
|
||||
, (18, 'Option 18.1')
|
||||
$$,
|
||||
'No row should have been changed'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ insert into campsite_type_option (campsite_type_id, name, range) values (16, ' ', '[5, 6]') $$,
|
||||
'23514', 'new row for relation "campsite_type_option" violates check constraint "name_not_empty"',
|
||||
'Should not be able to insert campsite type options with a blank name.'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ insert into campsite_type_option (campsite_type_id, name, range) values (16, 'Option 16.2', '[-1, 1]') $$,
|
||||
'23514', 'new row for relation "campsite_type_option" violates check constraint "range_not_negative"',
|
||||
'Should not be able to insert campsite type options with a range starting with a negative number'
|
||||
);
|
||||
|
||||
reset role;
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
-- Test campsite_type_option_cost
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(42);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_table('campsite_type_option_cost');
|
||||
select has_pk('campsite_type_option_cost');
|
||||
select col_is_pk('campsite_type_option_cost', array['campsite_type_option_id', 'season_id']);
|
||||
select table_privs_are('campsite_type_option_cost', 'guest', array['SELECT']);
|
||||
select table_privs_are('campsite_type_option_cost', 'employee', array['SELECT']);
|
||||
select table_privs_are('campsite_type_option_cost', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('campsite_type_option_cost', 'authenticator', array[]::text[]);
|
||||
|
||||
select has_column('campsite_type_option_cost', 'campsite_type_option_id');
|
||||
select col_is_fk('campsite_type_option_cost', 'campsite_type_option_id');
|
||||
select fk_ok('campsite_type_option_cost', 'campsite_type_option_id', 'campsite_type_option', 'campsite_type_option_id');
|
||||
select col_type_is('campsite_type_option_cost', 'campsite_type_option_id', 'integer');
|
||||
select col_not_null('campsite_type_option_cost', 'campsite_type_option_id');
|
||||
select col_hasnt_default('campsite_type_option_cost', 'campsite_type_option_id');
|
||||
|
||||
select has_column('campsite_type_option_cost', 'season_id');
|
||||
select col_is_fk('campsite_type_option_cost', 'season_id');
|
||||
select fk_ok('campsite_type_option_cost', 'season_id', 'season', 'season_id');
|
||||
select col_type_is('campsite_type_option_cost', 'season_id', 'integer');
|
||||
select col_not_null('campsite_type_option_cost', 'season_id');
|
||||
select col_hasnt_default('campsite_type_option_cost', 'season_id');
|
||||
|
||||
select has_column('campsite_type_option_cost', 'cost_per_night');
|
||||
select col_type_is('campsite_type_option_cost', 'cost_per_night', 'integer');
|
||||
select col_not_null('campsite_type_option_cost', 'cost_per_night');
|
||||
select col_hasnt_default('campsite_type_option_cost', 'cost_per_night');
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate campsite_type_option_cost cascade;
|
||||
truncate season cascade;
|
||||
truncate campsite_type_option cascade;
|
||||
truncate campsite_type cascade;
|
||||
truncate media cascade;
|
||||
truncate media_content cascade;
|
||||
truncate company_host cascade;
|
||||
truncate company_user cascade;
|
||||
truncate company cascade;
|
||||
truncate auth."user" cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at)
|
||||
values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||
, (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||
;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca')
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 'ca')
|
||||
;
|
||||
|
||||
insert into company_user (company_id, user_id, role)
|
||||
values (2, 1, 'admin')
|
||||
, (4, 5, 'admin')
|
||||
;
|
||||
|
||||
insert into company_host (company_id, host)
|
||||
values (2, 'co2')
|
||||
, (4, 'co4')
|
||||
;
|
||||
|
||||
insert into media_content (media_type, bytes)
|
||||
values ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffffff","a"};')
|
||||
;
|
||||
|
||||
insert into media (media_id, company_id, original_filename, content_hash)
|
||||
values (6, 2, 'cover2.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};'))
|
||||
, (8, 4, 'cover4.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};'))
|
||||
;
|
||||
|
||||
insert into campsite_type (campsite_type_id, company_id, name, media_id, dogs_allowed, max_campers)
|
||||
values (16, 2, 'Wooden lodge', 6, false, 7)
|
||||
, (18, 4, 'Bungalow', 8, false, 6)
|
||||
;
|
||||
|
||||
insert into season (season_id, company_id, name)
|
||||
values (26, 2, 'Low')
|
||||
, (27, 2, 'Mid')
|
||||
, (28, 4, 'Low')
|
||||
, (29, 4, 'Mid')
|
||||
;
|
||||
|
||||
insert into campsite_type_option (campsite_type_option_id, campsite_type_id, name, range)
|
||||
values (17, 16, 'Option 16.1', '[1, 16]')
|
||||
, (19, 18, 'Option 18.1', '[1, 18]')
|
||||
;
|
||||
|
||||
insert into campsite_type_option_cost (campsite_type_option_id, season_id, cost_per_night)
|
||||
values (17, 26, 2)
|
||||
, (19, 28, 4)
|
||||
;
|
||||
|
||||
prepare option_cost_data as
|
||||
select campsite_type_option_id, season_id, cost_per_night
|
||||
from campsite_type_option_cost
|
||||
;
|
||||
|
||||
set role guest;
|
||||
select bag_eq(
|
||||
'option_cost_data',
|
||||
$$ values (17, 26, 2)
|
||||
, (19, 28, 4)
|
||||
$$,
|
||||
'Everyone should be able to list all option costs across all companies'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2');
|
||||
|
||||
select lives_ok(
|
||||
$$ insert into campsite_type_option_cost(campsite_type_option_id, season_id, cost_per_night) values (17, 27, 3) $$,
|
||||
'Admin from company 2 should be able to insert a new option cost to that company.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'option_cost_data',
|
||||
$$ values (17, 26, 2)
|
||||
, (17, 27, 3)
|
||||
, (19, 28, 4)
|
||||
$$,
|
||||
'The new row should have been added'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ update campsite_type_option_cost set cost_per_night = 6 where campsite_type_option_id = 17 and season_id = 27 $$,
|
||||
'Admin from company 2 should be able to update option cost of that company.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'option_cost_data',
|
||||
$$ values (17, 26, 2)
|
||||
, (17, 27, 6)
|
||||
, (19, 28, 4)
|
||||
$$,
|
||||
'The row should have been updated.'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ delete from campsite_type_option_cost where campsite_type_option_id = 17 and season_id = 27 $$,
|
||||
'Admin from company 2 should be able to delete option cost from that company.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'option_cost_data',
|
||||
$$ values (17, 26, 2)
|
||||
, (19, 28, 4)
|
||||
$$,
|
||||
'The row should have been deleted.'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ insert into campsite_type_option_cost (campsite_type_option_id, season_id, cost_per_night) values (19, 29, 5) $$,
|
||||
'42501', 'new row violates row-level security policy for table "campsite_type_option_cost"',
|
||||
'Admin from company 2 should NOT be able to insert new option costs to company 4.'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ insert into campsite_type_option_cost (campsite_type_option_id, season_id, cost_per_night) values (19, 27, 5) $$,
|
||||
'42501', 'new row violates row-level security policy for table "campsite_type_option_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_option_cost (campsite_type_option_id, season_id, cost_per_night) values (17, 29, 5) $$,
|
||||
'42501', 'new row violates row-level security policy for table "campsite_type_option_cost"',
|
||||
'Admin from company 2 should NOT be able to insert new row with a season from company 4.'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ update campsite_type_option_cost set cost_per_night = 1 where campsite_type_option_id = 19 $$,
|
||||
'Admin from company 2 should not be able to update campsite types of company 4, but no error if campsite_type_option_id is not changed.'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ update campsite_type_option_cost set cost_per_night = 1 where season_id = 28 $$,
|
||||
'Admin from company 2 should not be able to update seasons of company 4, but no error if season_id is not changed.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'option_cost_data',
|
||||
$$ values (17, 26, 2)
|
||||
, (19, 28, 4)
|
||||
$$,
|
||||
'No row should have been changed.'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ update campsite_type_option_cost set campsite_type_option_id = 19 where campsite_type_option_id = 17 $$,
|
||||
'42501', 'new row violates row-level security policy for table "campsite_type_option_cost"',
|
||||
'Admin from company 2 should NOT be able to move campsite type to one of company 4'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ update campsite_type_option_cost set season_id = 29 where season_id = 26 $$,
|
||||
'42501', 'new row violates row-level security policy for table "campsite_type_option_cost"',
|
||||
'Admin from company 2 should NOT be able to move season to one of company 4'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ delete from campsite_type_option_cost where campsite_type_option_id = 19 $$,
|
||||
'Admin from company 2 should NOT be able to delete campsite type from company 4, but not error is thrown'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ delete from campsite_type_option_cost where season_id = 28 $$,
|
||||
'Admin from company 2 should NOT be able to delete season from company 4, but not error is thrown'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'option_cost_data',
|
||||
$$ values (17, 26, 2)
|
||||
, (19, 28, 4)
|
||||
$$,
|
||||
'No row should have been changed'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ insert into campsite_type_option_cost (campsite_type_option_id, season_id, cost_per_night) values (17, 27, -1) $$,
|
||||
'23514', 'new row for relation "campsite_type_option_cost" violates check constraint "cost_not_negative"',
|
||||
'Should not be able to insert option costs with negative cost per night.'
|
||||
);
|
||||
|
||||
reset role;
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
-- Test campsite_type_option_i18n
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(23);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_table('campsite_type_option_i18n');
|
||||
select has_pk('campsite_type_option_i18n');
|
||||
select col_is_pk('campsite_type_option_i18n', array['campsite_type_option_id', 'lang_tag']);
|
||||
select table_privs_are('campsite_type_option_i18n', 'guest', array['SELECT']);
|
||||
select table_privs_are('campsite_type_option_i18n', 'employee', array['SELECT']);
|
||||
select table_privs_are('campsite_type_option_i18n', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('campsite_type_option_i18n', 'authenticator', array[]::text[]);
|
||||
|
||||
select has_column('campsite_type_option_i18n', 'campsite_type_option_id');
|
||||
select col_is_fk('campsite_type_option_i18n', 'campsite_type_option_id');
|
||||
select fk_ok('campsite_type_option_i18n', 'campsite_type_option_id', 'campsite_type_option', 'campsite_type_option_id');
|
||||
select col_type_is('campsite_type_option_i18n', 'campsite_type_option_id', 'integer');
|
||||
select col_not_null('campsite_type_option_i18n', 'campsite_type_option_id');
|
||||
select col_hasnt_default('campsite_type_option_i18n', 'campsite_type_option_id');
|
||||
|
||||
select has_column('campsite_type_option_i18n', 'lang_tag');
|
||||
select col_is_fk('campsite_type_option_i18n', 'lang_tag');
|
||||
select fk_ok('campsite_type_option_i18n', 'lang_tag', 'language', 'lang_tag');
|
||||
select col_type_is('campsite_type_option_i18n', 'lang_tag', 'text');
|
||||
select col_not_null('campsite_type_option_i18n', 'lang_tag');
|
||||
select col_hasnt_default('campsite_type_option_i18n', 'lang_tag');
|
||||
|
||||
select has_column('campsite_type_option_i18n', 'name');
|
||||
select col_type_is('campsite_type_option_i18n', 'name', 'text');
|
||||
select col_not_null('campsite_type_option_i18n', 'name');
|
||||
select col_hasnt_default('campsite_type_option_i18n', 'name');
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
-- Test edit_campsite_type_option
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(13);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_function('camper', 'edit_campsite_type_option', array['integer', 'text', 'integer', 'integer']);
|
||||
select function_lang_is('camper', 'edit_campsite_type_option', array['integer', 'text', 'integer', 'integer'], 'sql');
|
||||
select function_returns('camper', 'edit_campsite_type_option', array['integer', 'text', 'integer', 'integer'], 'integer');
|
||||
select isnt_definer('camper', 'edit_campsite_type_option', array['integer', 'text', 'integer', 'integer']);
|
||||
select volatility_is('camper', 'edit_campsite_type_option', array['integer', 'text', 'integer', 'integer'], 'volatile');
|
||||
select function_privs_are('camper', 'edit_campsite_type_option', array ['integer', 'text', 'integer', 'integer'], 'guest', array[]::text[]);
|
||||
select function_privs_are('camper', 'edit_campsite_type_option', array ['integer', 'text', 'integer', 'integer'], 'employee', array[]::text[]);
|
||||
select function_privs_are('camper', 'edit_campsite_type_option', array ['integer', 'text', 'integer', 'integer'], 'admin', array['EXECUTE']);
|
||||
select function_privs_are('camper', 'edit_campsite_type_option', array ['integer', 'text', 'integer', 'integer'], 'authenticator', array[]::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate campsite_type_option_i18n cascade;
|
||||
truncate campsite_type_option cascade;
|
||||
truncate campsite_type cascade;
|
||||
truncate media cascade;
|
||||
truncate media_content cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca')
|
||||
;
|
||||
|
||||
insert into media_content (media_type, bytes)
|
||||
values ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffffff","a"};')
|
||||
;
|
||||
|
||||
insert into media (media_id, company_id, original_filename, content_hash)
|
||||
values (2, 1, 'cover2.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};'))
|
||||
;
|
||||
|
||||
insert into campsite_type (campsite_type_id, company_id, media_id, name, description, active, dogs_allowed, max_campers)
|
||||
values (3, 1, 2, 'Type A', '<p>A</p>', true, false, 4)
|
||||
;
|
||||
|
||||
insert into campsite_type_option (campsite_type_option_id, campsite_type_id, name, range)
|
||||
values (4, 3, 'Option 1', '[0, 10]')
|
||||
, (5, 3, 'Option 2', '[5, 15]')
|
||||
;
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_campsite_type_option(4, 'Option A', 1, 11) $$,
|
||||
'Should be able to edit the first option'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_campsite_type_option(5, 'Option B', 6, 14) $$,
|
||||
'Should be able to edit the second option'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select campsite_type_option_id, campsite_type_id, name, range from campsite_type_option $$,
|
||||
$$ values (4, 3, 'Option A', '[1, 11]'::int4range)
|
||||
, (5, 3, 'Option B', '[6, 14]'::int4range)
|
||||
$$,
|
||||
'Should have updated all campsite type options.'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select * from campsite_type_option_i18n $$,
|
||||
'Should not have added any translation for campsite type options.'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,93 @@
|
|||
-- Test set_campsite_type_option_cost
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(13);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_function('camper', 'set_campsite_type_option_cost', array['integer', 'integer', 'text', 'integer']);
|
||||
select function_lang_is('camper', 'set_campsite_type_option_cost', array['integer', 'integer', 'text', 'integer'], 'sql');
|
||||
select function_returns('camper', 'set_campsite_type_option_cost', array['integer', 'integer', 'text', 'integer'], 'void');
|
||||
select isnt_definer('camper', 'set_campsite_type_option_cost', array['integer', 'integer', 'text', 'integer']);
|
||||
select volatility_is('camper', 'set_campsite_type_option_cost', array['integer', 'integer', 'text', 'integer'], 'volatile');
|
||||
select function_privs_are('camper', 'set_campsite_type_option_cost', array ['integer', 'integer', 'text', 'integer'], 'guest', array[]::text[]);
|
||||
select function_privs_are('camper', 'set_campsite_type_option_cost', array ['integer', 'integer', 'text', 'integer'], 'employee', array[]::text[]);
|
||||
select function_privs_are('camper', 'set_campsite_type_option_cost', array ['integer', 'integer', 'text', 'integer'], 'admin', array['EXECUTE']);
|
||||
select function_privs_are('camper', 'set_campsite_type_option_cost', array ['integer', 'integer', 'text', 'integer'], 'authenticator', array[]::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate campsite_type_option_cost cascade;
|
||||
truncate season cascade;
|
||||
truncate campsite_type_option cascade;
|
||||
truncate campsite_type cascade;
|
||||
truncate media cascade;
|
||||
truncate media_content cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca')
|
||||
;
|
||||
|
||||
insert into media_content (media_type, bytes)
|
||||
values ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffffff","a"};')
|
||||
;
|
||||
|
||||
insert into media (media_id, company_id, original_filename, content_hash)
|
||||
values (2, 1, 'cover2.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};'))
|
||||
;
|
||||
|
||||
insert into campsite_type (campsite_type_id, company_id, slug, media_id, name, description, max_campers, dogs_allowed, active)
|
||||
values (3, 1, '87452b88-b48f-48d3-bb6c-0296de64164e', 2, 'Type A', '', 5, false, true)
|
||||
;
|
||||
|
||||
insert into season (season_id, company_id, name)
|
||||
values (4, 1, 'High')
|
||||
, (5, 1, 'Mid')
|
||||
, (6, 1, 'Low')
|
||||
;
|
||||
|
||||
insert into campsite_type_option (campsite_type_option_id, campsite_type_id, name, range)
|
||||
values (7, 3, 'Test Option', '[1, 100]')
|
||||
;
|
||||
|
||||
insert into campsite_type_option_cost (campsite_type_option_id, season_id, cost_per_night)
|
||||
values (7, 4, 44)
|
||||
, (7, 5, 55)
|
||||
;
|
||||
|
||||
select lives_ok(
|
||||
$$ select set_campsite_type_option_cost(7, 4, '12.34') $$,
|
||||
'Should be able to edit the cost for high season'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select set_campsite_type_option_cost(7, 5, '0.0') $$,
|
||||
'Should be able to set the cost for mid season to zero'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select set_campsite_type_option_cost(7, 6, '3.21') $$,
|
||||
'Should be able to set the cost for low season, inserting it.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select campsite_type_option_id, season_id, cost_per_night from campsite_type_option_cost $$,
|
||||
$$ values (7, 4, 1234)
|
||||
, (7, 5, 0)
|
||||
, (7, 6, 321)
|
||||
$$,
|
||||
'Should have updated all option costs.'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,85 @@
|
|||
-- Test translate_campsite_type_option_option
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(13);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_function('camper', 'translate_campsite_type_option', array['integer', 'text', 'text']);
|
||||
select function_lang_is('camper', 'translate_campsite_type_option', array['integer', 'text', 'text'], 'sql');
|
||||
select function_returns('camper', 'translate_campsite_type_option', array['integer', 'text', 'text'], 'void');
|
||||
select isnt_definer('camper', 'translate_campsite_type_option', array['integer', 'text', 'text']);
|
||||
select volatility_is('camper', 'translate_campsite_type_option', array['integer', 'text', 'text'], 'volatile');
|
||||
select function_privs_are('camper', 'translate_campsite_type_option', array['integer', 'text', 'text'], 'guest', array[]::text[]);
|
||||
select function_privs_are('camper', 'translate_campsite_type_option', array['integer', 'text', 'text'], 'employee', array[]::text[]);
|
||||
select function_privs_are('camper', 'translate_campsite_type_option', array['integer', 'text', 'text'], 'admin', array['EXECUTE']);
|
||||
select function_privs_are('camper', 'translate_campsite_type_option', array['integer', 'text', 'text'], 'authenticator', array[]::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate campsite_type_option_i18n cascade;
|
||||
truncate campsite_type_option cascade;
|
||||
truncate campsite_type cascade;
|
||||
truncate media cascade;
|
||||
truncate media_content cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_lang_tag)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca')
|
||||
;
|
||||
|
||||
insert into media_content (media_type, bytes)
|
||||
values ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffffff","a"};')
|
||||
;
|
||||
|
||||
insert into media (media_id, company_id, original_filename, content_hash)
|
||||
values (2, 1, 'cover2.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};'))
|
||||
;
|
||||
|
||||
insert into campsite_type (campsite_type_id, company_id, slug, media_id, name, description, active, dogs_allowed, max_campers)
|
||||
values (3, 1, '87452b88-b48f-48d3-bb6c-0296de64164e', 2, 'Type A', '<p>A</p>', true, false, 4)
|
||||
;
|
||||
|
||||
insert into campsite_type_option (campsite_type_option_id, campsite_type_id, name, range)
|
||||
values (4, 3, 'Option 1', '[1, 1]')
|
||||
, (5, 3, 'Option 2', '[1, 2]')
|
||||
;
|
||||
|
||||
insert into campsite_type_option_i18n (campsite_type_option_id, lang_tag, name)
|
||||
values (5, 'ca', 'opció2')
|
||||
;
|
||||
|
||||
select lives_ok(
|
||||
$$ select translate_campsite_type_option(4, 'ca', 'Opció 1') $$,
|
||||
'Should be able to translate the first option'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select translate_campsite_type_option(5, 'es', 'Opción 2') $$,
|
||||
'Should be able to translate the second option'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select translate_campsite_type_option(5, 'ca', 'Opció 2') $$,
|
||||
'Should be able to overwrite the catalan translation of the second option'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select campsite_type_option_id, lang_tag, name from campsite_type_option_i18n $$,
|
||||
$$ values (4, 'ca', 'Opció 1')
|
||||
, (5, 'ca', 'Opció 2')
|
||||
, (5, 'es', 'Opción 2')
|
||||
$$,
|
||||
'Should have added and updated all translations.'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify camper:add_campsite_type_option on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('camper.add_campsite_type_option(uuid, text, integer, integer)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,19 @@
|
|||
-- Verify camper:campsite_type_option on pg
|
||||
|
||||
begin;
|
||||
|
||||
select campsite_type_option_id
|
||||
, campsite_type_id
|
||||
, name
|
||||
, range
|
||||
from camper.campsite_type_option
|
||||
where false;
|
||||
|
||||
select 1 / count(*) from pg_class where oid = 'camper.campsite_type_option'::regclass and relrowsecurity;
|
||||
select 1 / count(*) from pg_policy where polname = 'guest_ok' and polrelid = 'camper.campsite_type_option'::regclass;
|
||||
select 1 / count(*) from pg_policy where polname = 'insert_to_company' and polrelid = 'camper.campsite_type_option'::regclass;
|
||||
select 1 / count(*) from pg_policy where polname = 'update_company' and polrelid = 'camper.campsite_type_option'::regclass;
|
||||
select 1 / count(*) from pg_policy where polname = 'delete_from_company' and polrelid = 'camper.campsite_type_option'::regclass;
|
||||
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,18 @@
|
|||
-- Verify camper:campsite_type_option_cost on pg
|
||||
|
||||
begin;
|
||||
|
||||
select campsite_type_option_id
|
||||
, season_id
|
||||
, cost_per_night
|
||||
from camper.campsite_type_option_cost
|
||||
where false;
|
||||
|
||||
select 1 / count(*) from pg_class where oid = 'camper.campsite_type_option_cost'::regclass and relrowsecurity;
|
||||
select 1 / count(*) from pg_policy where polname = 'guest_ok' and polrelid = 'camper.campsite_type_option_cost'::regclass;
|
||||
select 1 / count(*) from pg_policy where polname = 'insert_to_company' and polrelid = 'camper.campsite_type_option_cost'::regclass;
|
||||
select 1 / count(*) from pg_policy where polname = 'update_company' and polrelid = 'camper.campsite_type_option_cost'::regclass;
|
||||
select 1 / count(*) from pg_policy where polname = 'delete_from_company' and polrelid = 'camper.campsite_type_option_cost'::regclass;
|
||||
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,11 @@
|
|||
-- Verify camper:campsite_type_option_i18n on pg
|
||||
|
||||
begin;
|
||||
|
||||
select campsite_type_option_id
|
||||
, lang_tag
|
||||
, name
|
||||
from camper.campsite_type_option_i18n
|
||||
where false;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify camper:edit_campsite_type_option on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('camper.edit_campsite_type_option(integer, text, integer, integer)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify camper:set_campsite_type_option_cost on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('camper.set_campsite_type_option_cost(integer, integer, text, integer)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify camper:translate_campsite_type_option on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('camper.translate_campsite_type_option(integer, text, text)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,85 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 jordi fita mas <jordi@tandem.blog>
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
{{ define "title" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/campsite/types.optionForm*/ -}}
|
||||
{{ if .ID}}
|
||||
{{( pgettext "Edit Campsite Type Option" "title" )}}
|
||||
{{ else }}
|
||||
{{( pgettext "New Campsite Type Option" "title" )}}
|
||||
{{ end }}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/campsite/types.optionForm*/ -}}
|
||||
<form
|
||||
{{ if .ID }}
|
||||
data-hx-put="/admin/campsites/types/{{ .TypeSlug }}/options/{{ .ID }}"
|
||||
{{ else }}
|
||||
action="/admin/campsites/types/{{ .TypeSlug }}/options" method="post"
|
||||
{{ end }}
|
||||
>
|
||||
<h2>
|
||||
{{ if .ID }}
|
||||
{{( pgettext "Edit Campsite Type Option" "title" )}}
|
||||
{{ else }}
|
||||
{{( pgettext "New Campsite Type Option" "title" )}}
|
||||
{{ end }}
|
||||
</h2>
|
||||
{{ CSRFInput }}
|
||||
<fieldset>
|
||||
{{ with .Name -}}
|
||||
<label>
|
||||
{{( pgettext "Name" "input")}}<br>
|
||||
<input type="text" name="{{ .Name }}" value="{{ .Val }}"
|
||||
required {{ template "error-attrs" . }}><br>
|
||||
</label>
|
||||
{{ template "error-message" . }}
|
||||
{{- end }}
|
||||
{{ with .Min -}}
|
||||
<label>
|
||||
{{( pgettext "Minimum" "input")}}<br>
|
||||
<input type="number" name="{{ .Name }}" value="{{ .Val }}" min="0"
|
||||
required {{ template "error-attrs" . }}><br>
|
||||
{{ template "error-message" . }}
|
||||
</label>
|
||||
{{- end }}
|
||||
{{ with .Max -}}
|
||||
<label>
|
||||
{{( pgettext "Maximum" "input")}}<br>
|
||||
<input type="number" name="{{ .Name }}" value="{{ .Val }}" min="0"
|
||||
required {{ template "error-attrs" . }}><br>
|
||||
{{ template "error-message" . }}
|
||||
</label>
|
||||
{{- end }}
|
||||
{{ with .Prices }}
|
||||
<fieldset>
|
||||
<legend>{{( pgettext "Prices" "title" )}}</legend>
|
||||
{{ range . }}
|
||||
<fieldset>
|
||||
<legend>{{ .SeasonName }}</legend>
|
||||
{{ with .PricePerNight -}}
|
||||
<label>
|
||||
{{( pgettext "Price per night" "input")}}<br>
|
||||
<input type="number" name="{{ .Name }}" value="{{ .Val }}" min="0" step="0.01"
|
||||
required {{ template "error-attrs" . }}><br>
|
||||
{{ template "error-message" . }}
|
||||
</label>
|
||||
{{- end }}
|
||||
</fieldset>
|
||||
{{- end }}
|
||||
</fieldset>
|
||||
{{- end }}
|
||||
</fieldset>
|
||||
<footer>
|
||||
<button type="submit">
|
||||
{{ if .ID }}
|
||||
{{( pgettext "Update" "action" )}}
|
||||
{{ else }}
|
||||
{{( pgettext "Add" "action" )}}
|
||||
{{ end }}
|
||||
</button>
|
||||
</footer>
|
||||
</form>
|
||||
{{- end }}
|
|
@ -0,0 +1,41 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 jordi fita mas <jordi@tandem.blog>
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
{{ define "title" -}}
|
||||
{{( pgettext "Campsite Type Options" "title" )}}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/campsite/types.optionIndex*/ -}}
|
||||
<a href="/admin/campsites/types/{{ .TypeSlug }}/options/new">{{( pgettext "Add Option" "action" )}}</a>
|
||||
<h2>{{( pgettext "Campsite Type Options" "title" )}}</h2>
|
||||
{{ if .Options -}}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{{( pgettext "Name" "header" )}}</th>
|
||||
<th scope="col">{{( pgettext "Translations" "header" )}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{ range .Options -}}
|
||||
<tr>
|
||||
<td><a href="{{ .URL }}">{{ .Name }}</a></td>
|
||||
<td>
|
||||
{{ range .Translations }}
|
||||
<a
|
||||
{{ if .Missing }}
|
||||
class="missing-translation"
|
||||
{{ end }}
|
||||
href="{{ .URL }}">{{ .Endonym }}</a>
|
||||
{{ end }}
|
||||
</td>
|
||||
</tr>
|
||||
{{- end }}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ else -}}
|
||||
<p>{{( gettext "No campsite type options added yet." )}}</p>
|
||||
{{- end }}
|
||||
{{- end }}
|
|
@ -0,0 +1,35 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 jordi fita mas <jordi@tandem.blog>
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
{{ define "title" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/campsite/types.optionL10nForm*/ -}}
|
||||
{{printf (pgettext "Translate Campsite Type Option to %s" "title") .Locale.Endonym }}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/campsite/types.optionL10nForm*/ -}}
|
||||
<form data-hx-put="/admin/campsites/types/{{ .TypeSlug }}/options/{{ .ID }}/{{ .Locale.Language }}">
|
||||
<h2>
|
||||
{{printf (pgettext "Translate Campsite Type Option to %s" "title") .Locale.Endonym }}
|
||||
</h2>
|
||||
{{ CSRFInput }}
|
||||
<fieldset>
|
||||
{{ with .Name -}}
|
||||
<fieldset>
|
||||
<legend>{{( pgettext "Name" "input")}}</legend>
|
||||
{{( gettext "Source:" )}} {{ .Source }}<br>
|
||||
<label>
|
||||
{{( pgettext "Translation:" "input" )}}
|
||||
<input type="text" name="{{ .Name }}" value="{{ .Val }}"
|
||||
required {{ template "error-attrs" . }}><br>
|
||||
</label>
|
||||
{{ template "error-message" . }}
|
||||
</fieldset>
|
||||
{{- end }}
|
||||
</fieldset>
|
||||
<footer>
|
||||
<button type="submit">{{( pgettext "Translate" "action" )}}</button>
|
||||
</footer>
|
||||
</form>
|
||||
{{- end }}
|
|
@ -15,7 +15,8 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{{( pgettext "Name" "header" )}}</th>
|
||||
<th scope="col">{{( pgettext "Translations" "campsite type" )}}</th>
|
||||
<th scope="col">{{( pgettext "Translations" "header" )}}</th>
|
||||
<th scope="col">{{( pgettext "Options" "header" )}}</th>
|
||||
<th scope="col">{{( pgettext "Active" "campsite type" )}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -32,6 +33,7 @@
|
|||
href="/admin/campsites/types/{{ $type.Slug }}/{{ .Language }}">{{ .Endonym }}</a>
|
||||
{{ end }}
|
||||
</td>
|
||||
<td><a href="/admin/campsites/types/{{ .Slug }}/options">{{( pgettext "Edit Options" "action" )}}</a></td>
|
||||
<td>{{ if .Active }}{{( gettext "Yes" )}}{{ else }}{{( gettext "No" )}}{{ end }}</td>
|
||||
</tr>
|
||||
{{- end }}
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
<tr>
|
||||
<th scope="col">{{( pgettext "Image" "header" )}}</th>
|
||||
<th scope="col">{{( pgettext "Caption" "header" )}}</th>
|
||||
<th scope="col">{{( pgettext "Translations" "campsite type" )}}</th>
|
||||
<th scope="col">{{( pgettext "Actions" "campsite type" )}}</th>
|
||||
<th scope="col">{{( pgettext "Translations" "header" )}}</th>
|
||||
<th scope="col">{{( pgettext "Actions" "header" )}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<tr>
|
||||
<th scope="col">{{( pgettext "Color" "header" )}}</th>
|
||||
<th scope="col">{{( pgettext "Name" "header" )}}</th>
|
||||
<th scope="col">{{( pgettext "Translations" "campsite type" )}}</th>
|
||||
<th scope="col">{{( pgettext "Translations" "header" )}}</th>
|
||||
<th scope="col">{{( pgettext "Active" "season" )}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
<tr>
|
||||
<th scope="col">{{( pgettext "Image" "header" )}}</th>
|
||||
<th scope="col">{{( pgettext "Caption" "header" )}}</th>
|
||||
<th scope="col">{{( pgettext "Translations" "campsite type" )}}</th>
|
||||
<th scope="col">{{( pgettext "Actions" "campsite type" )}}</th>
|
||||
<th scope="col">{{( pgettext "Translations" "header" )}}</th>
|
||||
<th scope="col">{{( pgettext "Actions" "header" )}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -57,8 +57,8 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{{( pgettext "Service" "header" )}}</th>
|
||||
<th scope="col">{{( pgettext "Translations" "campsite type" )}}</th>
|
||||
<th scope="col">{{( pgettext "Actions" "campsite type" )}}</th>
|
||||
<th scope="col">{{( pgettext "Translations" "header" )}}</th>
|
||||
<th scope="col">{{( pgettext "Actions" "header" )}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
{{ .SeasonName }}
|
||||
</dt>
|
||||
<dd>{{ printf (gettext "%s €/night") .PricePerNight }}</dd>
|
||||
{{ range .Options }}
|
||||
<dd>{{ printf (gettext "%s: %s €/night") .OptionName .PricePerNight }}</dd>
|
||||
{{- end }}
|
||||
{{ if gt .MinNights 1 -}}
|
||||
<dd>{{ printf (gettext "*Minimum %d nights per stay") .MinNights }}</dd>
|
||||
{{- end }}
|
||||
|
|
Loading…
Reference in New Issue