Add ready_payment function and use their slug as URL

Now that the payments have slug, i can use them in the URL to show the
actual data of a payment, and kickstart the payment process with Redsys.
This commit is contained in:
jordi fita mas 2024-02-12 18:06:17 +01:00
parent 148d9075da
commit 15dde3f491
22 changed files with 785 additions and 307 deletions

View File

@ -19,10 +19,11 @@ create table payment_customer (
country_code country_code not null references country, country_code country_code not null references country,
email email not null, email email not null,
phone packed_phone_number not null, phone packed_phone_number not null,
acsi_card boolean not null acsi_card boolean not null,
lang_tag text not null references language
); );
grant select, insert on table payment_customer to guest; grant select, insert, update on table payment_customer to guest;
grant select, insert, update on table payment_customer to employee; grant select, insert, update on table payment_customer to employee;
grant select, insert, update, delete on table payment_customer to admin; grant select, insert, update, delete on table payment_customer to admin;

56
deploy/ready_payment.sql Normal file
View File

@ -0,0 +1,56 @@
-- Deploy camper:ready_payment to pg
-- requires: roles
-- requires: schema_camper
-- requires: payment
-- requires: payment_customer
-- requires: country_code
-- requires: email
-- requires: extension_pg_libphonenumber
begin;
set search_path to camper, public;
create or replace function ready_payment(payment_slug uuid, customer_name text, customer_address text, customer_post_code text, customer_city text, customer_country_code country_code, customer_email email, customer_phone text, customer_lang_tag text, customer_acsi_card boolean) returns integer as
$$
declare
pid integer;
begin
update payment
set payment_status = 'pending'
, updated_at = current_timestamp
where slug = payment_slug
and payment_status = 'draft'
returning payment_id into pid
;
if pid is null then
raise check_violation using message = 'insert or update on table "payment" violates check constraint "payment_is_draft"';
end if;
insert into payment_customer (payment_id, full_name, address, postal_code, city, country_code, email, phone, acsi_card, lang_tag)
values (pid, customer_name, customer_address, customer_post_code, customer_city, customer_country_code, customer_email, parse_packed_phone_number(customer_phone, customer_country_code), customer_acsi_card, customer_lang_tag)
on conflict (payment_id) do update
set full_name = excluded.full_name
, address = excluded.address
, postal_code = excluded.postal_code
, city = excluded.city
, country_code = excluded.country_code
, email = excluded.email
, phone = excluded.phone
, acsi_card = excluded.acsi_card
, lang_tag = excluded.lang_tag
;
return pid;
end;
$$
language plpgsql
;
revoke execute on function ready_payment(uuid, text, text, text, text, country_code, email, text, text, boolean) from public;
grant execute on function ready_payment(uuid, text, text, text, text, country_code, email, text, text, boolean) to guest;
grant execute on function ready_payment(uuid, text, text, text, text, country_code, email, text, text, boolean) to employee;
grant execute on function ready_payment(uuid, text, text, text, text, country_code, email, text, text, boolean) to admin;
commit;

View File

@ -17,6 +17,7 @@ import (
httplib "dev.tandem.ws/tandem/camper/pkg/http" httplib "dev.tandem.ws/tandem/camper/pkg/http"
"dev.tandem.ws/tandem/camper/pkg/legal" "dev.tandem.ws/tandem/camper/pkg/legal"
"dev.tandem.ws/tandem/camper/pkg/location" "dev.tandem.ws/tandem/camper/pkg/location"
"dev.tandem.ws/tandem/camper/pkg/payment"
"dev.tandem.ws/tandem/camper/pkg/services" "dev.tandem.ws/tandem/camper/pkg/services"
"dev.tandem.ws/tandem/camper/pkg/surroundings" "dev.tandem.ws/tandem/camper/pkg/surroundings"
"dev.tandem.ws/tandem/camper/pkg/template" "dev.tandem.ws/tandem/camper/pkg/template"
@ -29,6 +30,7 @@ type publicHandler struct {
campsite *campsite.PublicHandler campsite *campsite.PublicHandler
legal *legal.PublicHandler legal *legal.PublicHandler
location *location.PublicHandler location *location.PublicHandler
payment *payment.PublicHandler
services *services.PublicHandler services *services.PublicHandler
surroundings *surroundings.PublicHandler surroundings *surroundings.PublicHandler
} }
@ -41,6 +43,7 @@ func newPublicHandler() *publicHandler {
campsite: campsite.NewPublicHandler(), campsite: campsite.NewPublicHandler(),
legal: legal.NewPublicHandler(), legal: legal.NewPublicHandler(),
location: location.NewPublicHandler(), location: location.NewPublicHandler(),
payment: payment.NewPublicHandler(),
services: services.NewPublicHandler(), services: services.NewPublicHandler(),
surroundings: surroundings.NewPublicHandler(), surroundings: surroundings.NewPublicHandler(),
} }
@ -65,6 +68,8 @@ func (h *publicHandler) Handler(user *auth.User, company *auth.Company, conn *da
h.legal.Handler(user, company, conn).ServeHTTP(w, r) h.legal.Handler(user, company, conn).ServeHTTP(w, r)
case "location": case "location":
h.location.Handler(user, company, conn).ServeHTTP(w, r) h.location.Handler(user, company, conn).ServeHTTP(w, r)
case "payments":
h.payment.Handler(user, company, conn).ServeHTTP(w, r)
case "services": case "services":
h.services.Handler(user, company, conn).ServeHTTP(w, r) h.services.Handler(user, company, conn).ServeHTTP(w, r)
case "surroundings": case "surroundings":

View File

@ -6,98 +6,46 @@
package booking package booking
import ( import (
"fmt"
"net/http" "net/http"
"dev.tandem.ws/tandem/camper/pkg/auth" "dev.tandem.ws/tandem/camper/pkg/auth"
"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/redsys"
"dev.tandem.ws/tandem/camper/pkg/template"
) )
type paymentPage struct { func requestPayment(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
*template.PublicPage f, err := newBookingForm(r, company, conn, user.Locale)
Environment string if err != nil {
Request *redsys.SignedRequest http.Error(w, err.Error(), http.StatusBadRequest)
} return
}
func newPaymentPage(request *redsys.SignedRequest) *paymentPage { if ok, err := f.Valid(r.Context(), conn, user.Locale); err != nil {
return &paymentPage{ panic(err)
PublicPage: template.NewPublicPage(), } else if !ok {
Request: request, if !httplib.IsHTMxRequest(r) {
w.WriteHeader(http.StatusUnprocessableEntity)
}
page := newPublicPageWithForm(f)
page.MustRender(w, r, user, company, conn)
return
} }
}
func (p *paymentPage) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) { _, err = conn.ReadyPayment(
p.Setup(r, user, company, conn) r.Context(),
if err := conn.QueryRow(r.Context(), "select environment from redsys where company_id = $1", company.ID).Scan(&p.Environment); err != nil && !database.ErrorIsNotFound(err) { f.PaymentSlug.Val,
f.Customer.FullName.Val,
f.Customer.Address.Val,
f.Customer.PostalCode.Val,
f.Customer.City.Val,
f.Customer.Country.String(),
f.Customer.Email.Val,
f.Customer.Phone.Val,
user.Locale.Language,
f.Customer.ACSICard.Checked,
)
if err != nil {
panic(err) panic(err)
} }
template.MustRenderPublic(w, r, user, company, "payment/request.gohtml", p) httplib.Redirect(w, r, fmt.Sprintf("/%s/payments/%s", user.Locale.Language, f.PaymentSlug.Val), http.StatusSeeOther)
}
func handleSuccessfulPayment(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
var head string
head, r.URL.Path = httplib.ShiftPath(r.URL.Path)
switch head {
case "":
switch r.Method {
case http.MethodGet:
page := newSuccessfulPaymentPage()
page.MustRender(w, r, user, company, conn)
default:
httplib.MethodNotAllowed(w, r, http.MethodGet)
}
default:
http.NotFound(w, r)
}
}
type successfulPaymentPage struct {
*template.PublicPage
}
func newSuccessfulPaymentPage() *successfulPaymentPage {
return &successfulPaymentPage{
PublicPage: template.NewPublicPage(),
}
}
func (p *successfulPaymentPage) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
p.Setup(r, user, company, conn)
template.MustRenderPublic(w, r, user, company, "payment/success.gohtml", p)
}
func handleFailedPayment(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
var head string
head, r.URL.Path = httplib.ShiftPath(r.URL.Path)
switch head {
case "":
switch r.Method {
case http.MethodGet:
page := newFailedPaymentPage()
page.MustRender(w, r, user, company, conn)
default:
httplib.MethodNotAllowed(w, r, http.MethodGet)
}
default:
http.NotFound(w, r)
}
}
type failedPaymentPage struct {
*template.PublicPage
}
func newFailedPaymentPage() *failedPaymentPage {
return &failedPaymentPage{
PublicPage: template.NewPublicPage(),
}
}
func (p *failedPaymentPage) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
p.Setup(r, user, company, conn)
template.MustRenderPublic(w, r, user, company, "payment/failure.gohtml", p)
} }

View File

