Add the payment form to admin

Had to change setup_redsys because admins can not read the current
encrypt key, thus it is not possible to `set encrypt_key =
coalesce(…, encrypt_key)`.

Not that it did much sense, anyway, as i was already inside the branch
of the if when encrpty_key is null.

However, it seems that this also affects in the `on conflict` update. I
assume this is because `excluded` is some kind of row of the relation
and has the same restrictions.
This commit is contained in:
jordi fita mas 2023-10-27 16:04:43 +02:00
parent 57b4360dfb
commit 0cbf973cbb
10 changed files with 523 additions and 84 deletions

View File

@ -17,8 +17,7 @@ begin
set merchant_code = setup_redsys.merchant_code, set merchant_code = setup_redsys.merchant_code,
terminal_number = setup_redsys.terminal_number, terminal_number = setup_redsys.terminal_number,
environment = setup_redsys.environment, environment = setup_redsys.environment,
integration = setup_redsys.integration, integration = setup_redsys.integration
encrypt_key = coalesce(decode(setup_redsys.encrypt_key, 'base64'), redsys.encrypt_key)
where company_id = company where company_id = company
; ;
else else
@ -29,7 +28,7 @@ begin
terminal_number = excluded.terminal_number, terminal_number = excluded.terminal_number,
environment = excluded.environment, environment = excluded.environment,
integration = excluded.integration, integration = excluded.integration,
encrypt_key = coalesce(excluded.encrypt_key, redsys.encrypt_key) encrypt_key = decode(setup_redsys.encrypt_key, 'base64')
; ;
end if; end if;
end end

View File

@ -6,16 +6,17 @@
package app package app
import ( import (
"dev.tandem.ws/tandem/camper/pkg/media"
"net/http" "net/http"
"dev.tandem.ws/tandem/camper/pkg/auth" "dev.tandem.ws/tandem/camper/pkg/auth"
"dev.tandem.ws/tandem/camper/pkg/booking"
"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/company"
"dev.tandem.ws/tandem/camper/pkg/database" "dev.tandem.ws/tandem/camper/pkg/database"
"dev.tandem.ws/tandem/camper/pkg/home" "dev.tandem.ws/tandem/camper/pkg/home"
httplib "dev.tandem.ws/tandem/camper/pkg/http" httplib "dev.tandem.ws/tandem/camper/pkg/http"
"dev.tandem.ws/tandem/camper/pkg/locale" "dev.tandem.ws/tandem/camper/pkg/locale"
"dev.tandem.ws/tandem/camper/pkg/media"
"dev.tandem.ws/tandem/camper/pkg/season" "dev.tandem.ws/tandem/camper/pkg/season"
"dev.tandem.ws/tandem/camper/pkg/services" "dev.tandem.ws/tandem/camper/pkg/services"
"dev.tandem.ws/tandem/camper/pkg/template" "dev.tandem.ws/tandem/camper/pkg/template"
@ -26,6 +27,7 @@ type adminHandler struct {
company *company.AdminHandler company *company.AdminHandler
home *home.AdminHandler home *home.AdminHandler
media *media.AdminHandler media *media.AdminHandler
payment *booking.AdminHandler
season *season.AdminHandler season *season.AdminHandler
services *services.AdminHandler services *services.AdminHandler
} }
@ -36,6 +38,7 @@ func newAdminHandler(locales locale.Locales, mediaDir string) *adminHandler {
company: company.NewAdminHandler(), company: company.NewAdminHandler(),
home: home.NewAdminHandler(locales), home: home.NewAdminHandler(locales),
media: media.NewAdminHandler(mediaDir), media: media.NewAdminHandler(mediaDir),
payment: booking.NewAdminHandler(),
season: season.NewAdminHandler(locales), season: season.NewAdminHandler(locales),
services: services.NewAdminHandler(locales), services: services.NewAdminHandler(locales),
} }
@ -65,6 +68,8 @@ func (h *adminHandler) Handle(user *auth.User, company *auth.Company, conn *data
h.home.Handler(user, company, conn).ServeHTTP(w, r) h.home.Handler(user, company, conn).ServeHTTP(w, r)
case "media": case "media":
h.media.Handler(user, company, conn).ServeHTTP(w, r) h.media.Handler(user, company, conn).ServeHTTP(w, r)
case "payment":
h.payment.Handler(user, company, conn).ServeHTTP(w, r)
case "seasons": case "seasons":
h.season.Handler(user, company, conn).ServeHTTP(w, r) h.season.Handler(user, company, conn).ServeHTTP(w, r)
case "services": case "services":

171
pkg/booking/admin.go Normal file
View File

@ -0,0 +1,171 @@
/*
* SPDX-FileCopyrightText: 2023 jordi fita mas <jfita@peritasoft.com>
* SPDX-License-Identifier: AGPL-3.0-only
*/
package booking
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 := newPaymentForm(user.Locale)
if err := f.FillFromDatabase(r.Context(), company, conn); err != nil {
panic(err)
}
f.MustRender(w, r, user, company)
case http.MethodPut:
updatePaymentSettings(w, r, user, company, conn)
default:
httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPut)
}
default:
http.NotFound(w, r)
}
})
}
type paymentForm struct {
MerchantCode *form.Input
TerminalNumber *form.Input
Environment *form.Select
Integration *form.Select
EncryptKey *form.Input
}
func newPaymentForm(l *locale.Locale) *paymentForm {
return &paymentForm{
MerchantCode: &form.Input{
Name: "merchant_code",
},
TerminalNumber: &form.Input{
Name: "terminal_number",
},
Environment: &form.Select{
Name: "environment",
Options: []*form.Option{
{
Value: "test",
Label: l.Pgettext("Test", "redsys environment"),
},
{
Value: "live",
Label: l.Pgettext("Live", "redsys environment"),
},
},
},
Integration: &form.Select{
Name: "integration",
Options: []*form.Option{
{
Value: "insite",
Label: l.Pgettext("InSite", "redsys integration"),
},
{
Value: "redirect",
Label: l.Pgettext("Redirect", "redsys integration"),
},
},
},
EncryptKey: &form.Input{
Name: "encrypt_key",
},
}
}
func (f *paymentForm) FillFromDatabase(ctx context.Context, company *auth.Company, conn *database.Conn) error {
return conn.QueryRow(ctx, `
select merchant_code
, terminal_number::text
, array[environment::text]
, array[integration::text]
from redsys
where company_id = $1`, company.ID).Scan(
&f.MerchantCode.Val,
&f.TerminalNumber.Val,
&f.Environment.Selected,
&f.Integration.Selected,
)
}
func (f *paymentForm) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
template.MustRenderAdmin(w, r, user, company, "payment.gohtml", f)
}
func (f *paymentForm) Parse(r *http.Request) error {
if err := r.ParseForm(); err != nil {
return err
}
f.MerchantCode.FillValue(r)
f.TerminalNumber.FillValue(r)
f.Environment.FillValue(r)
f.Integration.FillValue(r)
f.EncryptKey.FillValue(r)
return nil
}
func (f *paymentForm) Valid(l *locale.Locale) bool {
v := form.NewValidator(l)
if v.CheckRequired(f.MerchantCode, l.GettextNoop("Merchant code can not be empty.")) {
if v.CheckExactLength(f.MerchantCode, 9, l.GettextNoop("Merchant code must be exactly nine digits long.")) {
v.CheckValidInteger(f.MerchantCode, l.GettextNoop("Merchant code must be a number."))
}
}
if v.CheckRequired(f.TerminalNumber, l.GettextNoop("Terminal number can not be empty.")) {
message := l.GettextNoop("Terminal number must be a number between 1 and 999.")
if v.CheckValidInteger(f.TerminalNumber, message) {
if v.CheckMinInteger(f.TerminalNumber, 1, message) {
v.CheckMaxInteger(f.TerminalNumber, 999, message)
}
}
}
v.CheckSelectedOptions(f.Environment, l.GettextNoop("Selected environment is not valid."))
v.CheckSelectedOptions(f.Integration, l.GettextNoop("Selected integration is not valid."))
if f.EncryptKey.Val != "" {
v.CheckValidBase64(f.EncryptKey, l.GettextNoop("The merchant key is not valid."))
}
return v.AllOK
}
func updatePaymentSettings(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
f := newPaymentForm(user.Locale)
if ok, err := form.Handle(f, w, r, user); err != nil {
return
} else if !ok {
f.MustRender(w, r, user, company)
return
}
if err := conn.SetupRedsys(r.Context(), company.ID, f.MerchantCode.Val, f.TerminalNumber.Int(), f.Environment.Selected[0], f.Integration.Selected[0], f.EncryptKey.Val); err != nil {
panic(err)
}
httplib.Redirect(w, r, "/admin/payment", http.StatusSeeOther)
}

