Add the form to update company’s tax details
It is inside the “user menu” only because this is where Numerus has the same option, although it makes less sense in this case, because Numerus is geared toward individual freelancers while Camper is for companies. But, since it is easy to change afterward, this will do for now. However, it should be only shown to admin users, because regular employees have no UPDATE privilege on the company relation. Thus, the need for a new template function to check if the user is admin. Part of #17.
This commit is contained in:
parent
93364b896c
commit
50fbfce9ee
|
@ -0,0 +1,23 @@
|
||||||
|
-- Deploy camper:input_is_valid to pg
|
||||||
|
-- requires: roles
|
||||||
|
-- requires: schema_public
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
set search_path to public;
|
||||||
|
|
||||||
|
create or replace function input_is_valid(input text, domname text) returns boolean as
|
||||||
|
$$
|
||||||
|
begin
|
||||||
|
begin
|
||||||
|
execute format('select %L::%s', input, domname);
|
||||||
|
return true;
|
||||||
|
exception when others then
|
||||||
|
return false;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
$$
|
||||||
|
language plpgsql
|
||||||
|
stable;
|
||||||
|
|
||||||
|
commit;
|
|
@ -0,0 +1,24 @@
|
||||||
|
-- Deploy camper:input_is_valid_phone to pg
|
||||||
|
-- requires: roles
|
||||||
|
-- requires: schema_public
|
||||||
|
-- requires: extension_pg_libphonenumber
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
set search_path to public;
|
||||||
|
|
||||||
|
create or replace function input_is_valid_phone(phone text, country text) returns boolean as
|
||||||
|
$$
|
||||||
|
begin
|
||||||
|
begin
|
||||||
|
perform parse_packed_phone_number(phone, country);
|
||||||
|
return true;
|
||||||
|
exception when others then
|
||||||
|
return false;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
$$
|
||||||
|
language plpgsql
|
||||||
|
stable;
|
||||||
|
|
||||||
|
commit;
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"dev.tandem.ws/tandem/camper/pkg/auth"
|
"dev.tandem.ws/tandem/camper/pkg/auth"
|
||||||
"dev.tandem.ws/tandem/camper/pkg/campsite"
|
"dev.tandem.ws/tandem/camper/pkg/campsite"
|
||||||
|
"dev.tandem.ws/tandem/camper/pkg/company"
|
||||||
"dev.tandem.ws/tandem/camper/pkg/database"
|
"dev.tandem.ws/tandem/camper/pkg/database"
|
||||||
httplib "dev.tandem.ws/tandem/camper/pkg/http"
|
httplib "dev.tandem.ws/tandem/camper/pkg/http"
|
||||||
"dev.tandem.ws/tandem/camper/pkg/template"
|
"dev.tandem.ws/tandem/camper/pkg/template"
|
||||||
|
@ -17,11 +18,13 @@ import (
|
||||||
|
|
||||||
type adminHandler struct {
|
type adminHandler struct {
|
||||||
campsite *campsite.AdminHandler
|
campsite *campsite.AdminHandler
|
||||||
|
company *company.AdminHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAdminHandler() *adminHandler {
|
func newAdminHandler() *adminHandler {
|
||||||
return &adminHandler{
|
return &adminHandler{
|
||||||
campsite: campsite.NewAdminHandler(),
|
campsite: campsite.NewAdminHandler(),
|
||||||
|
company: company.NewAdminHandler(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,6 +46,8 @@ func (h *adminHandler) Handle(user *auth.User, company *auth.Company, conn *data
|
||||||
switch head {
|
switch head {
|
||||||
case "campsites":
|
case "campsites":
|
||||||
h.campsite.Handler(user, company, conn).ServeHTTP(w, r)
|
h.campsite.Handler(user, company, conn).ServeHTTP(w, r)
|
||||||
|
case "company":
|
||||||
|
h.company.Handler(user, company, conn).ServeHTTP(w, r)
|
||||||
case "":
|
case "":
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case http.MethodGet:
|
case http.MethodGet:
|
||||||
|
|
|
@ -44,3 +44,7 @@ func (user *User) IsEmployee() bool {
|
||||||
role := user.Role[0]
|
role := user.Role[0]
|
||||||
return role == 'e' || role == 'a'
|
return role == 'e' || role == 'a'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (user *User) IsAdmin() bool {
|
||||||
|
return user.Role[0] == 'a'
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,283 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 jordi fita mas <jfita@peritasoft.com>
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package company
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdminHandler struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdminHandler() *AdminHandler {
|
||||||
|
return &AdminHandler{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *AdminHandler) Handler(user *auth.User, company *auth.Company, conn *database.Conn) 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 := newTaxDetailsForm(r.Context(), conn, user.Locale)
|
||||||
|
if err := f.FillFromDatabase(r.Context(), company, conn); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
f.MustRender(w, r, user, company)
|
||||||
|
case http.MethodPut:
|
||||||
|
editTaxDetails(w, r, user, company, conn)
|
||||||
|
default:
|
||||||
|
httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPut)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
http.NotFound(w, r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type taxDetailsForm struct {
|
||||||
|
BusinessName *form.Input
|
||||||
|
VATIN *form.Input
|
||||||
|
TradeName *form.Input
|
||||||
|
Phone *form.Input
|
||||||
|
Email *form.Input
|
||||||
|
Web *form.Input
|
||||||
|
Address *form.Input
|
||||||
|
City *form.Input
|
||||||
|
Province *form.Input
|
||||||
|
PostalCode *form.Input
|
||||||
|
Country *form.Select
|
||||||
|
Currency *form.Select
|
||||||
|
DefaultLanguage *form.Select
|
||||||
|
InvoiceNumberFormat *form.Input
|
||||||
|
LegalDisclaimer *form.Input
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTaxDetailsForm(ctx context.Context, conn *database.Conn, l *locale.Locale) *taxDetailsForm {
|
||||||
|
return &taxDetailsForm{
|
||||||
|
BusinessName: &form.Input{
|
||||||
|
Name: "business_name",
|
||||||
|
},
|
||||||
|
VATIN: &form.Input{
|
||||||
|
Name: "vatin",
|
||||||
|
},
|
||||||
|
TradeName: &form.Input{
|
||||||
|
Name: "trade_name",
|
||||||
|
},
|
||||||
|
Phone: &form.Input{
|
||||||
|
Name: "phone",
|
||||||
|
},
|
||||||
|
Email: &form.Input{
|
||||||
|
Name: "email",
|
||||||
|
},
|
||||||
|
Web: &form.Input{
|
||||||
|
Name: "web",
|
||||||
|
},
|
||||||
|
Address: &form.Input{
|
||||||
|
Name: "address",
|
||||||
|
},
|
||||||
|
City: &form.Input{
|
||||||
|
Name: "city",
|
||||||
|
},
|
||||||
|
Province: &form.Input{
|
||||||
|
Name: "province",
|
||||||
|
},
|
||||||
|
PostalCode: &form.Input{
|
||||||
|
Name: "postal_code",
|
||||||
|
},
|
||||||
|
Country: &form.Select{
|
||||||
|
Name: "country",
|
||||||
|
Options: form.MustGetOptions(ctx, conn, "select country.country_code, coalesce(i18n.name, country.name) as l10n_name from country left join country_i18n as i18n on country.country_code = i18n.country_code and i18n.lang_tag = $1 order by l10n_name", l.Language),
|
||||||
|
},
|
||||||
|
Currency: &form.Select{
|
||||||
|
Name: "currency",
|
||||||
|
Options: form.MustGetOptions(ctx, conn, "select currency_code, currency_symbol from currency order by currency_code"),
|
||||||
|
},
|
||||||
|
DefaultLanguage: &form.Select{
|
||||||
|
Name: "default_language",
|
||||||
|
Options: form.MustGetOptions(ctx, conn, "select lang_tag, endonym from language where selectable"),
|
||||||
|
},
|
||||||
|
InvoiceNumberFormat: &form.Input{
|
||||||
|
Name: "invoice_number_format",
|
||||||
|
},
|
||||||
|
LegalDisclaimer: &form.Input{
|
||||||
|
Name: "legal_disclaimer",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *taxDetailsForm) FillFromDatabase(ctx context.Context, company *auth.Company, conn *database.Conn) error {
|
||||||
|
return conn.QueryRow(ctx, `
|
||||||
|
select business_name
|
||||||
|
, substr(vatin::text, 3)
|
||||||
|
, trade_name
|
||||||
|
, phone
|
||||||
|
, email
|
||||||
|
, web
|
||||||
|
, address
|
||||||
|
, city
|
||||||
|
, province
|
||||||
|
, postal_code
|
||||||
|
, array[country_code::text]
|
||||||
|
, array[currency_code::text]
|
||||||
|
, array[default_lang_tag]
|
||||||
|
, invoice_number_format
|
||||||
|
, legal_disclaimer
|
||||||
|
from company
|
||||||
|
where company.company_id = $1`, company.ID).Scan(
|
||||||
|
&f.BusinessName.Val,
|
||||||
|
&f.VATIN.Val,
|
||||||
|
&f.TradeName.Val,
|
||||||
|
&f.Phone.Val,
|
||||||
|
&f.Email.Val,
|
||||||
|
&f.Web.Val,
|
||||||
|
&f.Address.Val,
|
||||||
|
&f.City.Val,
|
||||||
|
&f.Province.Val,
|
||||||
|
&f.PostalCode.Val,
|
||||||
|
&f.Country.Selected,
|
||||||
|
&f.Currency.Selected,
|
||||||
|
&f.DefaultLanguage.Selected,
|
||||||
|
&f.InvoiceNumberFormat.Val,
|
||||||
|
&f.LegalDisclaimer.Val,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *taxDetailsForm) Parse(r *http.Request) error {
|
||||||
|
if err := r.ParseForm(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.BusinessName.FillValue(r)
|
||||||
|
f.VATIN.FillValue(r)
|
||||||
|
f.TradeName.FillValue(r)
|
||||||
|
f.Phone.FillValue(r)
|
||||||
|
f.Email.FillValue(r)
|
||||||
|
f.Web.FillValue(r)
|
||||||
|
f.Address.FillValue(r)
|
||||||
|
f.City.FillValue(r)
|
||||||
|
f.Province.FillValue(r)
|
||||||
|
f.PostalCode.FillValue(r)
|
||||||
|
f.Country.FillValue(r)
|
||||||
|
f.Currency.FillValue(r)
|
||||||
|
f.DefaultLanguage.FillValue(r)
|
||||||
|
f.InvoiceNumberFormat.FillValue(r)
|
||||||
|
f.LegalDisclaimer.FillValue(r)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *taxDetailsForm) Valid(ctx context.Context, conn *database.Conn, l *locale.Locale) (bool, error) {
|
||||||
|
v := form.NewValidator(l)
|
||||||
|
|
||||||
|
var country string
|
||||||
|
if v.CheckSelectedOptions(f.Country, l.GettextNoop("Selected country is not valid.")) {
|
||||||
|
country = f.Country.Selected[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.CheckRequired(f.BusinessName, l.GettextNoop("Business name can not be empty.")) {
|
||||||
|
v.CheckMinLength(f.BusinessName, 2, l.GettextNoop("Business name must have at least two letters."))
|
||||||
|
}
|
||||||
|
if v.CheckRequired(f.VATIN, l.GettextNoop("VAT number can not be empty.")) {
|
||||||
|
if _, err := v.CheckValidVATIN(ctx, conn, f.VATIN, country, l.GettextNoop("This VAT number is not valid.")); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v.CheckRequired(f.Phone, l.GettextNoop("Phone can not be empty.")) {
|
||||||
|
if _, err := v.CheckValidPhone(ctx, conn, f.Phone, country, l.GettextNoop("This phone number is not valid.")); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v.CheckRequired(f.Email, l.GettextNoop("Email can not be empty.")) {
|
||||||
|
v.CheckValidEmail(f.Email, l.GettextNoop("This email is not valid. It should be like name@domain.com."))
|
||||||
|
}
|
||||||
|
if f.Web.Val != "" {
|
||||||
|
v.CheckValidURL(f.Web, l.GettextNoop("This web address is not valid. It should be like https://domain.com/."))
|
||||||
|
}
|
||||||
|
v.CheckRequired(f.Address, l.GettextNoop("Address can not be empty."))
|
||||||
|
v.CheckRequired(f.City, l.GettextNoop("City can not be empty."))
|
||||||
|
v.CheckRequired(f.Province, l.GettextNoop("Province can not be empty."))
|
||||||
|
if v.CheckRequired(f.PostalCode, l.GettextNoop("Postal code can not be empty.")) {
|
||||||
|
if _, err := v.CheckValidPostalCode(ctx, conn, f.PostalCode, country, l.GettextNoop("This postal code is not valid.")); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v.CheckSelectedOptions(f.Currency, l.GettextNoop("Selected currency is not valid."))
|
||||||
|
v.CheckSelectedOptions(f.DefaultLanguage, l.GettextNoop("Selected language is not valid."))
|
||||||
|
v.CheckRequired(f.InvoiceNumberFormat, l.GettextNoop("Invoice number format can not be empty."))
|
||||||
|
|
||||||
|
return v.AllOK, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *taxDetailsForm) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
|
||||||
|
template.MustRenderAdmin(w, r, user, company, "taxDetails.gohtml", f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func editTaxDetails(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
|
||||||
|
f := newTaxDetailsForm(r.Context(), conn, user.Locale)
|
||||||
|
if err := f.Parse(r); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := user.VerifyCSRFToken(r); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ok, err := f.Valid(r.Context(), conn, user.Locale); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else if !ok {
|
||||||
|
if !httplib.IsHTMxRequest(r) {
|
||||||
|
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||||
|
}
|
||||||
|
f.MustRender(w, r, user, company)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
conn.MustExec(r.Context(), `
|
||||||
|
update company
|
||||||
|
set business_name = $1
|
||||||
|
, vatin = ($11 || $2)::vatin
|
||||||
|
, trade_name = $3
|
||||||
|
, phone = parse_packed_phone_number($4, $11)
|
||||||
|
, email = $5
|
||||||
|
, web = $6
|
||||||
|
, address = $7
|
||||||
|
, city = $8
|
||||||
|
, province = $9
|
||||||
|
, postal_code = $10
|
||||||
|
, country_code = $11
|
||||||
|
, currency_code = $12
|
||||||
|
, default_lang_tag = $13
|
||||||
|
, invoice_number_format = $14
|
||||||
|
, legal_disclaimer = $15
|
||||||
|
where company_id = $16
|
||||||
|
`,
|
||||||
|
f.BusinessName,
|
||||||
|
f.VATIN,
|
||||||
|
f.TradeName,
|
||||||
|
f.Phone,
|
||||||
|
f.Email,
|
||||||
|
f.Web,
|
||||||
|
f.Address,
|
||||||
|
f.City,
|
||||||
|
f.Province,
|
||||||
|
f.PostalCode,
|
||||||
|
f.Country,
|
||||||
|
f.Currency,
|
||||||
|
f.DefaultLanguage,
|
||||||
|
f.InvoiceNumberFormat,
|
||||||
|
f.LegalDisclaimer,
|
||||||
|
company.ID)
|
||||||
|
httplib.Redirect(w, r, "/admin/company", http.StatusSeeOther)
|
||||||
|
}
|
|
@ -86,3 +86,11 @@ func (c *Conn) MustGetText(ctx context.Context, sql string, args ...interface{})
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Conn) GetBool(ctx context.Context, sql string, args ...interface{}) (bool, error) {
|
||||||
|
var result bool
|
||||||
|
if err := c.QueryRow(ctx, sql, args...).Scan(&result); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
|
@ -6,9 +6,14 @@
|
||||||
package form
|
package form
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"dev.tandem.ws/tandem/camper/pkg/locale"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"dev.tandem.ws/tandem/camper/pkg/database"
|
||||||
|
"dev.tandem.ws/tandem/camper/pkg/locale"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Validator struct {
|
type Validator struct {
|
||||||
|
@ -27,11 +32,48 @@ func (v *Validator) CheckRequired(input *Input, message string) bool {
|
||||||
return v.check(input, input.Val != "", message)
|
return v.check(input, input.Val != "", message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Validator) CheckMinLength(input *Input, min int, message string) bool {
|
||||||
|
return v.check(input, len(input.Val) >= min, message)
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Validator) CheckValidEmail(input *Input, message string) bool {
|
func (v *Validator) CheckValidEmail(input *Input, message string) bool {
|
||||||
_, err := mail.ParseAddress(input.Val)
|
_, err := mail.ParseAddress(input.Val)
|
||||||
return v.check(input, err == nil, message)
|
return v.check(input, err == nil, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Validator) CheckValidURL(input *Input, message string) bool {
|
||||||
|
_, err := url.Parse(input.Val)
|
||||||
|
return v.check(input, err == nil, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Validator) CheckValidVATIN(ctx context.Context, conn *database.Conn, input *Input, country string, message string) (bool, error) {
|
||||||
|
b, err := conn.GetBool(ctx, "select input_is_valid($1 || $2, 'vatin')", country, input.Val)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return v.check(input, b, message), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Validator) CheckValidPhone(ctx context.Context, conn *database.Conn, input *Input, country string, message string) (bool, error) {
|
||||||
|
b, err := conn.GetBool(ctx, "select input_is_valid_phone($1, $2)", input.Val, country)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return v.check(input, b, message), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Validator) CheckValidPostalCode(ctx context.Context, conn *database.Conn, input *Input, country string, message string) (bool, error) {
|
||||||
|
pattern, err := conn.GetText(ctx, "select '^' || postal_code_regex || '$' from country where country_code = $1", country)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
match, err := regexp.MatchString(pattern, input.Val)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return v.check(input, match, message), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Validator) CheckPasswordConfirmation(password *Input, confirm *Input, message string) bool {
|
func (v *Validator) CheckPasswordConfirmation(password *Input, confirm *Input, message string) bool {
|
||||||
return v.check(confirm, password.Val == confirm.Val, message)
|
return v.check(confirm, password.Val == confirm.Val, message)
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ func mustRenderLayout(w io.Writer, user *auth.User, company *auth.Company, templ
|
||||||
"isLoggedIn": func() bool {
|
"isLoggedIn": func() bool {
|
||||||
return user.LoggedIn
|
return user.LoggedIn
|
||||||
},
|
},
|
||||||
|
"isAdmin": user.IsAdmin,
|
||||||
"CSRFHeader": func() string {
|
"CSRFHeader": func() string {
|
||||||
return fmt.Sprintf(`"%s": "%s"`, auth.CSRFTokenHeader, user.CSRFToken)
|
return fmt.Sprintf(`"%s": "%s"`, auth.CSRFTokenHeader, user.CSRFToken)
|
||||||
},
|
},
|
||||||
|
|
154
po/ca.po
154
po/ca.po
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: camper\n"
|
"Project-Id-Version: camper\n"
|
||||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||||
"POT-Creation-Date: 2023-08-14 20:09+0200\n"
|
"POT-Creation-Date: 2023-08-15 22:22+0200\n"
|
||||||
"PO-Revision-Date: 2023-07-22 23:45+0200\n"
|
"PO-Revision-Date: 2023-07-22 23:45+0200\n"
|
||||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||||
"Language-Team: Catalan <ca@dodds.net>\n"
|
"Language-Team: Catalan <ca@dodds.net>\n"
|
||||||
|
@ -164,7 +164,7 @@ msgid "No campsite types added yet."
|
||||||
msgstr "No s’ha afegit cap tipus d’allotjament encara."
|
msgstr "No s’ha afegit cap tipus d’allotjament encara."
|
||||||
|
|
||||||
#: web/templates/admin/dashboard.gohtml:6
|
#: web/templates/admin/dashboard.gohtml:6
|
||||||
#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:44
|
#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:49
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Dashboard"
|
msgid "Dashboard"
|
||||||
msgstr "Tauler"
|
msgstr "Tauler"
|
||||||
|
@ -175,6 +175,7 @@ msgid "Login"
|
||||||
msgstr "Entrada"
|
msgstr "Entrada"
|
||||||
|
|
||||||
#: web/templates/admin/login.gohtml:22 web/templates/admin/profile.gohtml:35
|
#: web/templates/admin/login.gohtml:22 web/templates/admin/profile.gohtml:35
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:50
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Correu-e"
|
msgstr "Correu-e"
|
||||||
|
@ -216,25 +217,103 @@ msgid "Language"
|
||||||
msgstr "Idioma"
|
msgstr "Idioma"
|
||||||
|
|
||||||
#: web/templates/admin/profile.gohtml:75
|
#: web/templates/admin/profile.gohtml:75
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:144
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Save changes"
|
msgid "Save changes"
|
||||||
msgstr "Desa els canvis"
|
msgstr "Desa els canvis"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:6
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:12
|
||||||
|
msgctxt "title"
|
||||||
|
msgid "Tax Details"
|
||||||
|
msgstr "Configuració fiscal"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:17
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:58
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Business Name"
|
||||||
|
msgstr "Nom de l’empresa"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:26
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "VAT Number"
|
||||||
|
msgstr "NIF"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:34
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Trade Name"
|
||||||
|
msgstr "Nom comercial"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:42
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Phone"
|
||||||
|
msgstr "Telèfon"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:66
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Address"
|
||||||
|
msgstr "Adreça"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:74
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "City"
|
||||||
|
msgstr "Població"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:82
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Province"
|
||||||
|
msgstr "Província"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:90
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Postal Code"
|
||||||
|
msgstr "Codi postal"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:98
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Country"
|
||||||
|
msgstr "País"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:108
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Currency"
|
||||||
|
msgstr "Moneda"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:118
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Default Language"
|
||||||
|
msgstr "Idioma per defecte"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:128
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Invoice Number Format"
|
||||||
|
msgstr "Format del número de factura"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:136
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Legal Disclaimer"
|
||||||
|
msgstr "Nota legal"
|
||||||
|
|
||||||
#: web/templates/admin/layout.gohtml:25
|
#: web/templates/admin/layout.gohtml:25
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "User Menu"
|
msgid "User Menu"
|
||||||
msgstr "Menú d’usuari"
|
msgstr "Menú d’usuari"
|
||||||
|
|
||||||
#: web/templates/admin/layout.gohtml:33
|
#: web/templates/admin/layout.gohtml:33
|
||||||
|
msgctxt "title"
|
||||||
|
msgid "Company Settings"
|
||||||
|
msgstr "Paràmetres de l’empresa"
|
||||||
|
|
||||||
|
#: web/templates/admin/layout.gohtml:38
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Logout"
|
msgid "Logout"
|
||||||
msgstr "Surt"
|
msgstr "Surt"
|
||||||
|
|
||||||
#: pkg/app/login.go:56 pkg/app/user.go:246
|
#: pkg/app/login.go:56 pkg/app/user.go:246 pkg/company/admin.go:203
|
||||||
msgid "Email can not be empty."
|
msgid "Email can not be empty."
|
||||||
msgstr "No podeu deixar el correu-e en blanc."
|
msgstr "No podeu deixar el correu-e en blanc."
|
||||||
|
|
||||||
#: pkg/app/login.go:57 pkg/app/user.go:247
|
#: pkg/app/login.go:57 pkg/app/user.go:247 pkg/company/admin.go:204
|
||||||
msgid "This email is not valid. It should be like name@domain.com."
|
msgid "This email is not valid. It should be like name@domain.com."
|
||||||
msgstr "Aquest correu-e no és vàlid. Hauria de ser similar a nom@domini.com."
|
msgstr "Aquest correu-e no és vàlid. Hauria de ser similar a nom@domini.com."
|
||||||
|
|
||||||
|
@ -259,7 +338,7 @@ msgstr "No podeu deixar el nom en blanc."
|
||||||
msgid "Confirmation does not match password."
|
msgid "Confirmation does not match password."
|
||||||
msgstr "La confirmació no es correspon amb la contrasenya."
|
msgstr "La confirmació no es correspon amb la contrasenya."
|
||||||
|
|
||||||
#: pkg/app/user.go:251
|
#: pkg/app/user.go:251 pkg/company/admin.go:218
|
||||||
msgid "Selected language is not valid."
|
msgid "Selected language is not valid."
|
||||||
msgstr "L’idioma escollit no és vàlid."
|
msgstr "L’idioma escollit no és vàlid."
|
||||||
|
|
||||||
|
@ -267,7 +346,7 @@ msgstr "L’idioma escollit no és vàlid."
|
||||||
msgid "File must be a valid PNG or JPEG image."
|
msgid "File must be a valid PNG or JPEG image."
|
||||||
msgstr "El fitxer has de ser una imatge PNG o JPEG vàlida."
|
msgstr "El fitxer has de ser una imatge PNG o JPEG vàlida."
|
||||||
|
|
||||||
#: pkg/app/admin.go:37
|
#: pkg/app/admin.go:40
|
||||||
msgid "Access forbidden"
|
msgid "Access forbidden"
|
||||||
msgstr "Accés prohibit"
|
msgstr "Accés prohibit"
|
||||||
|
|
||||||
|
@ -279,6 +358,66 @@ msgstr "El tipus d’allotjament escollit no és vàlid."
|
||||||
msgid "Label can not be empty."
|
msgid "Label can not be empty."
|
||||||
msgstr "No podeu deixar l’etiqueta en blanc."
|
msgstr "No podeu deixar l’etiqueta en blanc."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:186
|
||||||
|
msgid "Selected country is not valid."
|
||||||
|
msgstr "El país escollit no és vàlid."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:190
|
||||||
|
msgid "Business name can not be empty."
|
||||||
|
msgstr "No podeu deixar el nom d’empresa en blanc."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:191
|
||||||
|
msgid "Business name must have at least two letters."
|
||||||
|
msgstr "El nom d’empresa ha de tenir com a mínim dues lletres."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:193
|
||||||
|
msgid "VAT number can not be empty."
|
||||||
|
msgstr "No podeu deixar el NIF en blanc."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:194
|
||||||
|
msgid "This VAT number is not valid."
|
||||||
|
msgstr "Aquest NIF no és vàlid."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:198
|
||||||
|
msgid "Phone can not be empty."
|
||||||
|
msgstr "No podeu deixar el telèfon en blanc."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:199
|
||||||
|
msgid "This phone number is not valid."
|
||||||
|
msgstr "Aquest número de telèfon no és vàlid."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:207
|
||||||
|
msgid "This web address is not valid. It should be like https://domain.com/."
|
||||||
|
msgstr "Aquesta adreça web no és vàlida. Hauria de ser similar a https://domini.com/."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:209
|
||||||
|
msgid "Address can not be empty."
|
||||||
|
msgstr "No podeu deixar l’adreça en blanc."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:210
|
||||||
|
msgid "City can not be empty."
|
||||||
|
msgstr "No podeu deixar la població en blanc."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:211
|
||||||
|
msgid "Province can not be empty."
|
||||||
|
msgstr "No podeu deixar la província en blanc."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:212
|
||||||
|
msgid "Postal code can not be empty."
|
||||||
|
msgstr "No podeu deixar el codi postal en blanc."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:213
|
||||||
|
msgid "This postal code is not valid."
|
||||||
|
msgstr "Aquest codi postal no és vàlid."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:217
|
||||||
|
msgid "Selected currency is not valid."
|
||||||
|
msgstr "La moneda escollida no és vàlida."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:219
|
||||||
|
msgid "Invoice number format can not be empty."
|
||||||
|
msgstr "No podeu deixar el format del número de factura en blanc."
|
||||||
|
|
||||||
#: pkg/auth/user.go:40
|
#: pkg/auth/user.go:40
|
||||||
msgid "Cross-site request forgery detected."
|
msgid "Cross-site request forgery detected."
|
||||||
msgstr "S’ha detectat un intent de falsificació de petició a llocs creuats."
|
msgstr "S’ha detectat un intent de falsificació de petició a llocs creuats."
|
||||||
|
@ -313,6 +452,3 @@ msgstr "S’ha detectat un intent de falsificació de petició a llocs creuats."
|
||||||
|
|
||||||
#~ msgid "No pages added yet."
|
#~ msgid "No pages added yet."
|
||||||
#~ msgstr "No s’ha afegit cap pàgina encara."
|
#~ msgstr "No s’ha afegit cap pàgina encara."
|
||||||
|
|
||||||
#~ msgid "Title can not be empty."
|
|
||||||
#~ msgstr "No podeu deixar el títol en blanc."
|
|
||||||
|
|
154
po/es.po
154
po/es.po
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: camper\n"
|
"Project-Id-Version: camper\n"
|
||||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||||
"POT-Creation-Date: 2023-08-14 20:09+0200\n"
|
"POT-Creation-Date: 2023-08-15 22:23+0200\n"
|
||||||
"PO-Revision-Date: 2023-07-22 23:46+0200\n"
|
"PO-Revision-Date: 2023-07-22 23:46+0200\n"
|
||||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||||
"Language-Team: Spanish <es@tp.org.es>\n"
|
"Language-Team: Spanish <es@tp.org.es>\n"
|
||||||
|
@ -164,7 +164,7 @@ msgid "No campsite types added yet."
|
||||||
msgstr "No se ha añadido ningún tipo de alojamiento todavía."
|
msgstr "No se ha añadido ningún tipo de alojamiento todavía."
|
||||||
|
|
||||||
#: web/templates/admin/dashboard.gohtml:6
|
#: web/templates/admin/dashboard.gohtml:6
|
||||||
#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:44
|
#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:49
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Dashboard"
|
msgid "Dashboard"
|
||||||
msgstr "Panel"
|
msgstr "Panel"
|
||||||
|
@ -175,6 +175,7 @@ msgid "Login"
|
||||||
msgstr "Entrada"
|
msgstr "Entrada"
|
||||||
|
|
||||||
#: web/templates/admin/login.gohtml:22 web/templates/admin/profile.gohtml:35
|
#: web/templates/admin/login.gohtml:22 web/templates/admin/profile.gohtml:35
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:50
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Correo-e"
|
msgstr "Correo-e"
|
||||||
|
@ -216,25 +217,103 @@ msgid "Language"
|
||||||
msgstr "Idioma"
|
msgstr "Idioma"
|
||||||
|
|
||||||
#: web/templates/admin/profile.gohtml:75
|
#: web/templates/admin/profile.gohtml:75
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:144
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Save changes"
|
msgid "Save changes"
|
||||||
msgstr "Guardar los cambios"
|
msgstr "Guardar los cambios"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:6
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:12
|
||||||
|
msgctxt "title"
|
||||||
|
msgid "Tax Details"
|
||||||
|
msgstr "Configuración fiscal"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:17
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:58
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Business Name"
|
||||||
|
msgstr "Nombre de empresa"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:26
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "VAT Number"
|
||||||
|
msgstr "NIF"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:34
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Trade Name"
|
||||||
|
msgstr "Nombre comercial"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:42
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Phone"
|
||||||
|
msgstr "Teléfono"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:66
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Address"
|
||||||
|
msgstr "Dirección"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:74
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "City"
|
||||||
|
msgstr "Población"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:82
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Province"
|
||||||
|
msgstr "Provincia"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:90
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Postal Code"
|
||||||
|
msgstr "Código postal"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:98
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Country"
|
||||||
|
msgstr "País"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:108
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Currency"
|
||||||
|
msgstr "Moneda"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:118
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Default Language"
|
||||||
|
msgstr "Idioma por defecto"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:128
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Invoice Number Format"
|
||||||
|
msgstr "Formato de número de factura"
|
||||||
|
|
||||||
|
#: web/templates/admin/taxDetails.gohtml:136
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Legal Disclaimer"
|
||||||
|
msgstr "Nota legal"
|
||||||
|
|
||||||
#: web/templates/admin/layout.gohtml:25
|
#: web/templates/admin/layout.gohtml:25
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "User Menu"
|
msgid "User Menu"
|
||||||
msgstr "Menú de usuario"
|
msgstr "Menú de usuario"
|
||||||
|
|
||||||
#: web/templates/admin/layout.gohtml:33
|
#: web/templates/admin/layout.gohtml:33
|
||||||
|
msgctxt "title"
|
||||||
|
msgid "Company Settings"
|
||||||
|
msgstr "Parámetros de la empresa"
|
||||||
|
|
||||||
|
#: web/templates/admin/layout.gohtml:38
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Logout"
|
msgid "Logout"
|
||||||
msgstr "Salir"
|
msgstr "Salir"
|
||||||
|
|
||||||
#: pkg/app/login.go:56 pkg/app/user.go:246
|
#: pkg/app/login.go:56 pkg/app/user.go:246 pkg/company/admin.go:203
|
||||||
msgid "Email can not be empty."
|
msgid "Email can not be empty."
|
||||||
msgstr "No podéis dejar el correo-e en blanco."
|
msgstr "No podéis dejar el correo-e en blanco."
|
||||||
|
|
||||||
#: pkg/app/login.go:57 pkg/app/user.go:247
|
#: pkg/app/login.go:57 pkg/app/user.go:247 pkg/company/admin.go:204
|
||||||
msgid "This email is not valid. It should be like name@domain.com."
|
msgid "This email is not valid. It should be like name@domain.com."
|
||||||
msgstr "Este correo-e no es válido. Tiene que ser parecido a nombre@dominio.com."
|
msgstr "Este correo-e no es válido. Tiene que ser parecido a nombre@dominio.com."
|
||||||
|
|
||||||
|
@ -259,7 +338,7 @@ msgstr "No podéis dejar el nombre en blanco."
|
||||||
msgid "Confirmation does not match password."
|
msgid "Confirmation does not match password."
|
||||||
msgstr "La confirmación no se corresponde con la contraseña."
|
msgstr "La confirmación no se corresponde con la contraseña."
|
||||||
|
|
||||||
#: pkg/app/user.go:251
|
#: pkg/app/user.go:251 pkg/company/admin.go:218
|
||||||
msgid "Selected language is not valid."
|
msgid "Selected language is not valid."
|
||||||
msgstr "El idioma escogido no es válido."
|
msgstr "El idioma escogido no es válido."
|
||||||
|
|
||||||
|
@ -267,7 +346,7 @@ msgstr "El idioma escogido no es válido."
|
||||||
msgid "File must be a valid PNG or JPEG image."
|
msgid "File must be a valid PNG or JPEG image."
|
||||||
msgstr "El archivo tiene que ser una imagen PNG o JPEG válida."
|
msgstr "El archivo tiene que ser una imagen PNG o JPEG válida."
|
||||||
|
|
||||||
#: pkg/app/admin.go:37
|
#: pkg/app/admin.go:40
|
||||||
msgid "Access forbidden"
|
msgid "Access forbidden"
|
||||||
msgstr "Acceso prohibido"
|
msgstr "Acceso prohibido"
|
||||||
|
|
||||||
|
@ -279,6 +358,66 @@ msgstr "El tipo de alojamiento escogido no es válido."
|
||||||
msgid "Label can not be empty."
|
msgid "Label can not be empty."
|
||||||
msgstr "No podéis dejar la etiqueta en blanco."
|
msgstr "No podéis dejar la etiqueta en blanco."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:186
|
||||||
|
msgid "Selected country is not valid."
|
||||||
|
msgstr "El país escogido no es válido."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:190
|
||||||
|
msgid "Business name can not be empty."
|
||||||
|
msgstr "No podéis dejar el nombre de empresa en blanco."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:191
|
||||||
|
msgid "Business name must have at least two letters."
|
||||||
|
msgstr "El nombre de la empresa tiene que tener como mínimo dos letras."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:193
|
||||||
|
msgid "VAT number can not be empty."
|
||||||
|
msgstr "No podéis dejar el NIF en blanco."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:194
|
||||||
|
msgid "This VAT number is not valid."
|
||||||
|
msgstr "Este NIF no es válido."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:198
|
||||||
|
msgid "Phone can not be empty."
|
||||||
|
msgstr "No podéis dejar el teléfono en blanco."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:199
|
||||||
|
msgid "This phone number is not valid."
|
||||||
|
msgstr "Este teléfono no es válido."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:207
|
||||||
|
msgid "This web address is not valid. It should be like https://domain.com/."
|
||||||
|
msgstr "Esta dirección web no es válida. Tiene que ser parecido a https://dominio.com/."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:209
|
||||||
|
msgid "Address can not be empty."
|
||||||
|
msgstr "No podéis dejar la dirección en blanco."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:210
|
||||||
|
msgid "City can not be empty."
|
||||||
|
msgstr "No podéis dejar la población en blanco."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:211
|
||||||
|
msgid "Province can not be empty."
|
||||||
|
msgstr "No podéis dejar la provincia en blanco."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:212
|
||||||
|
msgid "Postal code can not be empty."
|
||||||
|
msgstr "No podéis dejar el código postal en blanco."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:213
|
||||||
|
msgid "This postal code is not valid."
|
||||||
|
msgstr "Este código postal no es válido."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:217
|
||||||
|
msgid "Selected currency is not valid."
|
||||||
|
msgstr "La moneda escogida no es válida."
|
||||||
|
|
||||||
|
#: pkg/company/admin.go:219
|
||||||
|
msgid "Invoice number format can not be empty."
|
||||||
|
msgstr "No podéis dejar el formato de número de factura en blanco."
|
||||||
|
|
||||||
#: pkg/auth/user.go:40
|
#: pkg/auth/user.go:40
|
||||||
msgid "Cross-site request forgery detected."
|
msgid "Cross-site request forgery detected."
|
||||||
msgstr "Se ha detectado un intento de falsificación de petición en sitios cruzados."
|
msgstr "Se ha detectado un intento de falsificación de petición en sitios cruzados."
|
||||||
|
@ -313,6 +452,3 @@ msgstr "Se ha detectado un intento de falsificación de petición en sitios cruz
|
||||||
|
|
||||||
#~ msgid "No pages added yet."
|
#~ msgid "No pages added yet."
|
||||||
#~ msgstr "No se ha añadido ninguna página todavía."
|
#~ msgstr "No se ha añadido ninguna página todavía."
|
||||||
|
|
||||||
#~ msgid "Title can not be empty."
|
|
||||||
#~ msgstr "No podéis dejar el título en blanco."
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Revert camper:input_is_valid from pg
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
drop function if exists public.input_is_valid(text, text);
|
||||||
|
|
||||||
|
commit;
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Revert camper:input_is_valid_phone from pg
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
drop function if exists public.input_is_valid_phone(text, text);
|
||||||
|
|
||||||
|
commit;
|
|
@ -45,3 +45,5 @@ edit_campsite_type [roles schema_camper campsite_type company] 2023-08-07T22:21:
|
||||||
campsite [roles schema_camper company campsite_type user_profile] 2023-08-14T10:11:51Z jordi fita mas <jordi@tandem.blog> # Add campsite relation
|
campsite [roles schema_camper company campsite_type user_profile] 2023-08-14T10:11:51Z jordi fita mas <jordi@tandem.blog> # Add campsite relation
|
||||||
add_campsite [roles schema_camper campsite campsite_type] 2023-08-14T17:03:23Z jordi fita mas <jordi@tandem.blog> # Add function to create campsites
|
add_campsite [roles schema_camper campsite campsite_type] 2023-08-14T17:03:23Z jordi fita mas <jordi@tandem.blog> # Add function to create campsites
|
||||||
edit_campsite [roles schema_camper campsite] 2023-08-14T17:28:16Z jordi fita mas <jordi@tandem.blog> # Add function to update campsites
|
edit_campsite [roles schema_camper campsite] 2023-08-14T17:28:16Z jordi fita mas <jordi@tandem.blog> # Add function to update campsites
|
||||||
|
input_is_valid [roles schema_public] 2023-08-15T20:10:59Z jordi fita mas <jordi@tandem.blog> # Add function to check if an input string is valid for a domain
|
||||||
|
input_is_valid_phone [roles schema_public extension_pg_libphonenumber] 2023-08-15T20:15:01Z jordi fita mas <jordi@tandem.blog> # Add function to check if an input string is valid for the phone number domain
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
-- Test input_is_valid
|
||||||
|
set client_min_messages to warning;
|
||||||
|
create extension if not exists pgtap;
|
||||||
|
reset client_min_messages;
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
select plan(28);
|
||||||
|
|
||||||
|
set search_path to camper, public;
|
||||||
|
|
||||||
|
select has_function('public', 'input_is_valid', array ['text', 'text']);
|
||||||
|
select function_lang_is('public', 'input_is_valid', array ['text', 'text'], 'plpgsql');
|
||||||
|
select function_returns('public', 'input_is_valid', array ['text', 'text'], 'boolean');
|
||||||
|
select isnt_definer('public', 'input_is_valid', array ['text', 'text']);
|
||||||
|
select volatility_is('public', 'input_is_valid', array ['text', 'text'], 'stable');
|
||||||
|
select function_privs_are('public', 'input_is_valid', array ['text', 'text'], 'guest', array ['EXECUTE']);
|
||||||
|
select function_privs_are('public', 'input_is_valid', array ['text', 'text'], 'employee', array ['EXECUTE']);
|
||||||
|
select function_privs_are('public', 'input_is_valid', array ['text', 'text'], 'admin', array ['EXECUTE']);
|
||||||
|
select function_privs_are('public', 'input_is_valid', array ['text', 'text'], 'authenticator', array ['EXECUTE']);
|
||||||
|
|
||||||
|
select is( input_is_valid('123', 'integer'), true );
|
||||||
|
select is( input_is_valid('abc', 'integer'), false );
|
||||||
|
select is( input_is_valid('abc', 'email'), false );
|
||||||
|
select is( input_is_valid('ESabc', 'vatin'), false );
|
||||||
|
select is( input_is_valid('abc', 'text'), true );
|
||||||
|
select is( input_is_valid('ES44444444A', 'vatin'), true );
|
||||||
|
select is( input_is_valid('ES44444444A', 'text'), true );
|
||||||
|
select is( input_is_valid('ES44444444A', 'email'), false );
|
||||||
|
select is( input_is_valid('NL04RABO9373475770', 'text'), true );
|
||||||
|
select is( input_is_valid('ESNL04RABO9373475770', 'vatin'), false );
|
||||||
|
select is( input_is_valid('NL04RABO9373475770', 'email'), false );
|
||||||
|
select is( input_is_valid('ARBNNL22', 'text'), true );
|
||||||
|
select is( input_is_valid('ESARBNNL22', 'vatin'), false );
|
||||||
|
select is( input_is_valid('ARBNNL22', 'email'), false );
|
||||||
|
select is( input_is_valid('2023-05-12', 'text'), true );
|
||||||
|
select is( input_is_valid('2023-05-12', 'date'), true );
|
||||||
|
select is( input_is_valid('2023-05-12', 'integer'), false );
|
||||||
|
select is( input_is_valid('', 'text'), true );
|
||||||
|
select is( input_is_valid('', 'inexistent'), false );
|
||||||
|
|
||||||
|
select *
|
||||||
|
from finish();
|
||||||
|
|
||||||
|
rollback;
|
|
@ -0,0 +1,32 @@
|
||||||
|
-- Test input_is_valid_phone
|
||||||
|
set client_min_messages to warning;
|
||||||
|
create extension if not exists pgtap;
|
||||||
|
reset client_min_messages;
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
|
||||||
|
select plan(12);
|
||||||
|
|
||||||
|
set search_path to camper, public;
|
||||||
|
|
||||||
|
select has_function('public', 'input_is_valid_phone', array ['text', 'text']);
|
||||||
|
select function_lang_is('public', 'input_is_valid_phone', array ['text', 'text'], 'plpgsql');
|
||||||
|
select function_returns('public', 'input_is_valid_phone', array ['text', 'text'], 'boolean');
|
||||||
|
select isnt_definer('public', 'input_is_valid_phone', array ['text', 'text']);
|
||||||
|
select volatility_is('public', 'input_is_valid_phone', array ['text', 'text'], 'stable');
|
||||||
|
select function_privs_are('public', 'input_is_valid_phone', array ['text', 'text'], 'guest', array ['EXECUTE']);
|
||||||
|
select function_privs_are('public', 'input_is_valid_phone', array ['text', 'text'], 'employee', array ['EXECUTE']);
|
||||||
|
select function_privs_are('public', 'input_is_valid_phone', array ['text', 'text'], 'admin', array ['EXECUTE']);
|
||||||
|
select function_privs_are('public', 'input_is_valid_phone', array ['text', 'text'], 'authenticator', array ['EXECUTE']);
|
||||||
|
|
||||||
|
|
||||||
|
select is( input_is_valid_phone('555-555-5555', 'US'), true );
|
||||||
|
select is( input_is_valid_phone('555-555-5555555555', 'US'), false );
|
||||||
|
select is( input_is_valid_phone('555-555-55555555555', 'US'), false );
|
||||||
|
|
||||||
|
|
||||||
|
select *
|
||||||
|
from finish();
|
||||||
|
|
||||||
|
rollback;
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Verify camper:input_is_valid on pg
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
select has_function_privilege('public.input_is_valid(text, text)', 'execute');
|
||||||
|
|
||||||
|
rollback;
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Verify camper:input_is_valid_phone on pg
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
select has_function_privilege('public.input_is_valid_phone(text, text)', 'execute');
|
||||||
|
|
||||||
|
rollback;
|
|
@ -28,6 +28,11 @@
|
||||||
<li class="icon_profile">
|
<li class="icon_profile">
|
||||||
<a href="/me">{{( pgettext "Profile" "title" )}}</a>
|
<a href="/me">{{( pgettext "Profile" "title" )}}</a>
|
||||||
</li>
|
</li>
|
||||||
|
{{ if isAdmin -}}
|
||||||
|
<li class="icon_company">
|
||||||
|
<a href="/admin/company">{{( pgettext "Company Settings" "title" )}}</a>
|
||||||
|
</li>
|
||||||
|
{{- end }}
|
||||||
<li class="icon_logout">
|
<li class="icon_logout">
|
||||||
<button data-hx-delete="/me/session" data-hx-headers='{ {{ CSRFHeader }} }'
|
<button data-hx-delete="/me/session" data-hx-headers='{ {{ CSRFHeader }} }'
|
||||||
>{{( pgettext "Logout" "action" )}}</button>
|
>{{( pgettext "Logout" "action" )}}</button>
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: 2023 jordi fita mas <jordi@tandem.blog>
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
{{ define "title" -}}
|
||||||
|
{{( pgettext "Tax Details" "title" )}}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ define "content" -}}
|
||||||
|
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/company.taxDetailsForm*/ -}}
|
||||||
|
<form data-hx-put="/admin/company">
|
||||||
|
<h2>{{( pgettext "Tax Details" "title" )}}</h2>
|
||||||
|
{{ CSRFInput }}
|
||||||
|
<fieldset>
|
||||||
|
{{ with .BusinessName -}}
|
||||||
|
<label>
|
||||||
|
{{( pgettext "Business Name" "input")}}<br>
|
||||||
|
<input type="text" name="{{ .Name }}" value="{{ .Val }}"
|
||||||
|
required autocomplete="organization" minlength="2"
|
||||||
|
{{ template "error-attrs" . }}><br>
|
||||||
|
</label>
|
||||||
|
{{ template "error-message" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{ with .VATIN -}}
|
||||||
|
<label>
|
||||||
|
{{( pgettext "VAT Number" "input")}}<br>
|
||||||
|
<input type="text" name="{{ .Name }}" value="{{ .Val }}"
|
||||||
|
required {{ template "error-attrs" . }}><br>
|
||||||
|
</label>
|
||||||
|
{{ template "error-message" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{ with .TradeName -}}
|
||||||
|
<label>
|
||||||
|
{{( pgettext "Trade Name" "input")}}<br>
|
||||||
|
<input type="text" name="{{ .Name }}" value="{{ .Val }}"
|
||||||
|
{{ template "error-attrs" . }}><br>
|
||||||
|
</label>
|
||||||
|
{{ template "error-message" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{ with .Phone -}}
|
||||||
|
<label>
|
||||||
|
{{( pgettext "Phone" "input")}}<br>
|
||||||
|
<input type="tel" name="{{ .Name }}" value="{{ .Val }}"
|
||||||
|
required autocomplete="tel" {{ template "error-attrs" . }}><br>
|
||||||
|
</label>
|
||||||
|
{{ template "error-message" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{ with .Email -}}
|
||||||
|
<label>
|
||||||
|
{{( pgettext "Email" "input")}}<br>
|
||||||
|
<input type="email" name="{{ .Name }}" value="{{ .Val }}"
|
||||||
|
required autocomplete="email" {{ template "error-attrs" . }}><br>
|
||||||
|
</label>
|
||||||
|
{{ template "error-message" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{ with .Web -}}
|
||||||
|
<label>
|
||||||
|
{{( pgettext "Business Name" "input")}}<br>
|
||||||
|
<input type="url" name="{{ .Name }}" value="{{ .Val }}"
|
||||||
|
autocomplete="url" {{ template "error-attrs" . }}><br>
|
||||||
|
</label>
|
||||||
|
{{ template "error-message" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{ with .Address -}}
|
||||||
|
<label>
|
||||||
|
{{( pgettext "Address" "input")}}<br>
|
||||||
|
<input type="text" name="{{ .Name }}" value="{{ .Val }}"
|
||||||
|
required autocomplete="address-line1" {{ template "error-attrs" . }}><br>
|
||||||
|
</label>
|
||||||
|
{{ template "error-message" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{ with .City -}}
|
||||||
|
<label>
|
||||||
|
{{( pgettext "City" "input")}}<br>
|
||||||
|
<input type="text" name="{{ .Name }}" value="{{ .Val }}"
|
||||||
|
required {{ template "error-attrs" . }}><br>
|
||||||
|
</label>
|
||||||
|
{{ template "error-message" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{ with .Province -}}
|
||||||
|
<label>
|
||||||
|
{{( pgettext "Province" "input")}}<br>
|
||||||
|
<input type="text" name="{{ .Name }}" value="{{ .Val }}"
|
||||||
|
required {{ template "error-attrs" . }}><br>
|
||||||
|
</label>
|
||||||
|
{{ template "error-message" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{ with .PostalCode -}}
|
||||||
|
<label>
|
||||||
|
{{( pgettext "Postal Code" "input")}}<br>
|
||||||
|
<input type="text" name="{{ .Name }}" value="{{ .Val }}"
|
||||||
|
required autocomplete="postal-code" {{ template "error-attrs" . }}><br>
|
||||||
|
</label>
|
||||||
|
{{ template "error-message" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{ with .Country -}}
|
||||||
|
<label>
|
||||||
|
{{( pgettext "Country" "input")}}<br>
|
||||||
|
<select name="{{ .Name }}"
|
||||||
|
required autocomplete="country"
|
||||||
|
{{ template "error-attrs" . }}>{{ template "list-options" . }}
|
||||||
|
</select><br>
|
||||||
|
</label>
|
||||||
|
{{ template "error-message" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{ with .Currency -}}
|
||||||
|
<label>
|
||||||
|
{{( pgettext "Currency" "input")}}<br>
|
||||||
|
<select name="{{ .Name }}"
|
||||||
|
required
|
||||||
|
{{ template "error-attrs" . }}>{{ template "list-options" . }}
|
||||||
|
</select><br>
|
||||||
|
</label>
|
||||||
|
{{ template "error-message" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{ with .DefaultLanguage -}}
|
||||||
|
<label>
|
||||||
|
{{( pgettext "Default Language" "input")}}<br>
|
||||||
|
<select name="{{ .Name }}"
|
||||||
|
required
|
||||||
|
{{ template "error-attrs" . }}>{{ template "list-options" . }}
|
||||||
|
</select><br>
|
||||||
|
</label>
|
||||||
|
{{ template "error-message" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{ with .InvoiceNumberFormat -}}
|
||||||
|
<label>
|
||||||
|
{{( pgettext "Invoice Number Format" "input")}}<br>
|
||||||
|
<input type="text" name="{{ .Name }}" value="{{ .Val }}"
|
||||||
|
required {{ template "error-attrs" . }}><br>
|
||||||
|
</label>
|
||||||
|
{{ template "error-message" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{ with .LegalDisclaimer -}}
|
||||||
|
<label>
|
||||||
|
{{( pgettext "Legal Disclaimer" "input")}}<br>
|
||||||
|
<textarea name="{{ .Name }}"
|
||||||
|
{{ template "error-attrs" . }}>{{ .Val }}</textarea><br>
|
||||||
|
</label>
|
||||||
|
{{ template "error-message" . }}
|
||||||
|
{{- end }}
|
||||||
|
</fieldset>
|
||||||
|
<footer>
|
||||||
|
<button type="submit">{{( pgettext "Save changes" "action" )}}</button>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
{{- end }}
|
Loading…
Reference in New Issue