@ -7,8 +7,6 @@ package booking
import ( import (
"context" "context"
"crypto/rand"
"encoding/hex"
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
@ -20,7 +18,6 @@ import (
"dev.tandem.ws/tandem/camper/pkg/form" "dev.tandem.ws/tandem/camper/pkg/form"
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/redsys"
"dev.tandem.ws/tandem/camper/pkg/template" "dev.tandem.ws/tandem/camper/pkg/template"
) )
@ -47,66 +44,16 @@ func (h *PublicHandler) Handler(user *auth.User, company *auth.Company, conn *da
} }
page.MustRender(w, r, user, company, conn) page.MustRender(w, r, user, company, conn)
case http.MethodPost: case http.MethodPost:
makeReservation(w, r, user, company, conn) requestPayment(w, r, user, company, conn)
default: default:
httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPost) httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPost)
} }
case "success":
handleSuccessfulPayment(w, r, user, company, conn)
case "failure":
handleFailedPayment(w, r, user, company, conn)
default: default:
http.NotFound(w, r) http.NotFound(w, r)
} }
}) })
} }
func makeReservation(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
f, err := newBookingForm(r, company, conn, user.Locale)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
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)
}
page := newPublicPageWithForm(f)
page.MustRender(w, r, user, company, conn)
return
}
schema := httplib.Protocol(r)
authority := httplib.Host(r)
baseURL := fmt.Sprintf("%s://%s/%s/booking", schema, authority, user.Locale.Language)
request := &redsys.Request{
TransactionType: redsys.TransactionTypeCharge,
Amount: f.Cart.Total,
OrderNumber: randomOrderNumber(),
Product: "Test Booking",
SuccessURL: fmt.Sprintf("%s/success", baseURL),
FailureURL: fmt.Sprintf("%s/failure", baseURL),
NotificationURL: fmt.Sprintf("%s/notification", baseURL),
ConsumerLanguage: user.Locale.Language,
}
signed, err := redsys.SignRequest(r.Context(), conn, company, request)
if err != nil {
panic(err)
}
page := newPaymentPage(signed)
page.MustRender(w, r, user, company, conn)
}
func randomOrderNumber() string {
bytes := make([]byte, 6)
if _, err := rand.Read(bytes); err != nil {
panic(err)
}
return hex.EncodeToString(bytes)
}
type publicPage struct { type publicPage struct {
*template.PublicPage *template.PublicPage
Form *bookingForm Form *bookingForm
@ -272,6 +219,9 @@ func (f *bookingForm) Valid(ctx context.Context, conn *database.Conn, l *locale.
if f.Customer == nil { if f.Customer == nil {
return false, errors.New("no customer fields") return false, errors.New("no customer fields")
} }
if f.Cart == nil {
return false, errors.New("no booking cart")
}
v.CheckSelectedOptions(f.CampsiteType, l.GettextNoop("Selected campsite type is not valid.")) v.CheckSelectedOptions(f.CampsiteType, l.GettextNoop("Selected campsite type is not valid."))
f.Dates.Valid(v, l) f.Dates.Valid(v, l)

View File

@ -357,3 +357,7 @@ func (c *Conn) DraftPayment(ctx context.Context, paymentSlug string, arrivalDate
} }
return c.GetText(ctx, "select draft_payment($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", paymentSlugParam, arrivalDate, departureDate, campsiteTypeSlug, numAdults, numTeenagers, numChildren, numDogs, zonePreferences, options) return c.GetText(ctx, "select draft_payment($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", paymentSlugParam, arrivalDate, departureDate, campsiteTypeSlug, numAdults, numTeenagers, numChildren, numDogs, zonePreferences, options)
} }
func (c *Conn) ReadyPayment(ctx context.Context, paymentSlug string, customerName string, customerAddress string, customerPostCode string, customerCity string, customerCountryCode string, customerEmail string, customerPhone string, customerLangTag language.Tag, acsiCard bool) (int, error) {
return c.GetInt(ctx, "select ready_payment($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", paymentSlug, customerName, customerAddress, customerPostCode, customerCity, customerCountryCode, customerEmail, customerPhone, customerLangTag, acsiCard)
}

204
pkg/payment/public.go Normal file
View File

@ -0,0 +1,204 @@
package payment
import (
"context"
"fmt"
"net/http"
"time"
"dev.tandem.ws/tandem/camper/pkg/auth"
"dev.tandem.ws/tandem/camper/pkg/database"
httplib "dev.tandem.ws/tandem/camper/pkg/http"
"dev.tandem.ws/tandem/camper/pkg/redsys"
"dev.tandem.ws/tandem/camper/pkg/template"
"dev.tandem.ws/tandem/camper/pkg/uuid"
)
type PublicHandler struct {
}
func NewPublicHandler() *PublicHandler {
return &PublicHandler{}
}
func (h *PublicHandler) Handler(user *auth.User, company *auth.Company, conn *database.Conn) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var paymentSlug string
paymentSlug, r.URL.Path = httplib.ShiftPath(r.URL.Path)
if !uuid.Valid(paymentSlug) {
http.NotFound(w, r)
return
}
payment, err := fetchPayment(r.Context(), conn, paymentSlug)
if err != nil {
if database.ErrorIsNotFound(err) {
http.NotFound(w, r)
return
}
panic(err)
}
var head string
head, r.URL.Path = httplib.ShiftPath(r.URL.Path)
switch head {
case "":
switch r.Method {
case http.MethodGet:
page := newPaymentPage(payment)
page.MustRender(w, r, user, company, conn)
default:
httplib.MethodNotAllowed(w, r, http.MethodGet)
}
case "success":
handleSuccessfulPayment(w, r, user, company, conn, payment)
case "failure":
handleFailedPayment(w, r, user, company, conn, payment)
default:
http.NotFound(w, r)
}
})
}
func fetchPayment(ctx context.Context, conn *database.Conn, paymentSlug string) (*Payment, error) {
row := conn.QueryRow(ctx, `
select payment_id
, payment.slug::text
, payment.created_at
, to_price(total, decimal_digits)
from payment
join company using (company_id)
join currency using (currency_code)
where payment.slug = $1
and payment_status <> 'draft'
`, paymentSlug)
payment := &Payment{}
if err := row.Scan(&payment.ID, &payment.Slug, &payment.CreateTime, &payment.Total); err != nil {
return nil, err
}
return payment, nil
}
type Payment struct {
ID int
Slug string
Total string
CreateTime time.Time
}
func (payment *Payment) createRequest(r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) (*redsys.SignedRequest, error) {
schema := httplib.Protocol(r)
authority := httplib.Host(r)
baseURL := fmt.Sprintf("%s://%s/%s/payments/%s", schema, authority, user.Locale.Language, payment.Slug)
request := &redsys.Request{
TransactionType: redsys.TransactionTypeCharge,
Amount: payment.Total,
OrderNumber: payment.OrderNumber(),
Product: user.Locale.Pgettext("Campsite Booking", "order product name"),
SuccessURL: fmt.Sprintf("%s/success", baseURL),
FailureURL: fmt.Sprintf("%s/failure", baseURL),
NotificationURL: fmt.Sprintf("%s/notification", baseURL),
ConsumerLanguage: user.Locale.Language,
}
return redsys.SignRequest(r.Context(), conn, company, request)
}
func (payment *Payment) OrderNumber() string {
return fmt.Sprintf("%08d%s", payment.ID, payment.Slug[:4])
}
type paymentPage struct {
*template.PublicPage
Environment string
Payment *Payment
Request *redsys.SignedRequest
}
func newPaymentPage(payment *Payment) *paymentPage {
return &paymentPage{
PublicPage: template.NewPublicPage(),
Payment: payment,
}
}
func (p *paymentPage) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
p.Setup(r, user, company, conn)
request, err := p.Payment.createRequest(r, user, company, conn)
if err != nil {
panic(err)
}
p.Request = request
if err := conn.QueryRow(r.Context(), "select environment from redsys where company_id = $1", company.ID).Scan(&p.Environment); err != nil && !database.ErrorIsNotFound(err) {
panic(err)
}
template.MustRenderPublic(w, r, user, company, "payment/request.gohtml", p)
}
func handleSuccessfulPayment(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, payment *Payment) {
var head string
head, r.URL.Path = httplib.ShiftPath(r.URL.Path)
switch head {
case "":
switch r.Method {
case http.MethodGet:
page := newSuccessfulPaymentPage(payment)
page.MustRender(w, r, user, company, conn)
default:
httplib.MethodNotAllowed(w, r, http.MethodGet)
}
default:
http.NotFound(w, r)
}
}
type successfulPaymentPage struct {
*template.PublicPage
Payment *Payment
}
func newSuccessfulPaymentPage(payment *Payment) *successfulPaymentPage {
return &successfulPaymentPage{
PublicPage: template.NewPublicPage(),
Payment: payment,
}
}
func (p *successfulPaymentPage) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
p.Setup(r, user, company, conn)
template.MustRenderPublicFiles(w, r, user, company, p, "payment/success.gohtml", "payment/details.gohtml")
}
func handleFailedPayment(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, payment *Payment) {
var head string
head, r.URL.Path = httplib.ShiftPath(r.URL.Path)
switch head {
case "":
switch r.Method {
case http.MethodGet:
page := newFailedPaymentPage(payment)
page.MustRender(w, r, user, company, conn)
default:
httplib.MethodNotAllowed(w, r, http.MethodGet)
}
default:
http.NotFound(w, r)
}
}
type failedPaymentPage struct {
*template.PublicPage
Payment *Payment
}
func newFailedPaymentPage(payment *Payment) *failedPaymentPage {
return &failedPaymentPage{
PublicPage: template.NewPublicPage(),
Payment: payment,
}
}
func (p *failedPaymentPage) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
p.Setup(r, user, company, conn)
template.MustRenderPublicFiles(w, r, user, company, p, "payment/failure.gohtml", "payment/details.gohtml")
}