View File

@ -58,3 +58,12 @@ func (c *Conn) TranslateCampsiteTypeFeature(ctx context.Context, id int, langTag
_, err := c.Exec(ctx, "select translate_campsite_type_feature($1, $2, $3)", id, langTag, name) _, err := c.Exec(ctx, "select translate_campsite_type_feature($1, $2, $3)", id, langTag, name)
return err return err
} }
func (c *Conn) SetupRedsys(ctx context.Context, companyID int, merchantCode string, terminalNumber int, environment string, integration string, encryptKey string) error {
var encryptKeyParam interface{}
if encryptKey != "" {
encryptKeyParam = encryptKey
}
_, err := c.Exec(ctx, "select setup_redsys($1, $2, $3, $4, $5, $6)", companyID, merchantCode, terminalNumber, environment, integration, encryptKeyParam)
return err
}

View File

@ -7,6 +7,7 @@ package form
import ( import (
"context" "context"
"encoding/base64"
"errors" "errors"
"net/mail" "net/mail"
"net/url" "net/url"
@ -38,6 +39,10 @@ func (v *Validator) CheckMinLength(input *Input, min int, message string) bool {
return v.Check(input, len(input.Val) >= min, message) return v.Check(input, len(input.Val) >= min, message)
} }
func (v *Validator) CheckExactLength(input *Input, length int, message string) bool {
return v.Check(input, len(input.Val) == length, message)
}
func (v *Validator) CheckMinInteger(input *Input, min int, message string) bool { func (v *Validator) CheckMinInteger(input *Input, min int, message string) bool {
i, _ := strconv.Atoi(input.Val) i, _ := strconv.Atoi(input.Val)
return v.Check(input, i >= min, message) return v.Check(input, i >= min, message)
@ -58,6 +63,11 @@ func (v *Validator) CheckValidInteger(input *Input, message string) bool {
return v.Check(input, err == nil, message) return v.Check(input, err == nil, message)
} }
func (v *Validator) CheckValidBase64(input *Input, message string) bool {
_, err := base64.StdEncoding.DecodeString(input.Val)
return v.Check(input, err == nil, message)
}
func (v *Validator) CheckValidDecimal(input *Input, message string) bool { func (v *Validator) CheckValidDecimal(input *Input, message string) bool {
_, err := strconv.ParseFloat(input.Val, 64) _, err := strconv.ParseFloat(input.Val, 64)
return v.Check(input, err == nil, message) return v.Check(input, err == nil, message)

163
po/ca.po
View File

@ -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-10-27 01:12+0200\n" "POT-Creation-Date: 2023-10-27 15:59+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"
@ -111,7 +111,7 @@ msgid "Check-out Date"
msgstr "Data de sortida" msgstr "Data de sortida"
#: web/templates/public/campsite/type.gohtml:54 #: web/templates/public/campsite/type.gohtml:54
#: web/templates/public/booking.gohtml:175 #: web/templates/public/booking.gohtml:194
msgctxt "action" msgctxt "action"
msgid "Book" msgid "Book"
msgstr "Reserva" msgstr "Reserva"
@ -289,100 +289,100 @@ msgctxt "input"
msgid "Full name" msgid "Full name"
msgstr "Nom i cognoms" msgstr "Nom i cognoms"
#: web/templates/public/booking.gohtml:26 #: web/templates/public/booking.gohtml:27
msgctxt "input" msgctxt "input"
msgid "Address (optional)" msgid "Address (optional)"
msgstr "Adreça (opcional)" msgstr "Adreça (opcional)"
#: web/templates/public/booking.gohtml:34 #: web/templates/public/booking.gohtml:36
msgctxt "input" msgctxt "input"
msgid "Postcode (optional)" msgid "Postcode (optional)"
msgstr "Codi postal (opcional)" msgstr "Codi postal (opcional)"
#: web/templates/public/booking.gohtml:42 #: web/templates/public/booking.gohtml:45
msgctxt "input" msgctxt "input"
msgid "Town or village (optional)" msgid "Town or village (optional)"
msgstr "Població (opcional)" msgstr "Població (opcional)"
#: web/templates/public/booking.gohtml:50 #: web/templates/public/booking.gohtml:54
#: web/templates/admin/taxDetails.gohtml:98 #: web/templates/admin/taxDetails.gohtml:98
msgctxt "input" msgctxt "input"
msgid "Country" msgid "Country"
msgstr "País" msgstr "País"
#: web/templates/public/booking.gohtml:53 #: web/templates/public/booking.gohtml:57
msgid "Choose a country" msgid "Choose a country"
msgstr "Esculli un país" msgstr "Esculli un país"
#: web/templates/public/booking.gohtml:60 web/templates/admin/login.gohtml:22 #: web/templates/public/booking.gohtml:65 web/templates/admin/login.gohtml:22
#: web/templates/admin/profile.gohtml:35 #: web/templates/admin/profile.gohtml:35
#: web/templates/admin/taxDetails.gohtml:50 #: web/templates/admin/taxDetails.gohtml:50
msgctxt "input" msgctxt "input"
msgid "Email" msgid "Email"
msgstr "Correu-e" msgstr "Correu-e"
#: web/templates/public/booking.gohtml:68 #: web/templates/public/booking.gohtml:74
#: web/templates/admin/taxDetails.gohtml:42 #: web/templates/admin/taxDetails.gohtml:42
msgctxt "input" msgctxt "input"
msgid "Phone" msgid "Phone"
msgstr "Telèfon" msgstr "Telèfon"
#: web/templates/public/booking.gohtml:76 #: web/templates/public/booking.gohtml:83
msgctxt "title" msgctxt "title"
msgid "Party Details" msgid "Party Details"
msgstr "Dades dels visitants" msgstr "Dades dels visitants"
#: web/templates/public/booking.gohtml:79 #: web/templates/public/booking.gohtml:86
msgctxt "input" msgctxt "input"
msgid "Adults" msgid "Adults"
msgstr "Adults" msgstr "Adults"
#: web/templates/public/booking.gohtml:87 #: web/templates/public/booking.gohtml:95
msgctxt "input" msgctxt "input"
msgid "Teenagers (from 11 to 16 years old)" msgid "Teenagers (from 11 to 16 years old)"
msgstr "Adolescents (entre 11 i 16 anys)" msgstr "Adolescents (entre 11 i 16 anys)"
#: web/templates/public/booking.gohtml:95 #: web/templates/public/booking.gohtml:104
msgctxt "input" msgctxt "input"
msgid "Children (up to 10 years old)" msgid "Children (up to 10 years old)"
msgstr "Nens (fins a 10 anys)" msgstr "Nens (fins a 10 anys)"
#: web/templates/public/booking.gohtml:103 #: web/templates/public/booking.gohtml:113
msgctxt "input" msgctxt "input"
msgid "Dogs" msgid "Dogs"
msgstr "Gossos" msgstr "Gossos"
#: web/templates/public/booking.gohtml:111 #: web/templates/public/booking.gohtml:122
msgctxt "title" msgctxt "title"
msgid "Accomodation" msgid "Accomodation"
msgstr "Acomodacions" msgstr "Acomodacions"
#: web/templates/public/booking.gohtml:136 #: web/templates/public/booking.gohtml:150
msgctxt "title" msgctxt "title"
msgid "Booking Period" msgid "Booking Period"
msgstr "Període de reserva" msgstr "Període de reserva"
#: web/templates/public/booking.gohtml:139 #: web/templates/public/booking.gohtml:153
msgctxt "input" msgctxt "input"
msgid "Arrival date" msgid "Arrival date"
msgstr "Data darribada" msgstr "Data darribada"
#: web/templates/public/booking.gohtml:147 #: web/templates/public/booking.gohtml:162
msgctxt "input" msgctxt "input"
msgid "Departure date" msgid "Departure date"
msgstr "Data de sortida" msgstr "Data de sortida"
#: web/templates/public/booking.gohtml:155 #: web/templates/public/booking.gohtml:171
msgctxt "input" msgctxt "input"
msgid "Area preferences (optional)" msgid "Area preferences (optional)"
msgstr "Preferències dàrea (opcional)" msgstr "Preferències dàrea (opcional)"
#: web/templates/public/booking.gohtml:165 #: web/templates/public/booking.gohtml:182
msgctxt "input" msgctxt "input"
msgid "ACSI card? (optional)" msgid "ACSI card? (optional)"
msgstr "Targeta ACSI? (opcional)" msgstr "Targeta ACSI? (opcional)"
#: web/templates/public/booking.gohtml:171 #: web/templates/public/booking.gohtml:189
msgctxt "input" msgctxt "input"
msgid "I have read and I accept the reservation conditions" msgid "I have read and I accept the reservation conditions"
msgstr "He llegit i accepto les condicions de reserves" msgstr "He llegit i accepto les condicions de reserves"
@ -399,7 +399,7 @@ msgstr "Salta al contingut principal"
#: web/templates/public/layout.gohtml:35 web/templates/public/layout.gohtml:80 #: web/templates/public/layout.gohtml:35 web/templates/public/layout.gohtml:80
#: web/templates/admin/campsite/index.gohtml:6 #: web/templates/admin/campsite/index.gohtml:6
#: web/templates/admin/campsite/index.gohtml:12 #: web/templates/admin/campsite/index.gohtml:12
#: web/templates/admin/layout.gohtml:40 web/templates/admin/layout.gohtml:71 #: web/templates/admin/layout.gohtml:43 web/templates/admin/layout.gohtml:74
msgctxt "title" msgctxt "title"
msgid "Campsites" msgid "Campsites"
msgstr "Allotjaments" msgstr "Allotjaments"
@ -826,7 +826,7 @@ msgstr "Descripció"
#: web/templates/admin/campsite/type/index.gohtml:6 #: web/templates/admin/campsite/type/index.gohtml:6
#: web/templates/admin/campsite/type/index.gohtml:12 #: web/templates/admin/campsite/type/index.gohtml:12
#: web/templates/admin/layout.gohtml:37 #: web/templates/admin/layout.gohtml:40
msgctxt "title" msgctxt "title"
msgid "Campsite Types" msgid "Campsite Types"
msgstr "Tipus dallotjaments" msgstr "Tipus dallotjaments"
@ -901,7 +901,7 @@ msgstr "Color"
#: web/templates/admin/season/index.gohtml:6 #: web/templates/admin/season/index.gohtml:6
#: web/templates/admin/season/index.gohtml:12 #: web/templates/admin/season/index.gohtml:12
#: web/templates/admin/layout.gohtml:43 #: web/templates/admin/layout.gohtml:46
msgctxt "title" msgctxt "title"
msgid "Seasons" msgid "Seasons"
msgstr "Temporades" msgstr "Temporades"
@ -932,8 +932,50 @@ msgctxt "action"
msgid "Cancel" msgid "Cancel"
msgstr "Canceŀla" msgstr "Canceŀla"
#: web/templates/admin/payment.gohtml:6 web/templates/admin/payment.gohtml:12
#: web/templates/admin/layout.gohtml:37
msgctxt "title"
msgid "Payment Settings"
msgstr "Paràmetres de pagament"
#: web/templates/admin/payment.gohtml:17
msgctxt "input"
msgid "Merchant Code"
msgstr "Codi del comerç"
#: web/templates/admin/payment.gohtml:26
msgctxt "input"
msgid "Terminal Number"
msgstr "Número de terminal"
#: web/templates/admin/payment.gohtml:36
msgctxt "input"
msgid "Merchant Key (only if must change it)"
msgstr "Clau del comerç (només si sha de canviar)"
#: web/templates/admin/payment.gohtml:38
msgctxt "input"
msgid "Merchant Key"
msgstr "Clau del comerç"
#: web/templates/admin/payment.gohtml:48
msgctxt "title"
msgid "Environment"
msgstr "Entorn"
#: web/templates/admin/payment.gohtml:55
msgctxt "title"
msgid "Integration"
msgstr "Integració"
#: web/templates/admin/payment.gohtml:62 web/templates/admin/profile.gohtml:75
#: web/templates/admin/taxDetails.gohtml:152
msgctxt "action"
msgid "Save changes"
msgstr "Desa els canvis"
#: web/templates/admin/dashboard.gohtml:6 #: web/templates/admin/dashboard.gohtml:6
#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:68 #: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:71
msgctxt "title" msgctxt "title"
msgid "Dashboard" msgid "Dashboard"
msgstr "Tauler" msgstr "Tauler"
@ -966,7 +1008,7 @@ msgid "New Service"
msgstr "Nou servei" msgstr "Nou servei"
#: web/templates/admin/services/index.gohtml:6 #: web/templates/admin/services/index.gohtml:6
#: web/templates/admin/layout.gohtml:52 #: web/templates/admin/layout.gohtml:55
msgctxt "title" msgctxt "title"
msgid "Services Page" msgid "Services Page"
msgstr "Pàgina de serveis" msgstr "Pàgina de serveis"
@ -1027,12 +1069,6 @@ msgctxt "input"
msgid "Language" msgid "Language"
msgstr "Idioma" msgstr "Idioma"
#: web/templates/admin/profile.gohtml:75
#: web/templates/admin/taxDetails.gohtml:152
msgctxt "action"
msgid "Save changes"
msgstr "Desa els canvis"
#: web/templates/admin/taxDetails.gohtml:6 #: web/templates/admin/taxDetails.gohtml:6
#: web/templates/admin/taxDetails.gohtml:12 #: web/templates/admin/taxDetails.gohtml:12
msgctxt "title" msgctxt "title"
@ -1110,19 +1146,19 @@ msgctxt "title"
msgid "Company Settings" msgid "Company Settings"
msgstr "Paràmetres de lempresa" msgstr "Paràmetres de lempresa"
#: web/templates/admin/layout.gohtml:46 #: web/templates/admin/layout.gohtml:49
#: web/templates/admin/media/index.gohtml:6 #: web/templates/admin/media/index.gohtml:6
#: web/templates/admin/media/index.gohtml:11 #: web/templates/admin/media/index.gohtml:11
msgctxt "title" msgctxt "title"
msgid "Media" msgid "Media"
msgstr "Mèdia" msgstr "Mèdia"
#: web/templates/admin/layout.gohtml:49 web/templates/admin/home/index.gohtml:6 #: web/templates/admin/layout.gohtml:52 web/templates/admin/home/index.gohtml:6
msgctxt "title" msgctxt "title"
msgid "Home Page" msgid "Home Page"
msgstr "Pàgina dinici" msgstr "Pàgina dinici"
#: web/templates/admin/layout.gohtml:57 #: web/templates/admin/layout.gohtml:60
msgctxt "action" msgctxt "action"
msgid "Logout" msgid "Logout"
msgstr "Surt" msgstr "Surt"
@ -1253,7 +1289,7 @@ msgstr "Lidioma 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:53 #: pkg/app/admin.go:56
msgid "Access forbidden" msgid "Access forbidden"
msgstr "Accés prohibit" msgstr "Accés prohibit"
@ -1522,6 +1558,58 @@ msgstr "No podeu deixar el fitxer del mèdia en blanc."
msgid "Filename can not be empty." msgid "Filename can not be empty."
msgstr "No podeu deixar el nom del fitxer en blanc." msgstr "No podeu deixar el nom del fitxer en blanc."
#: pkg/booking/admin.go:74
msgctxt "redsys environment"
msgid "Test"
msgstr "Proves"
#: pkg/booking/admin.go:78
msgctxt "redsys environment"
msgid "Live"
msgstr "Real"
#: pkg/booking/admin.go:87
msgctxt "redsys integration"
msgid "InSite"
msgstr "InSite"
#: pkg/booking/admin.go:91
msgctxt "redsys integration"
msgid "Redirect"
msgstr "Redirecció"
#: pkg/booking/admin.go:135
msgid "Merchant code can not be empty."
msgstr "No podeu deixar el codi del comerç en blanc."
#: pkg/booking/admin.go:136
msgid "Merchant code must be exactly nine digits long."
msgstr "El codi del comerç ha de ser de nou dígits."
#: pkg/booking/admin.go:137
msgid "Merchant code must be a number."
msgstr "El codi del comerç."
#: pkg/booking/admin.go:141
msgid "Terminal number can not be empty."
msgstr "No podeu deixar el número del terminal en blanc."
#: pkg/booking/admin.go:142
msgid "Terminal number must be a number between 1 and 999."
msgstr "El número del terminal ha de ser entre 1 i 999"
#: pkg/booking/admin.go:150
msgid "Selected environment is not valid."
msgstr "Lentorn escollit no és vàlid."
#: pkg/booking/admin.go:151
msgid "Selected integration is not valid."
msgstr "La integració escollida no és vàlida."
#: pkg/booking/admin.go:154
msgid "The merchant key is not valid."
msgstr "Aquesta clau del comerç no és vàlid."
#: pkg/booking/public.go:279 #: pkg/booking/public.go:279
msgid "Full name can not be empty." msgid "Full name can not be empty."
msgstr "No podeu deixar el nom i els cognoms en blanc." msgstr "No podeu deixar el nom i els cognoms en blanc."
@ -1650,9 +1738,6 @@ msgstr "El valor de %s ha de ser com a màxim %d."
#~ msgid "Legend" #~ msgid "Legend"
#~ msgstr "Llegenda" #~ msgstr "Llegenda"
#~ msgid "Environment"
#~ msgstr "Entorn"
#~ msgctxt "input" #~ msgctxt "input"
#~ msgid "Title" #~ msgid "Title"
#~ msgstr "Títol" #~ msgstr "Títol"

163
po/es.po
View File

@ -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-10-27 01:12+0200\n" "POT-Creation-Date: 2023-10-27 15:59+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"
@ -111,7 +111,7 @@ msgid "Check-out Date"
msgstr "Fecha de salida" msgstr "Fecha de salida"
#: web/templates/public/campsite/type.gohtml:54 #: web/templates/public/campsite/type.gohtml:54
#: web/templates/public/booking.gohtml:175 #: web/templates/public/booking.gohtml:194
msgctxt "action" msgctxt "action"
msgid "Book" msgid "Book"
msgstr "Reservar" msgstr "Reservar"
@ -289,100 +289,100 @@ msgctxt "input"
msgid "Full name" msgid "Full name"
msgstr "Nombre y apellidos" msgstr "Nombre y apellidos"
#: web/templates/public/booking.gohtml:26 #: web/templates/public/booking.gohtml:27
msgctxt "input" msgctxt "input"
msgid "Address (optional)" msgid "Address (optional)"
msgstr "Dirección (opcional)" msgstr "Dirección (opcional)"
#: web/templates/public/booking.gohtml:34 #: web/templates/public/booking.gohtml:36
msgctxt "input" msgctxt "input"
msgid "Postcode (optional)" msgid "Postcode (optional)"
msgstr "Código postal (opcional)" msgstr "Código postal (opcional)"
#: web/templates/public/booking.gohtml:42 #: web/templates/public/booking.gohtml:45
msgctxt "input" msgctxt "input"
msgid "Town or village (optional)" msgid "Town or village (optional)"
msgstr "Población (opcional)" msgstr "Población (opcional)"
#: web/templates/public/booking.gohtml:50 #: web/templates/public/booking.gohtml:54
#: web/templates/admin/taxDetails.gohtml:98 #: web/templates/admin/taxDetails.gohtml:98
msgctxt "input" msgctxt "input"
msgid "Country" msgid "Country"
msgstr "País" msgstr "País"
#: web/templates/public/booking.gohtml:53 #: web/templates/public/booking.gohtml:57
msgid "Choose a country" msgid "Choose a country"
msgstr "Escoja un país" msgstr "Escoja un país"
#: web/templates/public/booking.gohtml:60 web/templates/admin/login.gohtml:22 #: web/templates/public/booking.gohtml:65 web/templates/admin/login.gohtml:22
#: web/templates/admin/profile.gohtml:35 #: web/templates/admin/profile.gohtml:35
#: web/templates/admin/taxDetails.gohtml:50 #: web/templates/admin/taxDetails.gohtml:50
msgctxt "input" msgctxt "input"
msgid "Email" msgid "Email"
msgstr "Correo-e" msgstr "Correo-e"
#: web/templates/public/booking.gohtml:68 #: web/templates/public/booking.gohtml:74
#: web/templates/admin/taxDetails.gohtml:42 #: web/templates/admin/taxDetails.gohtml:42
msgctxt "input" msgctxt "input"
msgid "Phone" msgid "Phone"
msgstr "Teléfono" msgstr "Teléfono"
#: web/templates/public/booking.gohtml:76 #: web/templates/public/booking.gohtml:83
msgctxt "title" msgctxt "title"
msgid "Party Details" msgid "Party Details"
msgstr "Datos de los visitantes" msgstr "Datos de los visitantes"
#: web/templates/public/booking.gohtml:79 #: web/templates/public/booking.gohtml:86
msgctxt "input" msgctxt "input"
msgid "Adults" msgid "Adults"
msgstr "Adultos" msgstr "Adultos"
#: web/templates/public/booking.gohtml:87 #: web/templates/public/booking.gohtml:95
msgctxt "input" msgctxt "input"
msgid "Teenagers (from 11 to 16 years old)" msgid "Teenagers (from 11 to 16 years old)"
msgstr "Adolescentes (de 11 a 16 años)" msgstr "Adolescentes (de 11 a 16 años)"
#: web/templates/public/booking.gohtml:95 #: web/templates/public/booking.gohtml:104
msgctxt "input" msgctxt "input"
msgid "Children (up to 10 years old)" msgid "Children (up to 10 years old)"
msgstr "Niños (hasta 10 años)" msgstr "Niños (hasta 10 años)"
#: web/templates/public/booking.gohtml:103 #: web/templates/public/booking.gohtml:113
msgctxt "input" msgctxt "input"
msgid "Dogs" msgid "Dogs"
msgstr "Perros" msgstr "Perros"
#: web/templates/public/booking.gohtml:111 #: web/templates/public/booking.gohtml:122
msgctxt "title" msgctxt "title"
msgid "Accomodation" msgid "Accomodation"
msgstr "Acomodaciones" msgstr "Acomodaciones"
#: web/templates/public/booking.gohtml:136 #: web/templates/public/booking.gohtml:150
msgctxt "title" msgctxt "title"
msgid "Booking Period" msgid "Booking Period"
msgstr "Periodo de reserva" msgstr "Periodo de reserva"
#: web/templates/public/booking.gohtml:139 #: web/templates/public/booking.gohtml:153
msgctxt "input" msgctxt "input"
msgid "Arrival date" msgid "Arrival date"
msgstr "Fecha de llegada" msgstr "Fecha de llegada"
#: web/templates/public/booking.gohtml:147 #: web/templates/public/booking.gohtml:162
msgctxt "input" msgctxt "input"
msgid "Departure date" msgid "Departure date"
msgstr "Fecha de salida" msgstr "Fecha de salida"
#: web/templates/public/booking.gohtml:155 #: web/templates/public/booking.gohtml:171
msgctxt "input" msgctxt "input"
msgid "Area preferences (optional)" msgid "Area preferences (optional)"
msgstr "Preferencias de área (opcional)" msgstr "Preferencias de área (opcional)"
#: web/templates/public/booking.gohtml:165 #: web/templates/public/booking.gohtml:182
msgctxt "input" msgctxt "input"
msgid "ACSI card? (optional)" msgid "ACSI card? (optional)"
msgstr "¿Tarjeta ACSI? (opcional)" msgstr "¿Tarjeta ACSI? (opcional)"
#: web/templates/public/booking.gohtml:171 #: web/templates/public/booking.gohtml:189
msgctxt "input" msgctxt "input"
msgid "I have read and I accept the reservation conditions" msgid "I have read and I accept the reservation conditions"
msgstr "He leído y acepto las condiciones de reserva" msgstr "He leído y acepto las condiciones de reserva"
@ -399,7 +399,7 @@ msgstr "Saltar al contenido principal"
#: web/templates/public/layout.gohtml:35 web/templates/public/layout.gohtml:80 #: web/templates/public/layout.gohtml:35 web/templates/public/layout.gohtml:80
#: web/templates/admin/campsite/index.gohtml:6 #: web/templates/admin/campsite/index.gohtml:6
#: web/templates/admin/campsite/index.gohtml:12 #: web/templates/admin/campsite/index.gohtml:12
#: web/templates/admin/layout.gohtml:40 web/templates/admin/layout.gohtml:71 #: web/templates/admin/layout.gohtml:43 web/templates/admin/layout.gohtml:74
msgctxt "title" msgctxt "title"
msgid "Campsites" msgid "Campsites"
msgstr "Alojamientos" msgstr "Alojamientos"
@ -826,7 +826,7 @@ msgstr "Descripción"
#: web/templates/admin/campsite/type/index.gohtml:6 #: web/templates/admin/campsite/type/index.gohtml:6
#: web/templates/admin/campsite/type/index.gohtml:12 #: web/templates/admin/campsite/type/index.gohtml:12
#: web/templates/admin/layout.gohtml:37 #: web/templates/admin/layout.gohtml:40
msgctxt "title" msgctxt "title"
msgid "Campsite Types" msgid "Campsite Types"
msgstr "Tipos de alojamientos" msgstr "Tipos de alojamientos"
@ -901,7 +901,7 @@ msgstr "Color"
#: web/templates/admin/season/index.gohtml:6 #: web/templates/admin/season/index.gohtml:6
#: web/templates/admin/season/index.gohtml:12 #: web/templates/admin/season/index.gohtml:12
#: web/templates/admin/layout.gohtml:43 #: web/templates/admin/layout.gohtml:46
msgctxt "title" msgctxt "title"
msgid "Seasons" msgid "Seasons"
msgstr "Temporadas" msgstr "Temporadas"
@ -932,8 +932,50 @@ msgctxt "action"
msgid "Cancel" msgid "Cancel"
msgstr "Cancelar" msgstr "Cancelar"
#: web/templates/admin/payment.gohtml:6 web/templates/admin/payment.gohtml:12
#: web/templates/admin/layout.gohtml:37
msgctxt "title"
msgid "Payment Settings"
msgstr "Parámetros de pago"
#: web/templates/admin/payment.gohtml:17
msgctxt "input"
msgid "Merchant Code"
msgstr "Código del comercio"
#: web/templates/admin/payment.gohtml:26
msgctxt "input"
msgid "Terminal Number"
msgstr "Número de terminal"
#: web/templates/admin/payment.gohtml:36
msgctxt "input"
msgid "Merchant Key (only if must change it)"
msgstr "Clave del comercio (sólo si se debe cambiar)"
#: web/templates/admin/payment.gohtml:38
msgctxt "input"
msgid "Merchant Key"
msgstr "Clave del comercio"
#: web/templates/admin/payment.gohtml:48
msgctxt "title"
msgid "Environment"
msgstr "Entorno"
#: web/templates/admin/payment.gohtml:55
msgctxt "title"
msgid "Integration"
msgstr "Integración"
#: web/templates/admin/payment.gohtml:62 web/templates/admin/profile.gohtml:75
#: web/templates/admin/taxDetails.gohtml:152
msgctxt "action"
msgid "Save changes"
msgstr "Guardar los cambios"
#: web/templates/admin/dashboard.gohtml:6 #: web/templates/admin/dashboard.gohtml:6
#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:68 #: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:71
msgctxt "title" msgctxt "title"
msgid "Dashboard" msgid "Dashboard"
msgstr "Panel" msgstr "Panel"
@ -966,7 +1008,7 @@ msgid "New Service"
msgstr "Nuevo servicio" msgstr "Nuevo servicio"
#: web/templates/admin/services/index.gohtml:6 #: web/templates/admin/services/index.gohtml:6
#: web/templates/admin/layout.gohtml:52 #: web/templates/admin/layout.gohtml:55
msgctxt "title" msgctxt "title"
msgid "Services Page" msgid "Services Page"
msgstr "Página de servicios" msgstr "Página de servicios"
@ -1027,12 +1069,6 @@ msgctxt "input"
msgid "Language" msgid "Language"
msgstr "Idioma" msgstr "Idioma"
#: web/templates/admin/profile.gohtml:75
#: web/templates/admin/taxDetails.gohtml:152
msgctxt "action"
msgid "Save changes"
msgstr "Guardar los cambios"
#: web/templates/admin/taxDetails.gohtml:6 #: web/templates/admin/taxDetails.gohtml:6
#: web/templates/admin/taxDetails.gohtml:12 #: web/templates/admin/taxDetails.gohtml:12
msgctxt "title" msgctxt "title"
@ -1110,19 +1146,19 @@ msgctxt "title"
msgid "Company Settings" msgid "Company Settings"
msgstr "Parámetros de la empresa" msgstr "Parámetros de la empresa"
#: web/templates/admin/layout.gohtml:46 #: web/templates/admin/layout.gohtml:49
#: web/templates/admin/media/index.gohtml:6 #: web/templates/admin/media/index.gohtml:6
#: web/templates/admin/media/index.gohtml:11 #: web/templates/admin/media/index.gohtml:11
msgctxt "title" msgctxt "title"
msgid "Media" msgid "Media"
msgstr "Medios" msgstr "Medios"
#: web/templates/admin/layout.gohtml:49 web/templates/admin/home/index.gohtml:6 #: web/templates/admin/layout.gohtml:52 web/templates/admin/home/index.gohtml:6
msgctxt "title" msgctxt "title"
msgid "Home Page" msgid "Home Page"
msgstr "Página de inicio" msgstr "Página de inicio"
#: web/templates/admin/layout.gohtml:57 #: web/templates/admin/layout.gohtml:60
msgctxt "action" msgctxt "action"
msgid "Logout" msgid "Logout"
msgstr "Salir" msgstr "Salir"
@ -1253,7 +1289,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:53 #: pkg/app/admin.go:56
msgid "Access forbidden" msgid "Access forbidden"
msgstr "Acceso prohibido" msgstr "Acceso prohibido"
@ -1522,6 +1558,58 @@ msgstr "No podéis dejar el archivo del medio en blanco."
msgid "Filename can not be empty." msgid "Filename can not be empty."
msgstr "No podéis dejar el nombre del archivo en blanco." msgstr "No podéis dejar el nombre del archivo en blanco."
#: pkg/booking/admin.go:74
msgctxt "redsys environment"
msgid "Test"
msgstr "Pruebas"
#: pkg/booking/admin.go:78
msgctxt "redsys environment"
msgid "Live"
msgstr "Real"
#: pkg/booking/admin.go:87
msgctxt "redsys integration"
msgid "InSite"
msgstr "InSite"
#: pkg/booking/admin.go:91
msgctxt "redsys integration"
msgid "Redirect"
msgstr "Redirección"
#: pkg/booking/admin.go:135
msgid "Merchant code can not be empty."
msgstr "No podéis dejar el código del comercio en blanco."
#: pkg/booking/admin.go:136
msgid "Merchant code must be exactly nine digits long."
msgstr "El código del comercio tiene que ser de nueve dígitos."
#: pkg/booking/admin.go:137
msgid "Merchant code must be a number."
msgstr "El código del comercio tiene que ser un número."
#: pkg/booking/admin.go:141
msgid "Terminal number can not be empty."
msgstr "No podéis dejar el número de terminal en blanco."
#: pkg/booking/admin.go:142
msgid "Terminal number must be a number between 1 and 999."
msgstr "El número de terminal tiene que ser entre 1 y 999."
#: pkg/booking/admin.go:150
msgid "Selected environment is not valid."
msgstr "El entorno escogido no es válido."
#: pkg/booking/admin.go:151
msgid "Selected integration is not valid."
msgstr "La integración escogida no es válida."
#: pkg/booking/admin.go:154
msgid "The merchant key is not valid."
msgstr "Esta clave del comercio no es válida."
#: pkg/booking/public.go:279 #: pkg/booking/public.go:279
msgid "Full name can not be empty." msgid "Full name can not be empty."
msgstr "No podéis dejar el nombre y los apellidos en blanco." msgstr "No podéis dejar el nombre y los apellidos en blanco."
@ -1650,9 +1738,6 @@ msgstr "%s tiene que ser como máximo %d"
#~ msgid "Legend" #~ msgid "Legend"
#~ msgstr "Leyenda" #~ msgstr "Leyenda"
#~ msgid "Environment"
#~ msgstr "Entorno"
#~ msgctxt "input" #~ msgctxt "input"
#~ msgid "Title" #~ msgid "Title"
#~ msgstr "Título" #~ msgstr "Título"

View File

@ -16,6 +16,13 @@
{{- end }} {{- end }}
{{- end }} {{- end }}
{{ define "radio-options" -}}
{{- range .Options }}
<label><input type="radio" name="{{ $.Name }}" value="{{ .Value }}"
{{ if $.IsSelected .Value }}checked{{ end }}> {{ .Label }}</label>
{{- end }}
{{- end }}
{{ define "media-picker" -}} {{ define "media-picker" -}}
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/form.Media*/ -}} {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/form.Media*/ -}}
<fieldset data-hx-target="this" data-hx-swap="outerHTML"> <fieldset data-hx-target="this" data-hx-swap="outerHTML">

View File

@ -26,13 +26,16 @@
<span>{{( pgettext "User Menu" "title" )}}</span> <span>{{( pgettext "User Menu" "title" )}}</span>
</summary> </summary>
<ul> <ul>
<li class="icon_profile"> <li>
<a href="/me">{{( pgettext "Profile" "title" )}}</a> <a href="/me">{{( pgettext "Profile" "title" )}}</a>
</li> </li>
{{ if isAdmin -}} {{ if isAdmin -}}
<li class="icon_company"> <li>
<a href="/admin/company">{{( pgettext "Company Settings" "title" )}}</a> <a href="/admin/company">{{( pgettext "Company Settings" "title" )}}</a>
</li> </li>
<li>
<a href="/admin/payment">{{( pgettext "Payment Settings" "title" )}}</a>
</li>
<li> <li>
<a href="/admin/campsites/types">{{( pgettext "Campsite Types" "title" )}}</a> <a href="/admin/campsites/types">{{( pgettext "Campsite Types" "title" )}}</a>
</li> </li>

View File

@ -0,0 +1,65 @@
<!--
SPDX-FileCopyrightText: 2023 jordi fita mas <jordi@tandem.blog>
SPDX-License-Identifier: AGPL-3.0-only
-->
{{ define "title" -}}
{{( pgettext "Payment Settings" "title" )}}
{{- end }}
{{ define "content" -}}
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/booking.paymentForm*/ -}}
<form data-hx-put="/admin/payment">
<h2>{{( pgettext "Payment Settings" "title" )}}</h2>
{{ CSRFInput }}
<fieldset>
{{ with .MerchantCode -}}
<label>
{{( pgettext "Merchant Code" "input")}}<br>
<input type="text" name="{{ .Name }}" value="{{ .Val }}"
required minlength="9"
{{ template "error-attrs" . }}><br>
</label>
{{ template "error-message" . }}
{{- end }}
{{ with .TerminalNumber -}}
<label>
{{( pgettext "Terminal Number" "input")}}<br>
<input type="number" name="{{ .Name }}" value="{{ .Val }}"
min="1" max="999"
required {{ template "error-attrs" . }}><br>
</label>
{{ template "error-message" . }}
{{- end }}
{{ with .EncryptKey -}}
<label>
{{ if $.MerchantCode.Val -}}
{{( pgettext "Merchant Key (only if must change it)" "input")}}<br>
{{- else -}}
{{( pgettext "Merchant Key" "input")}}<br>
{{- end }}
<input type="text" name="{{ .Name }}" value="{{ .Val }}"
{{ if not $.MerchantCode.Val }}required{{end }}
{{ template "error-attrs" . }}><br>
</label>
{{ template "error-message" . }}
{{- end }}
{{ with .Environment -}}
<fieldset>
<legend>{{( pgettext "Environment" "title")}}</legend>
{{ template "radio-options" . }}
</fieldset>
{{ template "error-message" . }}
{{- end }}
{{ with .Integration -}}
<fieldset>
<legend>{{( pgettext "Integration" "title")}}</legend>
{{ template "radio-options" . }}
</fieldset>
{{ template "error-message" . }}
{{- end }}
</fieldset>
<footer>
<button type="submit">{{( pgettext "Save changes" "action" )}}</button>
</footer>
</form>
{{- end }}