135
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: 2024-02-12 05:10+0100\n" "POT-Creation-Date: 2024-02-12 17:49+0100\n"
"PO-Revision-Date: 2024-02-06 10:04+0100\n" "PO-Revision-Date: 2024-02-06 10:04+0100\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"
@ -60,28 +60,56 @@ msgid "Zone 1"
msgstr "Zona 1" msgstr "Zona 1"
#: web/templates/public/payment/success.gohtml:6 #: web/templates/public/payment/success.gohtml:6
#: web/templates/public/payment/success.gohtml:11
msgctxt "title" msgctxt "title"
msgid "Payment Successful" msgid "Payment Successful"
msgstr "Pagament amb èxit" msgstr "Pagament amb èxit"
#: web/templates/public/payment/request.gohtml:6 #: web/templates/public/payment/success.gohtml:12
#: web/templates/public/payment/request.gohtml:11 msgid "We have received the payment. Thank you."
msgctxt "title" msgstr "Hem rebut el vostre pagament. Gràcies."
msgid "Payment"
msgstr "Pagament"
#: web/templates/public/payment/request.gohtml:23 #: web/templates/public/payment/request.gohtml:6
msgctxt "title"
msgid "Booking Payment"
msgstr "Pagament de la reserva"
#: web/templates/public/payment/request.gohtml:18
msgid "Thank you for your booking. Please, click the button below to pay with a credit card via Servired/Redsys."
msgstr "Gràcies per la vostra reserva. Feu clic al botó de sota per pagar amb targeta de crèdit via Servired/Redsys."
#: web/templates/public/payment/request.gohtml:27
msgctxt "action" msgctxt "action"
msgid "Pay" msgid "Pay with credit card"
msgstr "Paga" msgstr "Paga amb targeta de crèdit"
#: web/templates/public/payment/request.gohtml:30
msgid "Please, wait until we redirect you to the payment page."
msgstr "Espereu mentre us enviem a la pàgina de pagament."
#: web/templates/public/payment/failure.gohtml:6 #: web/templates/public/payment/failure.gohtml:6
#: web/templates/public/payment/failure.gohtml:11
msgctxt "title" msgctxt "title"
msgid "Payment Failed" msgid "Payment Failed"
msgstr "Ha fallat el pagament" msgstr "Ha fallat el pagament"
#: web/templates/public/payment/failure.gohtml:12
msgid "We could not process the payment. Please, contact us for support."
msgstr "No hem pogut processar el pagament. Poseu-vos en contacte amb nosaltres per solucionar el problema."
#: web/templates/public/payment/details.gohtml:4
msgctxt "title"
msgid "Order Number"
msgstr "Número de comanda"
#: web/templates/public/payment/details.gohtml:8
msgctxt "title"
msgid "Date"
msgstr "Data"
#: web/templates/public/payment/details.gohtml:12
msgctxt "title"
msgid "Total"
msgstr "Total"
#: web/templates/public/services.gohtml:7 #: web/templates/public/services.gohtml:7
#: web/templates/public/services.gohtml:16 #: web/templates/public/services.gohtml:16
#: web/templates/public/layout.gohtml:67 web/templates/public/layout.gohtml:95 #: web/templates/public/layout.gohtml:67 web/templates/public/layout.gohtml:95
@ -1951,6 +1979,11 @@ msgstr "Estat"
msgid "No booking found." msgid "No booking found."
msgstr "No sha trobat cap reserva." msgstr "No sha trobat cap reserva."
#: pkg/payment/public.go:97
msgctxt "order product name"
msgid "Campsite Booking"
msgstr "Reserva de càmping"
#: pkg/legal/admin.go:258 pkg/app/user.go:249 pkg/campsite/types/option.go:365 #: pkg/legal/admin.go:258 pkg/app/user.go:249 pkg/campsite/types/option.go:365
#: pkg/campsite/types/feature.go:272 pkg/campsite/types/admin.go:577 #: pkg/campsite/types/feature.go:272 pkg/campsite/types/admin.go:577
#: pkg/campsite/feature.go:269 pkg/season/admin.go:412 #: pkg/campsite/feature.go:269 pkg/season/admin.go:412
@ -1990,12 +2023,12 @@ msgid "Slide image must be an image media type."
msgstr "La imatge de la diapositiva ha de ser un mèdia de tipus imatge." msgstr "La imatge de la diapositiva ha de ser un mèdia de tipus imatge."
#: pkg/app/login.go:56 pkg/app/user.go:246 pkg/company/admin.go:217 #: pkg/app/login.go:56 pkg/app/user.go:246 pkg/company/admin.go:217
#: pkg/booking/public.go:583 #: pkg/booking/public.go:533
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/company/admin.go:218 #: pkg/app/login.go:57 pkg/app/user.go:247 pkg/company/admin.go:218
#: pkg/booking/public.go:584 #: pkg/booking/public.go:534
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."
@ -2206,8 +2239,8 @@ msgctxt "header"
msgid "Children (aged 2 to 10)" msgid "Children (aged 2 to 10)"
msgstr "Mainada (entre 2 i 10 anys)" msgstr "Mainada (entre 2 i 10 anys)"
#: pkg/campsite/admin.go:275 pkg/booking/public.go:224 #: pkg/campsite/admin.go:275 pkg/booking/public.go:171
#: pkg/booking/public.go:276 #: pkg/booking/public.go:226
msgid "Selected campsite type is not valid." msgid "Selected campsite type is not valid."
msgstr "El tipus dallotjament escollit no és vàlid." msgstr "El tipus dallotjament escollit no és vàlid."
@ -2363,7 +2396,7 @@ msgstr "No podeu deixar ladreça de lenllaç en blanc."
msgid "This web address is not valid. It should be like https://domain.com/." 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/." msgstr "Aquesta adreça web no és vàlida. Hauria de ser similar a https://domini.com/."
#: pkg/company/admin.go:200 pkg/booking/public.go:570 #: pkg/company/admin.go:200 pkg/booking/public.go:520
msgid "Selected country is not valid." msgid "Selected country is not valid."
msgstr "El país escollit no és vàlid." msgstr "El país escollit no és vàlid."
@ -2383,11 +2416,11 @@ msgstr "No podeu deixar el NIF en blanc."
msgid "This VAT number is not valid." msgid "This VAT number is not valid."
msgstr "Aquest NIF no és vàlid." msgstr "Aquest NIF no és vàlid."
#: pkg/company/admin.go:212 pkg/booking/public.go:586 #: pkg/company/admin.go:212 pkg/booking/public.go:536
msgid "Phone can not be empty." msgid "Phone can not be empty."
msgstr "No podeu deixar el telèfon en blanc." msgstr "No podeu deixar el telèfon en blanc."
#: pkg/company/admin.go:213 pkg/booking/public.go:587 #: pkg/company/admin.go:213 pkg/booking/public.go:537
msgid "This phone number is not valid." msgid "This phone number is not valid."
msgstr "Aquest número de telèfon no és vàlid." msgstr "Aquest número de telèfon no és vàlid."
@ -2407,7 +2440,7 @@ msgstr "No podeu deixar la província en blanc."
msgid "Postal code can not be empty." msgid "Postal code can not be empty."
msgstr "No podeu deixar el codi postal en blanc." msgstr "No podeu deixar el codi postal en blanc."
#: pkg/company/admin.go:227 pkg/booking/public.go:579 #: pkg/company/admin.go:227 pkg/booking/public.go:529
msgid "This postal code is not valid." msgid "This postal code is not valid."
msgstr "Aquest codi postal no és vàlid." msgstr "Aquest codi postal no és vàlid."
@ -2534,127 +2567,135 @@ msgstr "La integració escollida no és vàlida."
msgid "The merchant key is not valid." msgid "The merchant key is not valid."
msgstr "Aquesta clau del comerç no és vàlid." msgstr "Aquesta clau del comerç no és vàlid."
#: pkg/booking/public.go:325 pkg/booking/public.go:354 #: pkg/booking/public.go:275 pkg/booking/public.go:304
msgid "Arrival date must be a valid date." msgid "Arrival date must be a valid date."
msgstr "La data darribada ha de ser una data vàlida." msgstr "La data darribada ha de ser una data vàlida."
#: pkg/booking/public.go:339 pkg/booking/public.go:361 #: pkg/booking/public.go:289 pkg/booking/public.go:311
msgid "Departure date must be a valid date." msgid "Departure date must be a valid date."
msgstr "La data de sortida ha de ser una data vàlida." msgstr "La data de sortida ha de ser una data vàlida."
#: pkg/booking/public.go:353 #: pkg/booking/public.go:303
msgid "Arrival date can not be empty" msgid "Arrival date can not be empty"
msgstr "No podeu deixar la data darribada en blanc." msgstr "No podeu deixar la data darribada en blanc."
#: pkg/booking/public.go:355 #: pkg/booking/public.go:305
#, c-format #, c-format
msgid "Arrival date must be %s or after." msgid "Arrival date must be %s or after."
msgstr "La data darribada ha de ser igual o posterior a %s." msgstr "La data darribada ha de ser igual o posterior a %s."
#: pkg/booking/public.go:356 #: pkg/booking/public.go:306
#, c-format #, c-format
msgid "Arrival date must be %s or before." msgid "Arrival date must be %s or before."
msgstr "La data darribada ha de ser anterior o igual a %s." msgstr "La data darribada ha de ser anterior o igual a %s."
#: pkg/booking/public.go:360 #: pkg/booking/public.go:310
msgid "Departure date can not be empty" msgid "Departure date can not be empty"
msgstr "No podeu deixar la data de sortida en blanc." msgstr "No podeu deixar la data de sortida en blanc."
#: pkg/booking/public.go:362 #: pkg/booking/public.go:312
#, c-format #, c-format
msgid "Departure date must be %s or after." msgid "Departure date must be %s or after."
msgstr "La data de sortida ha de ser igual o posterior a %s." msgstr "La data de sortida ha de ser igual o posterior a %s."
#: pkg/booking/public.go:363 #: pkg/booking/public.go:313
#, c-format #, c-format
msgid "Departure date must be %s or before." msgid "Departure date must be %s or before."
msgstr "La data de sortida ha de ser anterior o igual a %s." msgstr "La data de sortida ha de ser anterior o igual a %s."
#: pkg/booking/public.go:405 #: pkg/booking/public.go:355
#, c-format #, c-format
msgid "There can be at most %d guests in this accommodation." msgid "There can be at most %d guests in this accommodation."
msgstr "Hi poden haver com a màxim %d convidats a aquest allotjament." msgstr "Hi poden haver com a màxim %d convidats a aquest allotjament."
#: pkg/booking/public.go:425 #: pkg/booking/public.go:375
msgid "Number of adults can not be empty" msgid "Number of adults can not be empty"
msgstr "No podeu deixar el número dadults en blanc." msgstr "No podeu deixar el número dadults en blanc."
#: pkg/booking/public.go:426 #: pkg/booking/public.go:376
msgid "Number of adults must be an integer." msgid "Number of adults must be an integer."
msgstr "El número dadults ha de ser enter." msgstr "El número dadults ha de ser enter."
#: pkg/booking/public.go:427 #: pkg/booking/public.go:377
msgid "There must be at least one adult." msgid "There must be at least one adult."
msgstr "Hi ha dhaver com a mínim un adult." msgstr "Hi ha dhaver com a mínim un adult."
#: pkg/booking/public.go:430 #: pkg/booking/public.go:380
msgid "Number of teenagers can not be empty" msgid "Number of teenagers can not be empty"
msgstr "No podeu deixar el número dadolescents en blanc." msgstr "No podeu deixar el número dadolescents en blanc."
#: pkg/booking/public.go:431 #: pkg/booking/public.go:381
msgid "Number of teenagers must be an integer." msgid "Number of teenagers must be an integer."
msgstr "El número dadolescents ha de ser enter." msgstr "El número dadolescents ha de ser enter."
#: pkg/booking/public.go:432 #: pkg/booking/public.go:382
msgid "Number of teenagers can not be negative." msgid "Number of teenagers can not be negative."
msgstr "El número dadolescents no pot ser negatiu." msgstr "El número dadolescents no pot ser negatiu."
#: pkg/booking/public.go:435 #: pkg/booking/public.go:385
msgid "Number of children can not be empty" msgid "Number of children can not be empty"
msgstr "No podeu deixar el número de nens en blanc." msgstr "No podeu deixar el número de nens en blanc."
#: pkg/booking/public.go:436 #: pkg/booking/public.go:386
msgid "Number of children must be an integer." msgid "Number of children must be an integer."
msgstr "El número de nens ha de ser enter." msgstr "El número de nens ha de ser enter."
#: pkg/booking/public.go:437 #: pkg/booking/public.go:387
msgid "Number of children can not be negative." msgid "Number of children can not be negative."
msgstr "El número de nens no pot ser negatiu." msgstr "El número de nens no pot ser negatiu."
#: pkg/booking/public.go:440 #: pkg/booking/public.go:390
msgid "Number of dogs can not be empty" msgid "Number of dogs can not be empty"
msgstr "No podeu deixar el número de gossos en blanc." msgstr "No podeu deixar el número de gossos en blanc."
#: pkg/booking/public.go:441 #: pkg/booking/public.go:391
msgid "Number of dogs must be an integer." msgid "Number of dogs must be an integer."
msgstr "El número de gossos ha de ser enter." msgstr "El número de gossos ha de ser enter."
#: pkg/booking/public.go:442 #: pkg/booking/public.go:392
msgid "Number of dogs can not be negative." msgid "Number of dogs can not be negative."
msgstr "El número de gossos no pot ser negatiu." msgstr "El número de gossos no pot ser negatiu."
#: pkg/booking/public.go:513 #: pkg/booking/public.go:463
#, c-format #, c-format
msgid "%s can not be empty" msgid "%s can not be empty"
msgstr "No podeu deixar %s en blanc." msgstr "No podeu deixar %s en blanc."
#: pkg/booking/public.go:514 #: pkg/booking/public.go:464
#, c-format #, c-format
msgid "%s must be an integer." msgid "%s must be an integer."
msgstr "%s ha de ser un número enter." msgstr "%s ha de ser un número enter."
#: pkg/booking/public.go:515 #: pkg/booking/public.go:465
#, c-format #, c-format
msgid "%s must be %d or greater." msgid "%s must be %d or greater."
msgstr "El valor de %s ha de ser com a mínim %d." msgstr "El valor de %s ha de ser com a mínim %d."
#: pkg/booking/public.go:516 #: pkg/booking/public.go:466
#, c-format #, c-format
msgid "%s must be at most %d." msgid "%s must be at most %d."
msgstr "El valor de %s ha de ser com a màxim %d." msgstr "El valor de %s ha de ser com a màxim %d."
#: pkg/booking/public.go:574 #: pkg/booking/public.go:524
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."
#: pkg/booking/public.go:575 #: pkg/booking/public.go:525
msgid "Full name must have at least one letter." msgid "Full name must have at least one letter."
msgstr "El nom i els cognoms han de tenir com a mínim una lletra." msgstr "El nom i els cognoms han de tenir com a mínim una lletra."
#: pkg/booking/public.go:592 #: pkg/booking/public.go:542
msgid "It is mandatory to agree to the reservation conditions." msgid "It is mandatory to agree to the reservation conditions."
msgstr "És obligatori acceptar les condicions de reserves." msgstr "És obligatori acceptar les condicions de reserves."
#~ msgctxt "title"
#~ msgid "Payment"
#~ msgstr "Pagament"
#~ msgctxt "action"
#~ msgid "Pay"
#~ msgstr "Paga"
#~ msgctxt "input" #~ msgctxt "input"
#~ msgid "Check-in Date" #~ msgid "Check-in Date"
#~ msgstr "Data dentrada" #~ msgstr "Data dentrada"

135
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: 2024-02-12 05:10+0100\n" "POT-Creation-Date: 2024-02-12 17:49+0100\n"
"PO-Revision-Date: 2024-02-06 10:04+0100\n" "PO-Revision-Date: 2024-02-06 10:04+0100\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"
@ -60,28 +60,56 @@ msgid "Zone 1"
msgstr "Zona 1" msgstr "Zona 1"
#: web/templates/public/payment/success.gohtml:6 #: web/templates/public/payment/success.gohtml:6
#: web/templates/public/payment/success.gohtml:11
msgctxt "title" msgctxt "title"
msgid "Payment Successful" msgid "Payment Successful"
msgstr "Pago con éxito" msgstr "Pago con éxito"
#: web/templates/public/payment/request.gohtml:6 #: web/templates/public/payment/success.gohtml:12
#: web/templates/public/payment/request.gohtml:11 msgid "We have received the payment. Thank you."
msgctxt "title" msgstr "Hemos recibido su pago. Gracias."
msgid "Payment"
msgstr "Pago"
#: web/templates/public/payment/request.gohtml:23 #: web/templates/public/payment/request.gohtml:6
msgctxt "title"
msgid "Booking Payment"
msgstr "Pago de la reserva"
#: web/templates/public/payment/request.gohtml:18
msgid "Thank you for your booking. Please, click the button below to pay with a credit card via Servired/Redsys."
msgstr "Gracias por vuestro pedido. Haced clic al botón de abajo para pagar con tarjeta de crédito vía Servired/Redsys."
#: web/templates/public/payment/request.gohtml:27
msgctxt "action" msgctxt "action"
msgid "Pay" msgid "Pay with credit card"
msgstr "Pagar" msgstr "Pagar con tarjeta de crédito"
#: web/templates/public/payment/request.gohtml:30
msgid "Please, wait until we redirect you to the payment page."
msgstr "Esperad mientras se os redirige a la página de pago."
#: web/templates/public/payment/failure.gohtml:6 #: web/templates/public/payment/failure.gohtml:6
#: web/templates/public/payment/failure.gohtml:11
msgctxt "title" msgctxt "title"
msgid "Payment Failed" msgid "Payment Failed"
msgstr "Pago fallido" msgstr "Pago fallido"
#: web/templates/public/payment/failure.gohtml:12
msgid "We could not process the payment. Please, contact us for support."
msgstr "No hemos podido procesar su pago. Póngase en contacto con nosotros para solucionar el problema."
#: web/templates/public/payment/details.gohtml:4
msgctxt "title"
msgid "Order Number"
msgstr "Número de pedido"
#: web/templates/public/payment/details.gohtml:8
msgctxt "title"
msgid "Date"
msgstr "Fecha"
#: web/templates/public/payment/details.gohtml:12
msgctxt "title"
msgid "Total"
msgstr "Total"
#: web/templates/public/services.gohtml:7 #: web/templates/public/services.gohtml:7
#: web/templates/public/services.gohtml:16 #: web/templates/public/services.gohtml:16
#: web/templates/public/layout.gohtml:67 web/templates/public/layout.gohtml:95 #: web/templates/public/layout.gohtml:67 web/templates/public/layout.gohtml:95
@ -1951,6 +1979,11 @@ msgstr "Estado"
msgid "No booking found." msgid "No booking found."
msgstr "No se ha encontrado ninguna reserva." msgstr "No se ha encontrado ninguna reserva."
#: pkg/payment/public.go:97
msgctxt "order product name"
msgid "Campsite Booking"
msgstr "Reserva de camping"
#: pkg/legal/admin.go:258 pkg/app/user.go:249 pkg/campsite/types/option.go:365 #: pkg/legal/admin.go:258 pkg/app/user.go:249 pkg/campsite/types/option.go:365
#: pkg/campsite/types/feature.go:272 pkg/campsite/types/admin.go:577 #: pkg/campsite/types/feature.go:272 pkg/campsite/types/admin.go:577
#: pkg/campsite/feature.go:269 pkg/season/admin.go:412 #: pkg/campsite/feature.go:269 pkg/season/admin.go:412
@ -1990,12 +2023,12 @@ msgid "Slide image must be an image media type."
msgstr "La imagen de la diapositiva tiene que ser un medio de tipo imagen." msgstr "La imagen de la diapositiva tiene que ser un medio de tipo imagen."
#: pkg/app/login.go:56 pkg/app/user.go:246 pkg/company/admin.go:217 #: pkg/app/login.go:56 pkg/app/user.go:246 pkg/company/admin.go:217
#: pkg/booking/public.go:583 #: pkg/booking/public.go:533
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/company/admin.go:218 #: pkg/app/login.go:57 pkg/app/user.go:247 pkg/company/admin.go:218
#: pkg/booking/public.go:584 #: pkg/booking/public.go:534
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."
@ -2206,8 +2239,8 @@ msgctxt "header"
msgid "Children (aged 2 to 10)" msgid "Children (aged 2 to 10)"
msgstr "Niños (de 2 a 10 años)" msgstr "Niños (de 2 a 10 años)"
#: pkg/campsite/admin.go:275 pkg/booking/public.go:224 #: pkg/campsite/admin.go:275 pkg/booking/public.go:171
#: pkg/booking/public.go:276 #: pkg/booking/public.go:226
msgid "Selected campsite type is not valid." msgid "Selected campsite type is not valid."
msgstr "El tipo de alojamiento escogido no es válido." msgstr "El tipo de alojamiento escogido no es válido."
@ -2363,7 +2396,7 @@ msgstr "No podéis dejar la dirección del enlace en blanco."
msgid "This web address is not valid. It should be like https://domain.com/." 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/." msgstr "Esta dirección web no es válida. Tiene que ser parecido a https://dominio.com/."
#: pkg/company/admin.go:200 pkg/booking/public.go:570 #: pkg/company/admin.go:200 pkg/booking/public.go:520
msgid "Selected country is not valid." msgid "Selected country is not valid."
msgstr "El país escogido no es válido." msgstr "El país escogido no es válido."
@ -2383,11 +2416,11 @@ msgstr "No podéis dejar el NIF en blanco."
msgid "This VAT number is not valid." msgid "This VAT number is not valid."
msgstr "Este NIF no es válido." msgstr "Este NIF no es válido."
#: pkg/company/admin.go:212 pkg/booking/public.go:586 #: pkg/company/admin.go:212 pkg/booking/public.go:536
msgid "Phone can not be empty." msgid "Phone can not be empty."
msgstr "No podéis dejar el teléfono en blanco." msgstr "No podéis dejar el teléfono en blanco."
#: pkg/company/admin.go:213 pkg/booking/public.go:587 #: pkg/company/admin.go:213 pkg/booking/public.go:537
msgid "This phone number is not valid." msgid "This phone number is not valid."
msgstr "Este teléfono no es válido." msgstr "Este teléfono no es válido."
@ -2407,7 +2440,7 @@ msgstr "No podéis dejar la provincia en blanco."
msgid "Postal code can not be empty." msgid "Postal code can not be empty."
msgstr "No podéis dejar el código postal en blanco." msgstr "No podéis dejar el código postal en blanco."
#: pkg/company/admin.go:227 pkg/booking/public.go:579 #: pkg/company/admin.go:227 pkg/booking/public.go:529
msgid "This postal code is not valid." msgid "This postal code is not valid."
msgstr "Este código postal no es válido." msgstr "Este código postal no es válido."
@ -2534,127 +2567,135 @@ msgstr "La integración escogida no es válida."
msgid "The merchant key is not valid." msgid "The merchant key is not valid."
msgstr "Esta clave del comercio no es válida." msgstr "Esta clave del comercio no es válida."
#: pkg/booking/public.go:325 pkg/booking/public.go:354 #: pkg/booking/public.go:275 pkg/booking/public.go:304
msgid "Arrival date must be a valid date." msgid "Arrival date must be a valid date."
msgstr "La fecha de llegada tiene que ser una fecha válida." msgstr "La fecha de llegada tiene que ser una fecha válida."
#: pkg/booking/public.go:339 pkg/booking/public.go:361 #: pkg/booking/public.go:289 pkg/booking/public.go:311
msgid "Departure date must be a valid date." msgid "Departure date must be a valid date."
msgstr "La fecha de partida tiene que ser una fecha válida." msgstr "La fecha de partida tiene que ser una fecha válida."
#: pkg/booking/public.go:353 #: pkg/booking/public.go:303
msgid "Arrival date can not be empty" msgid "Arrival date can not be empty"
msgstr "No podéis dejar la fecha de llegada en blanco." msgstr "No podéis dejar la fecha de llegada en blanco."
#: pkg/booking/public.go:355 #: pkg/booking/public.go:305
#, c-format #, c-format
msgid "Arrival date must be %s or after." msgid "Arrival date must be %s or after."
msgstr "La fecha de llegada tiene que ser igual o posterior a %s." msgstr "La fecha de llegada tiene que ser igual o posterior a %s."
#: pkg/booking/public.go:356 #: pkg/booking/public.go:306
#, c-format #, c-format
msgid "Arrival date must be %s or before." msgid "Arrival date must be %s or before."
msgstr "La fecha de llegada tiene que ser anterior o igual a %s." msgstr "La fecha de llegada tiene que ser anterior o igual a %s."
#: pkg/booking/public.go:360 #: pkg/booking/public.go:310
msgid "Departure date can not be empty" msgid "Departure date can not be empty"
msgstr "No podéis dejar la fecha de partida en blanco." msgstr "No podéis dejar la fecha de partida en blanco."
#: pkg/booking/public.go:362 #: pkg/booking/public.go:312
#, c-format #, c-format
msgid "Departure date must be %s or after." msgid "Departure date must be %s or after."
msgstr "La fecha de partida tiene que igual o posterior a %s." msgstr "La fecha de partida tiene que igual o posterior a %s."
#: pkg/booking/public.go:363 #: pkg/booking/public.go:313
#, c-format #, c-format
msgid "Departure date must be %s or before." msgid "Departure date must be %s or before."
msgstr "La fecha de partida tiene que ser anterior o igual a %s." msgstr "La fecha de partida tiene que ser anterior o igual a %s."
#: pkg/booking/public.go:405 #: pkg/booking/public.go:355
#, c-format #, c-format
msgid "There can be at most %d guests in this accommodation." msgid "There can be at most %d guests in this accommodation."
msgstr "Solo puede haber como máximo %d invitados en este alojamiento." msgstr "Solo puede haber como máximo %d invitados en este alojamiento."
#: pkg/booking/public.go:425 #: pkg/booking/public.go:375
msgid "Number of adults can not be empty" msgid "Number of adults can not be empty"
msgstr "No podéis dejar el número de adultos blanco." msgstr "No podéis dejar el número de adultos blanco."
#: pkg/booking/public.go:426 #: pkg/booking/public.go:376
msgid "Number of adults must be an integer." msgid "Number of adults must be an integer."
msgstr "El número de adultos tiene que ser entero." msgstr "El número de adultos tiene que ser entero."
#: pkg/booking/public.go:427 #: pkg/booking/public.go:377
msgid "There must be at least one adult." msgid "There must be at least one adult."
msgstr "Tiene que haber como mínimo un adulto." msgstr "Tiene que haber como mínimo un adulto."
#: pkg/booking/public.go:430 #: pkg/booking/public.go:380
msgid "Number of teenagers can not be empty" msgid "Number of teenagers can not be empty"
msgstr "No podéis dejar el número de adolescentes en blanco." msgstr "No podéis dejar el número de adolescentes en blanco."
#: pkg/booking/public.go:431 #: pkg/booking/public.go:381
msgid "Number of teenagers must be an integer." msgid "Number of teenagers must be an integer."
msgstr "El número de adolescentes tiene que ser entero." msgstr "El número de adolescentes tiene que ser entero."
#: pkg/booking/public.go:432 #: pkg/booking/public.go:382
msgid "Number of teenagers can not be negative." msgid "Number of teenagers can not be negative."
msgstr "El número de adolescentes no puede ser negativo." msgstr "El número de adolescentes no puede ser negativo."
#: pkg/booking/public.go:435 #: pkg/booking/public.go:385
msgid "Number of children can not be empty" msgid "Number of children can not be empty"
msgstr "No podéis dejar el número de niños en blanco." msgstr "No podéis dejar el número de niños en blanco."
#: pkg/booking/public.go:436 #: pkg/booking/public.go:386
msgid "Number of children must be an integer." msgid "Number of children must be an integer."
msgstr "El número de niños tiene que ser entero." msgstr "El número de niños tiene que ser entero."
#: pkg/booking/public.go:437 #: pkg/booking/public.go:387
msgid "Number of children can not be negative." msgid "Number of children can not be negative."
msgstr "El número de niños no puede ser negativo." msgstr "El número de niños no puede ser negativo."
#: pkg/booking/public.go:440 #: pkg/booking/public.go:390
msgid "Number of dogs can not be empty" msgid "Number of dogs can not be empty"
msgstr "No podéis dejar el número de perros en blanco." msgstr "No podéis dejar el número de perros en blanco."
#: pkg/booking/public.go:441 #: pkg/booking/public.go:391
msgid "Number of dogs must be an integer." msgid "Number of dogs must be an integer."
msgstr "El número de perros tiene que ser entero." msgstr "El número de perros tiene que ser entero."
#: pkg/booking/public.go:442 #: pkg/booking/public.go:392
msgid "Number of dogs can not be negative." msgid "Number of dogs can not be negative."
msgstr "El número de perros no puede ser negativo." msgstr "El número de perros no puede ser negativo."
#: pkg/booking/public.go:513 #: pkg/booking/public.go:463
#, c-format #, c-format
msgid "%s can not be empty" msgid "%s can not be empty"
msgstr "No podéis dejar %s en blanco." msgstr "No podéis dejar %s en blanco."
#: pkg/booking/public.go:514 #: pkg/booking/public.go:464
#, c-format #, c-format
msgid "%s must be an integer." msgid "%s must be an integer."
msgstr "%s tiene que ser un número entero." msgstr "%s tiene que ser un número entero."
#: pkg/booking/public.go:515 #: pkg/booking/public.go:465
#, c-format #, c-format
msgid "%s must be %d or greater." msgid "%s must be %d or greater."
msgstr "%s tiene que ser como mínimo %d." msgstr "%s tiene que ser como mínimo %d."
#: pkg/booking/public.go:516 #: pkg/booking/public.go:466
#, c-format #, c-format
msgid "%s must be at most %d." msgid "%s must be at most %d."
msgstr "%s tiene que ser como máximo %d" msgstr "%s tiene que ser como máximo %d"
#: pkg/booking/public.go:574 #: pkg/booking/public.go:524
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."
#: pkg/booking/public.go:575 #: pkg/booking/public.go:525
msgid "Full name must have at least one letter." msgid "Full name must have at least one letter."
msgstr "El nombre y los apellidos tienen que tener como mínimo una letra." msgstr "El nombre y los apellidos tienen que tener como mínimo una letra."
#: pkg/booking/public.go:592 #: pkg/booking/public.go:542
msgid "It is mandatory to agree to the reservation conditions." msgid "It is mandatory to agree to the reservation conditions."
msgstr "Es obligatorio aceptar las condiciones de reserva." msgstr "Es obligatorio aceptar las condiciones de reserva."
#~ msgctxt "title"
#~ msgid "Payment"
#~ msgstr "Pago"
#~ msgctxt "action"
#~ msgid "Pay"
#~ msgstr "Pagar"
#~ msgctxt "input" #~ msgctxt "input"
#~ msgid "Check-in Date" #~ msgid "Check-in Date"
#~ msgstr "Fecha de entrada" #~ msgstr "Fecha de entrada"

135
po/fr.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: 2024-02-12 05:10+0100\n" "POT-Creation-Date: 2024-02-12 17:49+0100\n"
"PO-Revision-Date: 2024-02-06 10:05+0100\n" "PO-Revision-Date: 2024-02-06 10:05+0100\n"
"Last-Translator: Oriol Carbonell <info@oriolcarbonell.cat>\n" "Last-Translator: Oriol Carbonell <info@oriolcarbonell.cat>\n"
"Language-Team: French <traduc@traduc.org>\n" "Language-Team: French <traduc@traduc.org>\n"
@ -60,28 +60,56 @@ msgid "Zone 1"
msgstr "Zone 1" msgstr "Zone 1"
#: web/templates/public/payment/success.gohtml:6 #: web/templates/public/payment/success.gohtml:6
#: web/templates/public/payment/success.gohtml:11
msgctxt "title" msgctxt "title"
msgid "Payment Successful" msgid "Payment Successful"
msgstr "Paiement réussi" msgstr "Paiement réussi"
#: web/templates/public/payment/request.gohtml:6 #: web/templates/public/payment/success.gohtml:12
#: web/templates/public/payment/request.gohtml:11 msgid "We have received the payment. Thank you."
msgctxt "title" msgstr "Nous avons reçu le paiement. Merci."
msgid "Payment"
msgstr "Paiement"
#: web/templates/public/payment/request.gohtml:23 #: web/templates/public/payment/request.gohtml:6
msgctxt "title"
msgid "Booking Payment"
msgstr "Paiement de la réservation"
#: web/templates/public/payment/request.gohtml:18
msgid "Thank you for your booking. Please, click the button below to pay with a credit card via Servired/Redsys."
msgstr "Merci pour votre réservation. Veuillez cliquer sur le bouton ci-dessous pour payer avec une carte de crédit via Servired/Redsys."
#: web/templates/public/payment/request.gohtml:27
msgctxt "action" msgctxt "action"
msgid "Pay" msgid "Pay with credit card"
msgstr "Payer" msgstr "Payez avec une carte de crédit"
#: web/templates/public/payment/request.gohtml:30
msgid "Please, wait until we redirect you to the payment page."
msgstr "Veuillez attendre que nous vous redirigeons vers la page de paiement."
#: web/templates/public/payment/failure.gohtml:6 #: web/templates/public/payment/failure.gohtml:6
#: web/templates/public/payment/failure.gohtml:11
msgctxt "title" msgctxt "title"
msgid "Payment Failed" msgid "Payment Failed"
msgstr "Le paiement a échoué" msgstr "Le paiement a échoué"
#: web/templates/public/payment/failure.gohtml:12
msgid "We could not process the payment. Please, contact us for support."
msgstr "Nous navons pas pu traiter le paiement. Sil vous plaît, contactez-nous pour obtenir de laide."
#: web/templates/public/payment/details.gohtml:4
msgctxt "title"
msgid "Order Number"
msgstr "Numéro de commande"
#: web/templates/public/payment/details.gohtml:8
msgctxt "title"
msgid "Date"
msgstr "Date"
#: web/templates/public/payment/details.gohtml:12
msgctxt "title"
msgid "Total"
msgstr "Totale"
#: web/templates/public/services.gohtml:7 #: web/templates/public/services.gohtml:7
#: web/templates/public/services.gohtml:16 #: web/templates/public/services.gohtml:16
#: web/templates/public/layout.gohtml:67 web/templates/public/layout.gohtml:95 #: web/templates/public/layout.gohtml:67 web/templates/public/layout.gohtml:95
@ -1951,6 +1979,11 @@ msgstr "Statut"
msgid "No booking found." msgid "No booking found."
msgstr "Aucune réservation trouvée." msgstr "Aucune réservation trouvée."
#: pkg/payment/public.go:97
msgctxt "order product name"
msgid "Campsite Booking"
msgstr "Réservation camping"
#: pkg/legal/admin.go:258 pkg/app/user.go:249 pkg/campsite/types/option.go:365 #: pkg/legal/admin.go:258 pkg/app/user.go:249 pkg/campsite/types/option.go:365
#: pkg/campsite/types/feature.go:272 pkg/campsite/types/admin.go:577 #: pkg/campsite/types/feature.go:272 pkg/campsite/types/admin.go:577
#: pkg/campsite/feature.go:269 pkg/season/admin.go:412 #: pkg/campsite/feature.go:269 pkg/season/admin.go:412
@ -1990,12 +2023,12 @@ msgid "Slide image must be an image media type."
msgstr "Limage de la diapositive doit être de type média dimage." msgstr "Limage de la diapositive doit être de type média dimage."
#: pkg/app/login.go:56 pkg/app/user.go:246 pkg/company/admin.go:217 #: pkg/app/login.go:56 pkg/app/user.go:246 pkg/company/admin.go:217
#: pkg/booking/public.go:583 #: pkg/booking/public.go:533
msgid "Email can not be empty." msgid "Email can not be empty."
msgstr "Le-mail ne peut pas être vide." msgstr "Le-mail ne peut pas être vide."
#: pkg/app/login.go:57 pkg/app/user.go:247 pkg/company/admin.go:218 #: pkg/app/login.go:57 pkg/app/user.go:247 pkg/company/admin.go:218
#: pkg/booking/public.go:584 #: pkg/booking/public.go:534
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 "Cette adresse e-mail nest pas valide. Il devrait en être name@domain.com." msgstr "Cette adresse e-mail nest pas valide. Il devrait en être name@domain.com."
@ -2206,8 +2239,8 @@ msgctxt "header"
msgid "Children (aged 2 to 10)" msgid "Children (aged 2 to 10)"
msgstr "Enfants (de 2 à 10 anys)" msgstr "Enfants (de 2 à 10 anys)"
#: pkg/campsite/admin.go:275 pkg/booking/public.go:224 #: pkg/campsite/admin.go:275 pkg/booking/public.go:171
#: pkg/booking/public.go:276 #: pkg/booking/public.go:226
msgid "Selected campsite type is not valid." msgid "Selected campsite type is not valid."
msgstr "Le type demplacement sélectionné nest pas valide." msgstr "Le type demplacement sélectionné nest pas valide."
@ -2363,7 +2396,7 @@ msgstr "Laddresse du lien ne peut pas être vide."
msgid "This web address is not valid. It should be like https://domain.com/." msgid "This web address is not valid. It should be like https://domain.com/."
msgstr "Cette adresse web nest pas valide. Il devrait en être https://domain.com/." msgstr "Cette adresse web nest pas valide. Il devrait en être https://domain.com/."
#: pkg/company/admin.go:200 pkg/booking/public.go:570 #: pkg/company/admin.go:200 pkg/booking/public.go:520
msgid "Selected country is not valid." msgid "Selected country is not valid."
msgstr "Le pays sélectionné nest pas valide." msgstr "Le pays sélectionné nest pas valide."
@ -2383,11 +2416,11 @@ msgstr "Le numéro de TVA ne peut pas être vide."
msgid "This VAT number is not valid." msgid "This VAT number is not valid."
msgstr "Ce numéro de TVA nest pas valide." msgstr "Ce numéro de TVA nest pas valide."
#: pkg/company/admin.go:212 pkg/booking/public.go:586 #: pkg/company/admin.go:212 pkg/booking/public.go:536
msgid "Phone can not be empty." msgid "Phone can not be empty."
msgstr "Le téléphone ne peut pas être vide." msgstr "Le téléphone ne peut pas être vide."
#: pkg/company/admin.go:213 pkg/booking/public.go:587 #: pkg/company/admin.go:213 pkg/booking/public.go:537
msgid "This phone number is not valid." msgid "This phone number is not valid."
msgstr "Ce numéro de téléphone nest pas valide." msgstr "Ce numéro de téléphone nest pas valide."
@ -2407,7 +2440,7 @@ msgstr "La province ne peut pas être vide."
msgid "Postal code can not be empty." msgid "Postal code can not be empty."
msgstr "Le code postal ne peut pas être vide." msgstr "Le code postal ne peut pas être vide."
#: pkg/company/admin.go:227 pkg/booking/public.go:579 #: pkg/company/admin.go:227 pkg/booking/public.go:529
msgid "This postal code is not valid." msgid "This postal code is not valid."
msgstr "Ce code postal nest pas valide." msgstr "Ce code postal nest pas valide."
@ -2534,127 +2567,135 @@ msgstr "Lintégration sélectionnée nest pas valide."
msgid "The merchant key is not valid." msgid "The merchant key is not valid."
msgstr "La clé marchand nest pas valide." msgstr "La clé marchand nest pas valide."
#: pkg/booking/public.go:325 pkg/booking/public.go:354 #: pkg/booking/public.go:275 pkg/booking/public.go:304
msgid "Arrival date must be a valid date." msgid "Arrival date must be a valid date."
msgstr "La date darrivée doit être une date valide." msgstr "La date darrivée doit être une date valide."
#: pkg/booking/public.go:339 pkg/booking/public.go:361 #: pkg/booking/public.go:289 pkg/booking/public.go:311
msgid "Departure date must be a valid date." msgid "Departure date must be a valid date."
msgstr "La date de départ doit être une date valide." msgstr "La date de départ doit être une date valide."
#: pkg/booking/public.go:353 #: pkg/booking/public.go:303
msgid "Arrival date can not be empty" msgid "Arrival date can not be empty"
msgstr "La date darrivée ne peut pas être vide" msgstr "La date darrivée ne peut pas être vide"
#: pkg/booking/public.go:355 #: pkg/booking/public.go:305
#, c-format #, c-format
msgid "Arrival date must be %s or after." msgid "Arrival date must be %s or after."
msgstr "La date darrivée doit être égale ou postérieure à %s." msgstr "La date darrivée doit être égale ou postérieure à %s."
#: pkg/booking/public.go:356 #: pkg/booking/public.go:306
#, c-format #, c-format
msgid "Arrival date must be %s or before." msgid "Arrival date must be %s or before."
msgstr "La date darrivée doit être antérieure ou égale à %s." msgstr "La date darrivée doit être antérieure ou égale à %s."
#: pkg/booking/public.go:360 #: pkg/booking/public.go:310
msgid "Departure date can not be empty" msgid "Departure date can not be empty"
msgstr "La date de départ ne peut pas être vide" msgstr "La date de départ ne peut pas être vide"
#: pkg/booking/public.go:362 #: pkg/booking/public.go:312
#, c-format #, c-format
msgid "Departure date must be %s or after." msgid "Departure date must be %s or after."
msgstr "La date de départ doit être égale ou postérieure à %s." msgstr "La date de départ doit être égale ou postérieure à %s."
#: pkg/booking/public.go:363 #: pkg/booking/public.go:313
#, c-format #, c-format
msgid "Departure date must be %s or before." msgid "Departure date must be %s or before."
msgstr "La date de départ doit être antérieure ou égale à %s." msgstr "La date de départ doit être antérieure ou égale à %s."
#: pkg/booking/public.go:405 #: pkg/booking/public.go:355
#, c-format #, c-format
msgid "There can be at most %d guests in this accommodation." msgid "There can be at most %d guests in this accommodation."
msgstr "Il peut y avoir au plus %d invités dans cet hébergement." msgstr "Il peut y avoir au plus %d invités dans cet hébergement."
#: pkg/booking/public.go:425 #: pkg/booking/public.go:375
msgid "Number of adults can not be empty" msgid "Number of adults can not be empty"
msgstr "Le nombre dadultes ne peut pas être vide." msgstr "Le nombre dadultes ne peut pas être vide."
#: pkg/booking/public.go:426 #: pkg/booking/public.go:376
msgid "Number of adults must be an integer." msgid "Number of adults must be an integer."
msgstr "Le nombre dadultes doit être un entier." msgstr "Le nombre dadultes doit être un entier."
#: pkg/booking/public.go:427 #: pkg/booking/public.go:377
msgid "There must be at least one adult." msgid "There must be at least one adult."
msgstr "Il doit y avoir au moins un adulte." msgstr "Il doit y avoir au moins un adulte."
#: pkg/booking/public.go:430 #: pkg/booking/public.go:380
msgid "Number of teenagers can not be empty" msgid "Number of teenagers can not be empty"
msgstr "Le nombre dadolescents ne peut pas être vide." msgstr "Le nombre dadolescents ne peut pas être vide."
#: pkg/booking/public.go:431 #: pkg/booking/public.go:381
msgid "Number of teenagers must be an integer." msgid "Number of teenagers must be an integer."
msgstr "Le nombre dadolescents doit être un entier." msgstr "Le nombre dadolescents doit être un entier."
#: pkg/booking/public.go:432 #: pkg/booking/public.go:382
msgid "Number of teenagers can not be negative." msgid "Number of teenagers can not be negative."
msgstr "Le nombre dadolescents ne peut pas être négatif." msgstr "Le nombre dadolescents ne peut pas être négatif."
#: pkg/booking/public.go:435 #: pkg/booking/public.go:385
msgid "Number of children can not be empty" msgid "Number of children can not be empty"
msgstr "Le nombre denfants ne peut pas être vide." msgstr "Le nombre denfants ne peut pas être vide."
#: pkg/booking/public.go:436 #: pkg/booking/public.go:386
msgid "Number of children must be an integer." msgid "Number of children must be an integer."
msgstr "Le nombre denfants doit être un entier." msgstr "Le nombre denfants doit être un entier."
#: pkg/booking/public.go:437 #: pkg/booking/public.go:387
msgid "Number of children can not be negative." msgid "Number of children can not be negative."
msgstr "Le nombre denfants ne peut pas être négatif." msgstr "Le nombre denfants ne peut pas être négatif."
#: pkg/booking/public.go:440 #: pkg/booking/public.go:390
msgid "Number of dogs can not be empty" msgid "Number of dogs can not be empty"
msgstr "Le nombre de chiens ne peut pas être vide." msgstr "Le nombre de chiens ne peut pas être vide."
#: pkg/booking/public.go:441 #: pkg/booking/public.go:391
msgid "Number of dogs must be an integer." msgid "Number of dogs must be an integer."
msgstr "Le nombre de chiens nuits être un entier." msgstr "Le nombre de chiens nuits être un entier."
#: pkg/booking/public.go:442 #: pkg/booking/public.go:392
msgid "Number of dogs can not be negative." msgid "Number of dogs can not be negative."
msgstr "Le nombre de chiens ne peut pas être négatif." msgstr "Le nombre de chiens ne peut pas être négatif."
#: pkg/booking/public.go:513 #: pkg/booking/public.go:463
#, c-format #, c-format
msgid "%s can not be empty" msgid "%s can not be empty"
msgstr "%s ne peut pas être vide" msgstr "%s ne peut pas être vide"
#: pkg/booking/public.go:514 #: pkg/booking/public.go:464
#, c-format #, c-format
msgid "%s must be an integer." msgid "%s must be an integer."
msgstr "%s doit être un entier." msgstr "%s doit être un entier."
#: pkg/booking/public.go:515 #: pkg/booking/public.go:465
#, c-format #, c-format
msgid "%s must be %d or greater." msgid "%s must be %d or greater."
msgstr "%s doit être %d ou plus." msgstr "%s doit être %d ou plus."
#: pkg/booking/public.go:516 #: pkg/booking/public.go:466
#, c-format #, c-format
msgid "%s must be at most %d." msgid "%s must be at most %d."
msgstr "%s doit être tout au plus %d." msgstr "%s doit être tout au plus %d."
#: pkg/booking/public.go:574 #: pkg/booking/public.go:524
msgid "Full name can not be empty." msgid "Full name can not be empty."
msgstr "Le nom complet ne peut pas être vide." msgstr "Le nom complet ne peut pas être vide."
#: pkg/booking/public.go:575 #: pkg/booking/public.go:525
msgid "Full name must have at least one letter." msgid "Full name must have at least one letter."
msgstr "Le nom complet doit comporter au moins une lettre." msgstr "Le nom complet doit comporter au moins une lettre."
#: pkg/booking/public.go:592 #: pkg/booking/public.go:542
msgid "It is mandatory to agree to the reservation conditions." msgid "It is mandatory to agree to the reservation conditions."
msgstr "Il est obligatoire daccepter les conditions de réservation." msgstr "Il est obligatoire daccepter les conditions de réservation."
#~ msgctxt "title"
#~ msgid "Payment"
#~ msgstr "Paiement"
#~ msgctxt "action"
#~ msgid "Pay"
#~ msgstr "Payer"
#~ msgctxt "input" #~ msgctxt "input"
#~ msgid "Check-in Date" #~ msgid "Check-in Date"
#~ msgstr "Date d'arrivée" #~ msgstr "Date d'arrivée"

7
revert/ready_payment.sql Normal file
View File

@ -0,0 +1,7 @@
-- Revert camper:ready_payment from pg
begin;
drop function if exists camper.ready_payment(uuid, text, text, text, text, camper.country_code, camper.email, text, text, boolean);
commit;

View File

@ -247,3 +247,4 @@ payment [roles schema_camper company campsite_type payment_status] 2024-02-11T21
payment_customer [roles schema_camper payment country country_code extension_pg_libphonenumber] 2024-02-12T00:10:20Z jordi fita mas <jordi@tandem.blog> # Add relation of payment customer payment_customer [roles schema_camper payment country country_code extension_pg_libphonenumber] 2024-02-12T00:10:20Z jordi fita mas <jordi@tandem.blog> # Add relation of payment customer
payment_option [roles schema_camper payment campsite_type_option] 2024-02-12T00:58:07Z jordi fita mas <jordi@tandem.blog> # Add relation of payment for campsite type options payment_option [roles schema_camper payment campsite_type_option] 2024-02-12T00:58:07Z jordi fita mas <jordi@tandem.blog> # Add relation of payment for campsite type options
draft_payment [roles schema_camper season_calendar season campsite_type campsite_type_pet_cost campsite_type_cost campsite_type_option_cost campsite_type_option payment payment_option] 2024-02-12T01:31:52Z jordi fita mas <jordi@tandem.blog> # Add function to create a payment draft draft_payment [roles schema_camper season_calendar season campsite_type campsite_type_pet_cost campsite_type_cost campsite_type_option_cost campsite_type_option payment payment_option] 2024-02-12T01:31:52Z jordi fita mas <jordi@tandem.blog> # Add function to create a payment draft
ready_payment [roles schema_camper payment payment_customer country_code email extension_pg_libphonenumber] 2024-02-12T12:57:24Z jordi fita mas <jordi@tandem.blog> # Add function to ready a draft payment

View File

@ -115,9 +115,9 @@ select lives_ok(
); );
select bag_eq( select bag_eq(
$$ select company_id, campsite_type_id, arrival_date::text, departure_date::text, subtotal_nights, number_adults, subtotal_adults, number_teenagers, subtotal_teenagers, number_children, subtotal_children, number_dogs, subtotal_dogs, subtotal_tourist_tax, total, zone_preferences, created_at, updated_at from payment $$, $$ select company_id, campsite_type_id, arrival_date::text, departure_date::text, subtotal_nights, number_adults, subtotal_adults, number_teenagers, subtotal_teenagers, number_children, subtotal_children, number_dogs, subtotal_dogs, subtotal_tourist_tax, total, zone_preferences, payment_status, created_at, updated_at from payment $$,
$$ values (2, 12, '2024-08-28', '2024-09-04', 3200, 2, 10420, 4, 20840, 6, 25080, 3, 2450, 4900, 79160, 'pref I before E', '2024-01-01 01:01:01', current_timestamp) $$ values (2, 12, '2024-08-28', '2024-09-04', 3200, 2, 10420, 4, 20840, 6, 25080, 3, 2450, 4900, 79160, 'pref I before E', 'draft', '2024-01-01 01:01:01', current_timestamp)
, (2, 14, '2024-08-29', '2024-09-03', 71000, 1, 0, 2, 0, 3, 0, 0, 0, 1750, 72750, '', current_timestamp, current_timestamp) , (2, 14, '2024-08-29', '2024-09-03', 71000, 1, 0, 2, 0, 3, 0, 0, 0, 1750, 72750, '', 'draft', current_timestamp, current_timestamp)
$$, $$,
'Should have added and updated payments' 'Should have added and updated payments'
); );

View File

@ -5,13 +5,13 @@ reset client_min_messages;
begin; begin;
select plan(47); select plan(53);
set search_path to camper, public; set search_path to camper, public;
select has_table('payment_customer'); select has_table('payment_customer');
select has_pk('payment_customer'); select has_pk('payment_customer');
select table_privs_are('payment_customer', 'guest', array['SELECT', 'INSERT']); select table_privs_are('payment_customer', 'guest', array['SELECT', 'INSERT', 'UPDATE']);
select table_privs_are('payment_customer', 'employee', array['SELECT', 'INSERT', 'UPDATE']); select table_privs_are('payment_customer', 'employee', array['SELECT', 'INSERT', 'UPDATE']);
select table_privs_are('payment_customer', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']); select table_privs_are('payment_customer', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('payment_customer', 'authenticator', array[]::text[]); select table_privs_are('payment_customer', 'authenticator', array[]::text[]);
@ -66,6 +66,13 @@ select col_type_is('payment_customer', 'acsi_card', 'boolean');
select col_not_null('payment_customer', 'acsi_card'); select col_not_null('payment_customer', 'acsi_card');
select col_hasnt_default('payment_customer', 'acsi_card'); select col_hasnt_default('payment_customer', 'acsi_card');
select has_column('payment_customer', 'lang_tag');
select col_is_fk('payment_customer', 'lang_tag');
select fk_ok('payment_customer', 'lang_tag', 'language', 'lang_tag');
select col_type_is('payment_customer', 'lang_tag', 'text');
select col_not_null('payment_customer', 'lang_tag');
select col_hasnt_default('payment_customer', 'lang_tag');
select * select *
from finish(); from finish();

101
test/ready_payment.sql Normal file
View File

@ -0,0 +1,101 @@
-- Test ready_payment
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(15);
set search_path to camper, public;
select has_function('camper', 'ready_payment', array['uuid', 'text', 'text', 'text', 'text', 'country_code', 'email', 'text', 'text', 'boolean']);
select function_lang_is('camper', 'ready_payment', array['uuid', 'text', 'text', 'text', 'text', 'country_code', 'email', 'text', 'text', 'boolean'], 'plpgsql');
select function_returns('camper', 'ready_payment', array['uuid', 'text', 'text', 'text', 'text', 'country_code', 'email', 'text', 'text', 'boolean'], 'integer');
select isnt_definer('camper', 'ready_payment', array['uuid', 'text', 'text', 'text', 'text', 'country_code', 'email', 'text', 'text', 'boolean']);
select volatility_is('camper', 'ready_payment', array['uuid', 'text', 'text', 'text', 'text', 'country_code', 'email', 'text', 'text', 'boolean'], 'volatile');
select function_privs_are('camper', 'ready_payment', array ['uuid', 'text', 'text', 'text', 'text', 'country_code', 'email', 'text', 'text', 'boolean'], 'guest', array['EXECUTE']);
select function_privs_are('camper', 'ready_payment', array ['uuid', 'text', 'text', 'text', 'text', 'country_code', 'email', 'text', 'text', 'boolean'], 'employee', array['EXECUTE']);
select function_privs_are('camper', 'ready_payment', array ['uuid', 'text', 'text', 'text', 'text', 'country_code', 'email', 'text', 'text', 'boolean'], 'admin', array['EXECUTE']);
select function_privs_are('camper', 'ready_payment', array ['uuid', 'text', 'text', 'text', 'text', 'country_code', 'email', 'text', 'text', 'boolean'], 'authenticator', array[]::text[]);
set client_min_messages to warning;
truncate payment_customer cascade;
truncate payment cascade;
truncate campsite_type cascade;
truncate media cascade;
truncate media_content cascade;
truncate company cascade;
reset client_min_messages;
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, country_code, currency_code, default_lang_tag)
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 350, 'ES', 'EUR', 'ca')
;
insert into media_content (media_type, bytes)
values ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffffff","a"};')
;
insert into media (media_id, company_id, original_filename, content_hash)
values (10, 2, 'cover2.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};'))
;
insert into campsite_type (campsite_type_id, slug, company_id, name, media_id, max_campers, bookable_nights, overflow_allowed)
values (12, 'c1b6f4fc-32c1-4cd5-b796-0c5059152a52', 2, 'Plots', 10, 6, '[1, 7]', true)
;
insert into payment (payment_id, slug, company_id, campsite_type_id, arrival_date, departure_date, subtotal_nights, number_adults, subtotal_adults, number_teenagers, subtotal_teenagers, number_children, subtotal_children, number_dogs, subtotal_dogs, subtotal_tourist_tax, total, zone_preferences, payment_status, created_at, updated_at)
values (22, '4ef35e2f-ef98-42d6-a724-913bd761ca8c', 2, 12, '2024-08-28', '2024-09-04', 3200, 2, 10420, 4, 20840, 6, 25080, 3, 2450, 4900, 79160, 'pref I before E', 'draft', '2024-01-01 01:01:01', '2024-01-01')
, (24, '6d1b8e4c-c3c6-4fe4-92c1-2cbf94526693', 2, 12, '2024-08-29', '2024-09-03', 71000, 1, 0, 2, 0, 3, 0, 0, 0, 1750, 72750, '', 'draft', current_timestamp, current_timestamp)
;
insert into payment_customer (payment_id, full_name, address, postal_code, city, country_code, email, phone, acsi_card, lang_tag)
values (24, '', '', '', '', 'FR', 'a@a.com', '555-555-555', true, 'es')
;
select is(
ready_payment('4ef35e2f-ef98-42d6-a724-913bd761ca8c', 'Juli Verd', 'C/ Fals, 123', '17486', 'Castelló dEmpúries', 'ES', 'juli@verd.cat', '972486 160', 'ca', true),
22,
'Should be able to ready the first draft payment'
);
select is(
ready_payment('6d1b8e4c-c3c6-4fe4-92c1-2cbf94526693', 'Pere Gil', 'Gutenbergstr. 9-13', '82178', 'Puchheim', 'DE', 'pere@gil.de', '8980902-0', 'en', false),
24,
'Should be able to ready the second draft payment, and update customer details'
);
select throws_ok(
$$ select ready_payment('7ba7f0a5-d73d-4d6e-a9c4-3b88f2b2a357', 'Juli Verd', 'C/ Fals, 123', '17486', 'Castelló dEmpúries', 'ES', 'juli@verd.cat', '972 486 160', 'ca', true) $$,
'23514', 'insert or update on table "payment" violates check constraint "payment_is_draft"',
'Should not be able to ready an inexistent payment'
);
select throws_ok(
$$ select ready_payment('4ef35e2f-ef98-42d6-a724-913bd761ca8c', 'Juli Verd', 'C/ Fals, 123', '17486', 'Castelló dEmpúries', 'ES', 'juli@verd.cat', '972 486 160', 'ca', true) $$,
'23514', 'insert or update on table "payment" violates check constraint "payment_is_draft"',
'Should not be able to ready an already processed payment'
);
select bag_eq(
$$ select payment_id, slug::text, company_id, campsite_type_id, arrival_date::text, departure_date::text, subtotal_nights, number_adults, subtotal_adults, number_teenagers, subtotal_teenagers, number_children, subtotal_children, number_dogs, subtotal_dogs, subtotal_tourist_tax, total, zone_preferences, payment_status, created_at, updated_at from payment $$,
$$ values (22, '4ef35e2f-ef98-42d6-a724-913bd761ca8c', 2, 12, '2024-08-28', '2024-09-04', 3200, 2, 10420, 4, 20840, 6, 25080, 3, 2450, 4900, 79160, 'pref I before E', 'pending', '2024-01-01 01:01:01', current_timestamp)
, (24, '6d1b8e4c-c3c6-4fe4-92c1-2cbf94526693', 2, 12, '2024-08-29', '2024-09-03', 71000, 1, 0, 2, 0, 3, 0, 0, 0, 1750, 72750, '', 'pending', current_timestamp, current_timestamp)
$$,
'Should have updated payments'
);
select bag_eq(
$$ select payment_id, full_name, address, postal_code, city, country_code::text, email::text, phone::text, acsi_card, lang_tag from payment_customer $$,
$$ values (22, 'Juli Verd', 'C/ Fals, 123', '17486', 'Castelló dEmpúries', 'ES', 'juli@verd.cat', '+34 972 48 61 60', true, 'ca')
, (24, 'Pere Gil', 'Gutenbergstr. 9-13', '82178', 'Puchheim', 'DE', 'pere@gil.de', '+49 89 809020', false, 'en')
$$,
'Should have added and updated customer details'
);
select *
from finish();
rollback;

View File

@ -11,6 +11,7 @@ select payment_id
, email , email
, phone , phone
, acsi_card , acsi_card
, lang_tag
from camper.payment_customer from camper.payment_customer
where false; where false;

7
verify/ready_payment.sql Normal file
View File

@ -0,0 +1,7 @@
-- Verify camper:ready_payment on pg
begin;
select has_function_privilege('camper.ready_payment(uuid, text, text, text, text, camper.country_code, camper.email, text, text, boolean)', 'execute');
rollback;

View File

@ -140,6 +140,10 @@ p + p, dl + p {
margin-top: 1.5em; margin-top: 1.5em;
} }
dialog:modal {
margin: auto;
}
h2 { h2 {
font-size: 4.2rem; font-size: 4.2rem;
font-weight: 400; font-weight: 400;
@ -1526,6 +1530,16 @@ input[type="checkbox"]:focus {
width: 4.8rem; width: 4.8rem;
} }
dl.payment-details div {
flex-basis: unset;
min-height: unset;
padding: unset;
}
dl.payment-details dt {
padding: unset;
}
body > footer { body > footer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -1812,7 +1826,6 @@ dt {
z-index: 3; z-index: 3;
} }
.services_icon::before { .services_icon::before {
content: ''; content: '';
background: var(--accent-2); background: var(--accent-2);
@ -1825,17 +1838,41 @@ dt {
z-index: 1; z-index: 1;
} }
/*
.services_icon:hover::before {
background: var(--accent-2);
}
*/
.services_icon a { .services_icon a {
color: var(--accent); color: var(--accent);
text-decoration: var(--accent) wavy underline; text-decoration: var(--accent) wavy underline;
} }
/*<editor-fold desc="redirect dialog">*/
dialog.redirect:modal {
display: flex;
gap: 1rem;
}
dialog.redirect::before {
content: "";
display: inline-block;
width: 2.4rem;
aspect-ratio: 1;
border: 3px solid var(--contrast);
border-bottom-color: transparent;
border-radius: 50%;
animation: rotation linear 1s infinite;
}
@keyframes rotation {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
/*</editor-fold>*/
[x-cloak] { [x-cloak] {
display: none !important; display: none !important;
} }

View File

@ -0,0 +1,15 @@
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/payment.Payment*/ -}}
<dl class="payment-details">
<div>
<dt>{{( pgettext "Order Number" "title" )}}</dt>
<dd>{{ .OrderNumber }}</dd>
</div>
<div>
<dt>{{( pgettext "Date" "title" )}}</dt>
<dd>{{ .CreateTime | formatDate }}</dd>
</div>
<div>
<dt>{{( pgettext "Total" "title" )}}</dt>
<dd>{{ .Total | formatPrice }}</dd>
</div>
</dl>

View File

@ -7,6 +7,8 @@
{{- end }} {{- end }}
{{ define "content" -}} {{ define "content" -}}
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/booking.failedPaymentPage*/ -}} {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/payment.failedPaymentPage*/ -}}
<h2>{{( pgettext "Payment Failed" "title" )}}</h2> <h2>{{ template "title" . }}</h2>
<p>{{( gettext "We could not process the payment. Please, contact us for support." )}}</p>
{{ template "details.gohtml" .Payment }}
{{- end }} {{- end }}

View File

@ -3,24 +3,31 @@
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
--> -->
{{ define "title" -}} {{ define "title" -}}
{{( pgettext "Payment" "title" )}} {{( pgettext "Booking Payment" "title" )}}
{{- end }}
{{ define "head" -}}
{{ template "alpineScript" }}
{{- end }} {{- end }}
{{ define "content" -}} {{ define "content" -}}
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/booking.paymentPage*/ -}} {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/payment.paymentPage*/ -}}
<h2>{{( pgettext "Payment" "title" )}}</h2> <h2>{{ template "title" . }}</h2>
{{ template "details.gohtml" .Payment }}
{{ with .Request -}} {{ with .Request -}}
<form <p>{{( gettext "Thank you for your booking. Please, click the button below to pay with a credit card via Servired/Redsys.")}}</p>
{{ if eq $.Environment "live" -}} <form id="payment" method="post" x-init="$el.submit()"
action="https://sis.redsys.es/sis/realizarPago" {{- if eq $.Environment "live" }} action="https://sis.redsys.es/sis/realizarPago"
{{- else -}} {{- else }} action="https://sis-t.redsys.es:25443/sis/realizarPago"
action="https://sis-t.redsys.es:25443/sis/realizarPago" {{- end -}}
{{- end }} >
method="post">
<input type="hidden" name="Ds_MerchantParameters" value="{{ .MerchantParameters }}"/> <input type="hidden" name="Ds_MerchantParameters" value="{{ .MerchantParameters }}"/>
<input type="hidden" name="Ds_Signature" value="{{ .Signature }}"/> <input type="hidden" name="Ds_Signature" value="{{ .Signature }}"/>
<input type="hidden" name="Ds_SignatureVersion" value="{{ .SignatureVersion }}"/> <input type="hidden" name="Ds_SignatureVersion" value="{{ .SignatureVersion }}"/>
<button type="submit">{{( pgettext "Pay" "action" )}}</button> <button type="submit">{{( pgettext "Pay with credit card" "action" )}}</button>
</form> </form>
<dialog class="redirect" x-init="$el.showModal()">
<p>{{( gettext "Please, wait until we redirect you to the payment page.")}}</p>
</dialog>
{{- end }} {{- end }}
{{- end }} {{- end }}

View File

@ -7,6 +7,8 @@
{{- end }} {{- end }}
{{ define "content" -}} {{ define "content" -}}
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/booking.failedPaymentPage*/ -}} {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/payment.successfulPaymentPage*/ -}}
<h2>{{( pgettext "Payment Successful" "title" )}}</h2> <h2>{{ template "title" . }}</h2>
<p>{{( gettext "We have received the payment. Thank you." )}}</p>
{{ template "details.gohtml" .Payment }}
{{- end }} {{- end }}