Add down payment

Customer wants to require a down payment of 30 % for bookings made
one week or more before the actual date, and to make the full payment
otherwise.

This would require yet another relation to keep these values. Fuck it;
i added them to the function, as they are very unlikely to change.

That forced me to change the test for draft_payment to use relative
dates, otherwise there is no way i can have stable results in the
future.
This commit is contained in:
jordi fita mas 2024-02-13 23:45:25 +01:00
parent af31daba8a
commit bd84df8169
23 changed files with 434 additions and 134 deletions

23
deploy/down_payment.sql Normal file
View File

@ -0,0 +1,23 @@
-- Deploy camper:down_payment to pg
-- requires: roles
-- requires: schema_camper
-- requires: payment
begin;
set search_path to camper, public;
create or replace function down_payment(p payment) returns integer as
$$
select round(p.total * p.down_payment_percent)::integer;
$$
language sql
stable
;
revoke execute on function down_payment(payment) from public;
grant execute on function down_payment(payment) to guest;
grant execute on function down_payment(payment) to employee;
grant execute on function down_payment(payment) to admin;
commit;

View File

@ -42,6 +42,7 @@ begin
, subtotal_dogs
, subtotal_tourist_tax
, total
, down_payment_percent
, zone_preferences
)
select coalesce(payment_slug, gen_random_uuid())
@ -60,6 +61,7 @@ begin
, sum(case when num_dogs > 0 then coalesce(pet.cost_per_night, 0) else 0 end)::integer
, sum(tourist_tax * num_adults)::integer
, 0
, case when arrival_date - current_date >= 7 then 0.3 else 1.0 end
, coalesce(zone_preferences, '')
from generate_series(arrival_date, departure_date - 1, interval '1 day') as date(day)
left join season_calendar on season_range @> date.day::date
@ -87,6 +89,7 @@ begin
, subtotal_dogs = excluded.subtotal_dogs
, subtotal_tourist_tax = excluded.subtotal_tourist_tax
, total = excluded.total
, down_payment_percent = excluded.down_payment_percent
, zone_preferences = excluded.zone_preferences
, updated_at = current_timestamp
returning *
@ -134,6 +137,7 @@ begin
set total = subtotal_nights + subtotal_adults + subtotal_teenagers + subtotal_children + subtotal_dogs + subtotal_tourist_tax + coalesce(option.subtotal, 0)
from option
where payment_id = p.payment_id
returning total into p.total
;
else
delete
@ -143,6 +147,7 @@ begin
update payment
set total = subtotal_nights + subtotal_adults + subtotal_teenagers + subtotal_children + subtotal_dogs + subtotal_tourist_tax
where payment_id = p.payment_id
returning total into p.total
;
end if;

View File

@ -6,6 +6,7 @@
-- requires: payment_status
-- requires: positive_integer
-- requires: nonnegative_integer
-- requires: percentage
begin;
@ -29,6 +30,7 @@ create table payment (
subtotal_dogs nonnegative_integer not null,
subtotal_tourist_tax nonnegative_integer not null,
total nonnegative_integer not null,
down_payment_percent percentage not null default 1.0,
zone_preferences text not null,
payment_status text not null default 'draft' references payment_status,
created_at timestamp with time zone not null default current_timestamp,

14
deploy/percentage.sql Normal file
View File

@ -0,0 +1,14 @@
-- Deploy camper:percentage to pg
-- requires: schema_camper
begin;
set search_path to camper, public;
create domain percentage as numeric(3, 2)
check( value >= 0 and value <= 1 )
;
comment on domain percentage is 'a positive percentage without decimals, but represented as numeric';
commit;

View File

@ -12,6 +12,7 @@ import (
type bookingCart struct {
Lines []*cartLine
Total string
DownPayment string
Enabled bool
}
@ -93,6 +94,7 @@ func newBookingCart(ctx context.Context, conn *database.Conn, f *bookingForm, ca
, to_price(subtotal_dogs, decimal_digits)
, to_price(subtotal_tourist_tax, decimal_digits)
, to_price(total, decimal_digits)
, to_price(payment.down_payment, decimal_digits)
from draft_payment($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) as payment
join company using (company_id)
join currency using (currency_code)
@ -117,7 +119,20 @@ func newBookingCart(ctx context.Context, conn *database.Conn, f *bookingForm, ca
var dogs string
var touristTax string
var total string
if err = row.Scan(&f.PaymentSlug.Val, &paymentID, &numNights, &nights, &adults, &teenagers, &children, &dogs, &touristTax, &total); err != nil {
var downPayment string
if err = row.Scan(
&f.PaymentSlug.Val,
&paymentID,
&numNights,
&nights,
&adults,
&teenagers,
&children,
&dogs,
&touristTax,
&total,
&downPayment,
); err != nil {
if database.ErrorIsNotFound(err) {
return cart, nil
}
@ -177,6 +192,10 @@ func newBookingCart(ctx context.Context, conn *database.Conn, f *bookingForm, ca
if total != "0.0" {
cart.Total = total
cart.Enabled = f.Guests.Error == nil
if downPayment != total {
cart.DownPayment = downPayment
}
}
return cart, nil

View File

@ -76,6 +76,7 @@ func fetchPayment(ctx context.Context, conn *database.Conn, paymentSlug string)
, payment.slug::text
, payment.created_at
, to_price(total, decimal_digits)
, to_price(payment.down_payment, decimal_digits)
from payment
join company using (company_id)
join currency using (currency_code)
@ -83,7 +84,7 @@ func fetchPayment(ctx context.Context, conn *database.Conn, paymentSlug string)
and payment_status <> 'draft'
`, paymentSlug)
payment := &Payment{}
if err := row.Scan(&payment.ID, &payment.Slug, &payment.CreateTime, &payment.Total); err != nil {
if err := row.Scan(&payment.ID, &payment.Slug, &payment.CreateTime, &payment.Total, &payment.DownPayment); err != nil {
return nil, err
}
return payment, nil
@ -93,6 +94,7 @@ type Payment struct {
ID int
Slug string
Total string
DownPayment string
CreateTime time.Time
}
@ -102,7 +104,7 @@ func (payment *Payment) createRequest(r *http.Request, user *auth.User, company
baseURL := fmt.Sprintf("%s://%s/%s/payments/%s", schema, authority, user.Locale.Language, payment.Slug)
request := redsys.Request{
TransactionType: redsys.TransactionTypeCharge,
Amount: payment.Total,
Amount: payment.DownPayment,
OrderNumber: payment.OrderNumber(),
Product: user.Locale.Pgettext("Campsite Booking", "order product name"),
SuccessURL: fmt.Sprintf("%s/success", baseURL),
@ -269,6 +271,7 @@ type CompletedEmail struct {
ArrivalDate string
DepartureDate string
Total string
DownPayment string
CompanyAddress *address
}
@ -286,6 +289,7 @@ func sendEmail(ctx context.Context, conn *database.Conn, payment *Payment, compa
CurrentLocale: locale.Language.String(),
PaymentReference: payment.OrderNumber(),
Total: template.FormatPrice(payment.Total, locale.Language, locale.CurrencyPattern, company.DecimalDigits, company.CurrencySymbol),
DownPayment: template.FormatPrice(payment.DownPayment, locale.Language, locale.CurrencyPattern, company.DecimalDigits, company.CurrencySymbol),
CompanyAddress: &address{},
}

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: camper\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-02-13 05:09+0100\n"
"POT-Creation-Date: 2024-02-13 23:38+0100\n"
"PO-Revision-Date: 2024-02-06 10:04+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Catalan <ca@dodds.net>\n"
@ -95,7 +95,11 @@ msgid "Total: <strong>%s</strong>"
msgstr "Total: <strong>%s</strong>"
#: web/templates/mail/payment/body.gohtml:48
#: web/templates/mail/payment/body.gotxt:11
msgid "Down payment: <strong>%s</strong>"
msgstr "A compte: <strong>%s</strong>"
#: web/templates/mail/payment/body.gohtml:52
#: web/templates/mail/payment/body.gotxt:12
msgid "Thank you for your booking, and see you soon!"
msgstr "Moltes gràcies per la reserva i fins aviat!"
@ -119,6 +123,10 @@ msgstr "Data de sortida: **%s**"
msgid "Total: **%s**"
msgstr "Total: **%s**"
#: web/templates/mail/payment/body.gotxt:10
msgid "Down payment: **%s**"
msgstr "A compte: **%s**"
#: web/templates/public/payment/success.gohtml:6
msgctxt "title"
msgid "Payment Successful"
@ -170,6 +178,11 @@ msgctxt "title"
msgid "Total"
msgstr "Total"
#: web/templates/public/payment/details.gohtml:17
msgctxt "title"
msgid "Down Payment"
msgstr "A compte"
#: web/templates/public/services.gohtml:7
#: web/templates/public/services.gohtml:16
#: web/templates/public/layout.gohtml:67 web/templates/public/layout.gohtml:95
@ -243,7 +256,7 @@ msgid "Discover"
msgstr "Descobreix"
#: web/templates/public/campsite/type.gohtml:49
#: web/templates/public/booking/fields.gohtml:245
#: web/templates/public/booking/fields.gohtml:256
msgctxt "action"
msgid "Book"
msgstr "Reserva"
@ -366,13 +379,13 @@ msgid "Sun"
msgstr "dg"
#: web/templates/public/campsite/dates.gohtml:4
#: web/templates/public/booking/fields.gohtml:28
#: web/templates/public/booking/fields.gohtml:29
msgctxt "input"
msgid "Arrival date"
msgstr "Data darribada"
#: web/templates/public/campsite/dates.gohtml:15
#: web/templates/public/booking/fields.gohtml:39
#: web/templates/public/booking/fields.gohtml:40
msgctxt "input"
msgid "Departure date"
msgstr "Data de sortida"
@ -629,121 +642,126 @@ msgstr "Obertura"
msgid "<abbr title=\"Catalonia Tourism Registry\">RTC</abbr> <abbr title=\"Number\">#</abbr>%s"
msgstr "<abbr title=\"Número\">Núm.</abbr> <abbr title=\"Registre de Turisme de Catalunya\">RTC</abbr> %s"
#: web/templates/public/booking/fields.gohtml:15
#: web/templates/public/booking/fields.gohtml:14
msgctxt "title"
msgid "Accommodation"
msgstr "Allotjaments"
#: web/templates/public/booking/fields.gohtml:25
#: web/templates/public/booking/fields.gohtml:26
msgctxt "title"
msgid "Booking Period"
msgstr "Període de reserva"
#: web/templates/public/booking/fields.gohtml:52
#: web/templates/public/booking/fields.gohtml:55
msgctxt "title"
msgid "Guests"
msgstr "Hostes"
#: web/templates/public/booking/fields.gohtml:56
#: web/templates/public/booking/fields.gohtml:59
msgctxt "input"
msgid "Adults aged 17 or older"
msgstr "Adults de 17 anys o més"
#: web/templates/public/booking/fields.gohtml:67
#: web/templates/public/booking/fields.gohtml:70
msgctxt "input"
msgid "Teenagers from 11 to 16 years old"
msgstr "Adolescents dentre 11 i 16 anys"
#: web/templates/public/booking/fields.gohtml:78
#: web/templates/public/booking/fields.gohtml:81
msgctxt "input"
msgid "Children from 2 to 10 years old"
msgstr "Nens dentre 2 i 10 anys)"
#: web/templates/public/booking/fields.gohtml:88
#: web/templates/public/booking/fields.gohtml:91
msgid "Note: Due to guest capacity, we have added more accomodations to the booking, but we <strong>cannot</strong> guarantee that they will be next to each other."
msgstr "Nota: Shan afegit més allotjaments a la reserva degut a la capacitat de cadascuna, però <strong>no</strong> es garanteix que estiguin de costat."
#: web/templates/public/booking/fields.gohtml:96
#: web/templates/public/booking/fields.gohtml:99
msgctxt "input"
msgid "Dogs"
msgstr "Gossos"
#: web/templates/public/booking/fields.gohtml:105
#: web/templates/public/booking/fields.gohtml:108
msgid "Note: This accommodation does <strong>not</strong> allow dogs."
msgstr "Nota: A aquest allotjament <strong>no</strong> shi permeten gossos."
#: web/templates/public/booking/fields.gohtml:115
#: web/templates/public/booking/fields.gohtml:120
msgctxt "input"
msgid "Area preferences (optional)"
msgstr "Preferències dàrea (opcional)"
#: web/templates/public/booking/fields.gohtml:117
#: web/templates/public/booking/fields.gohtml:122
msgid "Campground map"
msgstr "Mapa del càmping"
#: web/templates/public/booking/fields.gohtml:140
#: web/templates/public/booking/fields.gohtml:145
msgctxt "title"
msgid "Customer Details"
msgstr "Detalls del client"
#: web/templates/public/booking/fields.gohtml:143
#: web/templates/public/booking/fields.gohtml:148
msgctxt "input"
msgid "Full name"
msgstr "Nom i cognoms"
#: web/templates/public/booking/fields.gohtml:152
#: web/templates/public/booking/fields.gohtml:157
msgctxt "input"
msgid "Address (optional)"
msgstr "Adreça (opcional)"
#: web/templates/public/booking/fields.gohtml:161
#: web/templates/public/booking/fields.gohtml:166
msgctxt "input"
msgid "Postcode (optional)"
msgstr "Codi postal (opcional)"
#: web/templates/public/booking/fields.gohtml:170
#: web/templates/public/booking/fields.gohtml:175
msgctxt "input"
msgid "Town or village (optional)"
msgstr "Població (opcional)"
#: web/templates/public/booking/fields.gohtml:179
#: web/templates/public/booking/fields.gohtml:184
#: web/templates/admin/taxDetails.gohtml:101
msgctxt "input"
msgid "Country"
msgstr "País"
#: web/templates/public/booking/fields.gohtml:182
#: web/templates/public/booking/fields.gohtml:187
msgid "Choose a country"
msgstr "Esculli un país"
#: web/templates/public/booking/fields.gohtml:190
#: web/templates/public/booking/fields.gohtml:195
#: web/templates/admin/login.gohtml:27 web/templates/admin/profile.gohtml:38
#: web/templates/admin/taxDetails.gohtml:53
msgctxt "input"
msgid "Email"
msgstr "Correu-e"
#: web/templates/public/booking/fields.gohtml:199
#: web/templates/public/booking/fields.gohtml:204
#: web/templates/admin/taxDetails.gohtml:45
msgctxt "input"
msgid "Phone"
msgstr "Telèfon"
#: web/templates/public/booking/fields.gohtml:210
#: web/templates/public/booking/fields.gohtml:215
msgctxt "input"
msgid "ACSI card? (optional)"
msgstr "Targeta ACSI? (opcional)"
#: web/templates/public/booking/fields.gohtml:217
#: web/templates/public/booking/fields.gohtml:222
msgctxt "input"
msgid "I have read and I accept %[1]sthe reservation conditions%[2]s"
msgstr "He llegit i accepto %[1]sles condicions de reserves%[2]s"
#: web/templates/public/booking/fields.gohtml:234
#: web/templates/public/booking/fields.gohtml:239
msgctxt "cart"
msgid "Total"
msgstr "Total"
#: web/templates/public/booking/fields.gohtml:244
msgctxt "cart"
msgid "Down payment"
msgstr "A compte"
#: web/templates/admin/legal/form.gohtml:8
#: web/templates/admin/legal/form.gohtml:29
msgctxt "title"
@ -2039,12 +2057,12 @@ msgstr "Estat"
msgid "No booking found."
msgstr "No sha trobat cap reserva."
#: pkg/payment/public.go:107
#: pkg/payment/public.go:109
msgctxt "order product name"
msgid "Campsite Booking"
msgstr "Reserva de càmping"
#: pkg/payment/public.go:344
#: pkg/payment/public.go:346
msgctxt "subject"
msgid "Booking payment successfully received"
msgstr "Rebut amb èxit el pagament de la reserva"
@ -2545,32 +2563,32 @@ msgstr "No podeu deixar el fitxer del mèdia en blanc."
msgid "Filename can not be empty."
msgstr "No podeu deixar el nom del fitxer en blanc."
#: pkg/booking/cart.go:144
#: pkg/booking/cart.go:151
msgctxt "cart"
msgid "Night"
msgstr "Nit"
#: pkg/booking/cart.go:145
#: pkg/booking/cart.go:152
msgctxt "cart"
msgid "Adult"
msgstr "Adult"
#: pkg/booking/cart.go:146
#: pkg/booking/cart.go:153
msgctxt "cart"
msgid "Teenager"
msgstr "Adolescent"
#: pkg/booking/cart.go:147
#: pkg/booking/cart.go:154
msgctxt "cart"
msgid "Child"
msgstr "Nen"
#: pkg/booking/cart.go:148
#: pkg/booking/cart.go:155
msgctxt "cart"
msgid "Dog"
msgstr "Gos"
#: pkg/booking/cart.go:183
#: pkg/booking/cart.go:190
msgctxt "cart"
msgid "Tourist tax"
msgstr "Impost turístic"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: camper\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-02-13 05:09+0100\n"
"POT-Creation-Date: 2024-02-13 23:38+0100\n"
"PO-Revision-Date: 2024-02-06 10:04+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Spanish <es@tp.org.es>\n"
@ -95,7 +95,11 @@ msgid "Total: <strong>%s</strong>"
msgstr "Total: <strong>%s</strong>"
#: web/templates/mail/payment/body.gohtml:48
#: web/templates/mail/payment/body.gotxt:11
msgid "Down payment: <strong>%s</strong>"
msgstr "A cuenta: <strong>%s</strong>"
#: web/templates/mail/payment/body.gohtml:52
#: web/templates/mail/payment/body.gotxt:12
msgid "Thank you for your booking, and see you soon!"
msgstr "Gracias por su reserva y ¡hasta pronto!"
@ -119,6 +123,10 @@ msgstr "Fecha de salida: **%s**"
msgid "Total: **%s**"
msgstr "Total: **%s**"
#: web/templates/mail/payment/body.gotxt:10
msgid "Down payment: **%s**"
msgstr "A cuenta: **%s**"
#: web/templates/public/payment/success.gohtml:6
msgctxt "title"
msgid "Payment Successful"
@ -170,6 +178,11 @@ msgctxt "title"
msgid "Total"
msgstr "Total"
#: web/templates/public/payment/details.gohtml:17
msgctxt "title"
msgid "Down Payment"
msgstr "A cuenta"
#: web/templates/public/services.gohtml:7
#: web/templates/public/services.gohtml:16
#: web/templates/public/layout.gohtml:67 web/templates/public/layout.gohtml:95
@ -243,7 +256,7 @@ msgid "Discover"
msgstr "Descubre"
#: web/templates/public/campsite/type.gohtml:49
#: web/templates/public/booking/fields.gohtml:245
#: web/templates/public/booking/fields.gohtml:256
msgctxt "action"
msgid "Book"
msgstr "Reservar"
@ -366,13 +379,13 @@ msgid "Sun"
msgstr "do"
#: web/templates/public/campsite/dates.gohtml:4
#: web/templates/public/booking/fields.gohtml:28
#: web/templates/public/booking/fields.gohtml:29
msgctxt "input"
msgid "Arrival date"
msgstr "Fecha de llegada"
#: web/templates/public/campsite/dates.gohtml:15
#: web/templates/public/booking/fields.gohtml:39
#: web/templates/public/booking/fields.gohtml:40
msgctxt "input"
msgid "Departure date"
msgstr "Fecha de salida"
@ -629,121 +642,126 @@ msgstr "Apertura"
msgid "<abbr title=\"Catalonia Tourism Registry\">RTC</abbr> <abbr title=\"Number\">#</abbr>%s"
msgstr "<abbr title=\"Número\">Nº</abbr> <abbr title=\"Registro de Turismo de Cataluña\">RTC</abbr> %s"
#: web/templates/public/booking/fields.gohtml:15
#: web/templates/public/booking/fields.gohtml:14
msgctxt "title"
msgid "Accommodation"
msgstr "Alojamientos"
#: web/templates/public/booking/fields.gohtml:25
#: web/templates/public/booking/fields.gohtml:26
msgctxt "title"
msgid "Booking Period"
msgstr "Periodo de reserva"
#: web/templates/public/booking/fields.gohtml:52
#: web/templates/public/booking/fields.gohtml:55
msgctxt "title"
msgid "Guests"
msgstr "Huéspedes"
#: web/templates/public/booking/fields.gohtml:56
#: web/templates/public/booking/fields.gohtml:59
msgctxt "input"
msgid "Adults aged 17 or older"
msgstr "Adultos de 17 años o más"
#: web/templates/public/booking/fields.gohtml:67
#: web/templates/public/booking/fields.gohtml:70
msgctxt "input"
msgid "Teenagers from 11 to 16 years old"
msgstr "Adolescentes de 11 a 16 años"
#: web/templates/public/booking/fields.gohtml:78
#: web/templates/public/booking/fields.gohtml:81
msgctxt "input"
msgid "Children from 2 to 10 years old"
msgstr "Niños de 2 a 10 años"
#: web/templates/public/booking/fields.gohtml:88
#: web/templates/public/booking/fields.gohtml:91
msgid "Note: Due to guest capacity, we have added more accomodations to the booking, but we <strong>cannot</strong> guarantee that they will be next to each other."
msgstr "Nota: Se han añadido alojamientos a la reserva debido a la capacidad de cada una, pero <strong>no</strong> se garantiza que estén de lado."
#: web/templates/public/booking/fields.gohtml:96
#: web/templates/public/booking/fields.gohtml:99
msgctxt "input"
msgid "Dogs"
msgstr "Perros"
#: web/templates/public/booking/fields.gohtml:105
#: web/templates/public/booking/fields.gohtml:108
msgid "Note: This accommodation does <strong>not</strong> allow dogs."
msgstr "Nota: En este alojamiento <strong>no</strong> se permiten perros."
#: web/templates/public/booking/fields.gohtml:115
#: web/templates/public/booking/fields.gohtml:120
msgctxt "input"
msgid "Area preferences (optional)"
msgstr "Preferencias de área (opcional)"
#: web/templates/public/booking/fields.gohtml:117
#: web/templates/public/booking/fields.gohtml:122
msgid "Campground map"
msgstr "Mapa del camping"
#: web/templates/public/booking/fields.gohtml:140
#: web/templates/public/booking/fields.gohtml:145
msgctxt "title"
msgid "Customer Details"
msgstr "Detalles del cliente"
#: web/templates/public/booking/fields.gohtml:143
#: web/templates/public/booking/fields.gohtml:148
msgctxt "input"
msgid "Full name"
msgstr "Nombre y apellidos"
#: web/templates/public/booking/fields.gohtml:152
#: web/templates/public/booking/fields.gohtml:157
msgctxt "input"
msgid "Address (optional)"
msgstr "Dirección (opcional)"
#: web/templates/public/booking/fields.gohtml:161
#: web/templates/public/booking/fields.gohtml:166
msgctxt "input"
msgid "Postcode (optional)"
msgstr "Código postal (opcional)"
#: web/templates/public/booking/fields.gohtml:170
#: web/templates/public/booking/fields.gohtml:175
msgctxt "input"
msgid "Town or village (optional)"
msgstr "Población (opcional)"
#: web/templates/public/booking/fields.gohtml:179
#: web/templates/public/booking/fields.gohtml:184
#: web/templates/admin/taxDetails.gohtml:101
msgctxt "input"
msgid "Country"
msgstr "País"
#: web/templates/public/booking/fields.gohtml:182
#: web/templates/public/booking/fields.gohtml:187
msgid "Choose a country"
msgstr "Escoja un país"
#: web/templates/public/booking/fields.gohtml:190
#: web/templates/public/booking/fields.gohtml:195
#: web/templates/admin/login.gohtml:27 web/templates/admin/profile.gohtml:38
#: web/templates/admin/taxDetails.gohtml:53
msgctxt "input"
msgid "Email"
msgstr "Correo-e"
#: web/templates/public/booking/fields.gohtml:199
#: web/templates/public/booking/fields.gohtml:204
#: web/templates/admin/taxDetails.gohtml:45
msgctxt "input"
msgid "Phone"
msgstr "Teléfono"
#: web/templates/public/booking/fields.gohtml:210
#: web/templates/public/booking/fields.gohtml:215
msgctxt "input"
msgid "ACSI card? (optional)"
msgstr "¿Tarjeta ACSI? (opcional)"
#: web/templates/public/booking/fields.gohtml:217
#: web/templates/public/booking/fields.gohtml:222
msgctxt "input"
msgid "I have read and I accept %[1]sthe reservation conditions%[2]s"
msgstr "He leído y acepto %[1]slas condiciones de reserva%[2]s"
#: web/templates/public/booking/fields.gohtml:234
#: web/templates/public/booking/fields.gohtml:239
msgctxt "cart"
msgid "Total"
msgstr "Total"
#: web/templates/public/booking/fields.gohtml:244
msgctxt "cart"
msgid "Down payment"
msgstr "A cuenta"
#: web/templates/admin/legal/form.gohtml:8
#: web/templates/admin/legal/form.gohtml:29
msgctxt "title"
@ -2039,12 +2057,12 @@ msgstr "Estado"
msgid "No booking found."
msgstr "No se ha encontrado ninguna reserva."
#: pkg/payment/public.go:107
#: pkg/payment/public.go:109
msgctxt "order product name"
msgid "Campsite Booking"
msgstr "Reserva de camping"
#: pkg/payment/public.go:344
#: pkg/payment/public.go:346
msgctxt "subject"
msgid "Booking payment successfully received"
msgstr "Se ha recibido correctamente el pago de la reserva"
@ -2545,32 +2563,32 @@ msgstr "No podéis dejar el archivo del medio en blanco."
msgid "Filename can not be empty."
msgstr "No podéis dejar el nombre del archivo en blanco."
#: pkg/booking/cart.go:144
#: pkg/booking/cart.go:151
msgctxt "cart"
msgid "Night"
msgstr "Noche"
#: pkg/booking/cart.go:145
#: pkg/booking/cart.go:152
msgctxt "cart"
msgid "Adult"
msgstr "Adulto"
#: pkg/booking/cart.go:146
#: pkg/booking/cart.go:153
msgctxt "cart"
msgid "Teenager"
msgstr "Adolescente"
#: pkg/booking/cart.go:147
#: pkg/booking/cart.go:154
msgctxt "cart"
msgid "Child"
msgstr "Niño"
#: pkg/booking/cart.go:148
#: pkg/booking/cart.go:155
msgctxt "cart"
msgid "Dog"
msgstr "Perro"
#: pkg/booking/cart.go:183
#: pkg/booking/cart.go:190
msgctxt "cart"
msgid "Tourist tax"
msgstr "Impuesto turístico"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: camper\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-02-13 05:02+0100\n"
"POT-Creation-Date: 2024-02-13 23:38+0100\n"
"PO-Revision-Date: 2024-02-06 10:05+0100\n"
"Last-Translator: Oriol Carbonell <info@oriolcarbonell.cat>\n"
"Language-Team: French <traduc@traduc.org>\n"
@ -95,7 +95,11 @@ msgid "Total: <strong>%s</strong>"
msgstr "Totale : <strong>%s</strong>"
#: web/templates/mail/payment/body.gohtml:48
#: web/templates/mail/payment/body.gotxt:11
msgid "Down payment: <strong>%s</strong>"
msgstr "Acompte : <strong>%s</strong>"
#: web/templates/mail/payment/body.gohtml:52
#: web/templates/mail/payment/body.gotxt:12
msgid "Thank you for your booking, and see you soon!"
msgstr "Merci pour votre réservation et à bientôt !"
@ -119,6 +123,10 @@ msgstr "Date de depart : **%s**"
msgid "Total: **%s**"
msgstr "Totale : **%s**"
#: web/templates/mail/payment/body.gotxt:10
msgid "Down payment: **%s**"
msgstr "Acompte : **%s**"
#: web/templates/public/payment/success.gohtml:6
msgctxt "title"
msgid "Payment Successful"
@ -170,6 +178,11 @@ msgctxt "title"
msgid "Total"
msgstr "Totale"
#: web/templates/public/payment/details.gohtml:17
msgctxt "title"
msgid "Down Payment"
msgstr "Acompte"
#: web/templates/public/services.gohtml:7
#: web/templates/public/services.gohtml:16
#: web/templates/public/layout.gohtml:67 web/templates/public/layout.gohtml:95
@ -243,7 +256,7 @@ msgid "Discover"
msgstr "Découvrir"
#: web/templates/public/campsite/type.gohtml:49
#: web/templates/public/booking/fields.gohtml:245
#: web/templates/public/booking/fields.gohtml:256
msgctxt "action"
msgid "Book"
msgstr "Réserver"
@ -366,13 +379,13 @@ msgid "Sun"
msgstr "Dim."
#: web/templates/public/campsite/dates.gohtml:4
#: web/templates/public/booking/fields.gohtml:28
#: web/templates/public/booking/fields.gohtml:29
msgctxt "input"
msgid "Arrival date"
msgstr "Date darrivée"
#: web/templates/public/campsite/dates.gohtml:15
#: web/templates/public/booking/fields.gohtml:39
#: web/templates/public/booking/fields.gohtml:40
msgctxt "input"
msgid "Departure date"
msgstr "Date de depart"
@ -629,121 +642,126 @@ msgstr "Ouverture"
msgid "<abbr title=\"Catalonia Tourism Registry\">RTC</abbr> <abbr title=\"Number\">#</abbr>%s"
msgstr "<abbr title=\"Registre du tourisme de Catalogne\"># RTC</abbr> %s"
#: web/templates/public/booking/fields.gohtml:15
#: web/templates/public/booking/fields.gohtml:14
msgctxt "title"
msgid "Accommodation"
msgstr "Hébergement"
#: web/templates/public/booking/fields.gohtml:25
#: web/templates/public/booking/fields.gohtml:26
msgctxt "title"
msgid "Booking Period"
msgstr "Période de réservation"
#: web/templates/public/booking/fields.gohtml:52
#: web/templates/public/booking/fields.gohtml:55
msgctxt "title"
msgid "Guests"
msgstr "Personnes logeant"
#: web/templates/public/booking/fields.gohtml:56
#: web/templates/public/booking/fields.gohtml:59
msgctxt "input"
msgid "Adults aged 17 or older"
msgstr "Adultes âgés 17 ans ou plus"
#: web/templates/public/booking/fields.gohtml:67
#: web/templates/public/booking/fields.gohtml:70
msgctxt "input"
msgid "Teenagers from 11 to 16 years old"
msgstr "Adolescents de 11 à 16 ans"
#: web/templates/public/booking/fields.gohtml:78
#: web/templates/public/booking/fields.gohtml:81
msgctxt "input"
msgid "Children from 2 to 10 years old"
msgstr "Enfants de 2 à 10 ans"
#: web/templates/public/booking/fields.gohtml:88
#: web/templates/public/booking/fields.gohtml:91
msgid "Note: Due to guest capacity, we have added more accomodations to the booking, but we <strong>cannot</strong> guarantee that they will be next to each other."
msgstr "Remarque : En raison de la capacité daccueils, nous avons ajouté dautres hébergements à la réservation, mais nous <strong>ne pouvons</strong> garantir quils seront côte à côte."
#: web/templates/public/booking/fields.gohtml:96
#: web/templates/public/booking/fields.gohtml:99
msgctxt "input"
msgid "Dogs"
msgstr "Chiens"
#: web/templates/public/booking/fields.gohtml:105
#: web/templates/public/booking/fields.gohtml:108
msgid "Note: This accommodation does <strong>not</strong> allow dogs."
msgstr "Remarque : Dans cet hébergement les chiens <strong>ne</strong> sont pas acceptés."
#: web/templates/public/booking/fields.gohtml:115
#: web/templates/public/booking/fields.gohtml:120
msgctxt "input"
msgid "Area preferences (optional)"
msgstr "Préférences de zone (facultatif)"
#: web/templates/public/booking/fields.gohtml:117
#: web/templates/public/booking/fields.gohtml:122
msgid "Campground map"
msgstr "Plan du camping"
#: web/templates/public/booking/fields.gohtml:140
#: web/templates/public/booking/fields.gohtml:145
msgctxt "title"
msgid "Customer Details"
msgstr "Détails du client"
#: web/templates/public/booking/fields.gohtml:143
#: web/templates/public/booking/fields.gohtml:148
msgctxt "input"
msgid "Full name"
msgstr "Nom et prénom"
#: web/templates/public/booking/fields.gohtml:152
#: web/templates/public/booking/fields.gohtml:157
msgctxt "input"
msgid "Address (optional)"
msgstr "Adresse (Facultatif)"
#: web/templates/public/booking/fields.gohtml:161
#: web/templates/public/booking/fields.gohtml:166
msgctxt "input"
msgid "Postcode (optional)"
msgstr "Code postal (Facultatif)"
#: web/templates/public/booking/fields.gohtml:170
#: web/templates/public/booking/fields.gohtml:175
msgctxt "input"
msgid "Town or village (optional)"
msgstr "Ville (Facultatif)"
#: web/templates/public/booking/fields.gohtml:179
#: web/templates/public/booking/fields.gohtml:184
#: web/templates/admin/taxDetails.gohtml:101
msgctxt "input"
msgid "Country"
msgstr "Pays"
#: web/templates/public/booking/fields.gohtml:182
#: web/templates/public/booking/fields.gohtml:187
msgid "Choose a country"
msgstr "Choisissez un pays"
#: web/templates/public/booking/fields.gohtml:190
#: web/templates/public/booking/fields.gohtml:195
#: web/templates/admin/login.gohtml:27 web/templates/admin/profile.gohtml:38
#: web/templates/admin/taxDetails.gohtml:53
msgctxt "input"
msgid "Email"
msgstr "E-mail"
#: web/templates/public/booking/fields.gohtml:199
#: web/templates/public/booking/fields.gohtml:204
#: web/templates/admin/taxDetails.gohtml:45
msgctxt "input"
msgid "Phone"
msgstr "Téléphone"
#: web/templates/public/booking/fields.gohtml:210
#: web/templates/public/booking/fields.gohtml:215
msgctxt "input"
msgid "ACSI card? (optional)"
msgstr "Carte ACSI ? (Facultatif)"
#: web/templates/public/booking/fields.gohtml:217
#: web/templates/public/booking/fields.gohtml:222
msgctxt "input"
msgid "I have read and I accept %[1]sthe reservation conditions%[2]s"
msgstr "Jai lu et jaccepte %[1]sles conditions de réservation%[2]s"
#: web/templates/public/booking/fields.gohtml:234
#: web/templates/public/booking/fields.gohtml:239
msgctxt "cart"
msgid "Total"
msgstr "Totale"
#: web/templates/public/booking/fields.gohtml:244
msgctxt "cart"
msgid "Down payment"
msgstr "Acompte"
#: web/templates/admin/legal/form.gohtml:8
#: web/templates/admin/legal/form.gohtml:29
msgctxt "title"
@ -2039,12 +2057,12 @@ msgstr "Statut"
msgid "No booking found."
msgstr "Aucune réservation trouvée."
#: pkg/payment/public.go:107
#: pkg/payment/public.go:109
msgctxt "order product name"
msgid "Campsite Booking"
msgstr "Réservation camping"
#: pkg/payment/public.go:344
#: pkg/payment/public.go:346
msgctxt "subject"
msgid "Booking payment successfully received"
msgstr "Paiement de réservation reçu avec succès"
@ -2545,32 +2563,32 @@ msgstr "Le fichier téléchargé ne peut pas être vide."
msgid "Filename can not be empty."
msgstr "Le nom de fichier ne peut pas être vide."
#: pkg/booking/cart.go:144
#: pkg/booking/cart.go:151
msgctxt "cart"
msgid "Night"
msgstr "Nuit"
#: pkg/booking/cart.go:145
#: pkg/booking/cart.go:152
msgctxt "cart"
msgid "Adult"
msgstr "Adulte"
#: pkg/booking/cart.go:146
#: pkg/booking/cart.go:153
msgctxt "cart"
msgid "Teenager"
msgstr "Adolescent"
#: pkg/booking/cart.go:147
#: pkg/booking/cart.go:154
msgctxt "cart"
msgid "Child"
msgstr "Enfant"
#: pkg/booking/cart.go:148
#: pkg/booking/cart.go:155
msgctxt "cart"
msgid "Dog"
msgstr "Chien"
#: pkg/booking/cart.go:183
#: pkg/booking/cart.go:190
msgctxt "cart"
msgid "Tourist tax"
msgstr "Taxe touristique"

7
revert/down_payment.sql Normal file
View File

@ -0,0 +1,7 @@
-- Revert camper:down_payment from pg
begin;
drop function if exists camper.down_payment(camper.payment);
commit;

7
revert/percentage.sql Normal file
View File

@ -0,0 +1,7 @@
-- Revert camper:percentage from pg
begin;
drop domain if exists camper.percentage;
commit;

View File

@ -245,7 +245,8 @@ payment_status [roles schema_camper] 2024-02-11T21:13:32Z jordi fita mas <jordi@
payment_status_i18n [roles schema_camper payment_status language] 2024-02-11T21:20:11Z jordi fita mas <jordi@tandem.blog> # Add relation for translation of payment status
available_payment_status [payment_status payment_status_i18n] 2024-02-11T21:22:38Z jordi fita mas <jordi@tandem.blog> # Add available payment statuses
positive_integer [schema_camper] 2024-02-13T20:24:09Z jordi fita mas <jordi@tandem.blog> # Add positive integer domain
payment [roles schema_camper company campsite_type payment_status positive_integer nonnegative_integer] 2024-02-11T21:54:13Z jordi fita mas <jordi@tandem.blog> # Add relation for payments
percentage [schema_camper] 2024-02-13T20:09:10Z jordi fita mas <jordi@tandem.blog> # Add percentage domain
payment [roles schema_camper company campsite_type payment_status positive_integer nonnegative_integer percentage] 2024-02-11T21:54:13Z jordi fita mas <jordi@tandem.blog> # Add relation for payments
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 positive_integer nonnegative_integer] 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
@ -255,3 +256,4 @@ decode_base64url [roles schema_camper] 2024-02-12T20:03:17Z jordi fita mas <jord
redsys_decode_response [roles schema_camper extension_pgcrypto decode_base64url redsys_encrypt redsys_response company currency to_price] 2024-02-12T20:52:09Z jordi fita mas <jordi@tandem.blog> # Add function to decode a Redsys signed response
payment_redsys_response [roles schema_camper payment currency_code] 2024-02-12T21:32:23Z jordi fita mas <jordi@tandem.blog> # Add relation for Redsys responses to payments
process_payment_response [roles schema_camper redsys_response payment payment_redsys_response parse_price currency] 2024-02-12T22:04:48Z jordi fita mas <jordi@tandem.blog> # Add function to process Redsys response of a payment
down_payment [roles schema_camper payment] 2024-02-13T21:53:44Z jordi fita mas <jordi@tandem.blog> # Add function to compute payment down payment from its percentage

76
test/down_payment.sql Normal file
View File

@ -0,0 +1,76 @@
-- Test down_payment
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(10);
set search_path to camper, public;
select has_function('camper', 'down_payment', array['payment']);
select function_lang_is('camper', 'down_payment', array['payment'], 'sql');
select function_returns('camper', 'down_payment', array['payment'], 'integer');
select isnt_definer('camper', 'down_payment', array['payment']);
select volatility_is('camper', 'down_payment', array['payment'], 'stable');
select function_privs_are('camper', 'down_payment', array['payment'], 'guest', array['EXECUTE']);
select function_privs_are('camper', 'down_payment', array['payment'], 'employee', array['EXECUTE']);
select function_privs_are('camper', 'down_payment', array['payment'], 'admin', array['EXECUTE']);
select function_privs_are('camper', 'down_payment', array['payment'], '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, 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, down_payment_percent, zone_preferences)
values (22, 2, 12, '2024-08-28', '2024-09-03', 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.0, '')
, (23, 2, 12, '2024-08-28', '2024-09-03', 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.5, '')
, (24, 2, 12, '2024-08-28', '2024-09-03', 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2500, 0.75, '')
, (25, 2, 12, '2024-08-28', '2024-09-03', 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1234, 0.99, '')
, (26, 2, 12, '2024-08-28', '2024-09-03', 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0.5, '')
, (27, 2, 12, '2024-08-28', '2024-09-03', 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 987654, 0.33, '')
, (28, 2, 12, '2024-08-28', '2024-09-03', 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 987654, 0.19, '')
;
select bag_eq(
$$ select payment_id, payment.down_payment from payment $$,
$$ values (22, 0)
, (23, 0)
, (24, 1875)
, (25, 1222)
, (26, 1)
, (27, 325926)
, (28, 187654)
$$,
'Should give out the down payment for each payment'
);
select *
from finish();
rollback;

View File

@ -5,7 +5,7 @@ reset client_min_messages;
begin;
select plan(14);
select plan(15);
set search_path to camper, public;
@ -46,9 +46,9 @@ values (4, 2, 'High')
;
insert into season_calendar (season_id, season_range)
values (4, '[2024-07-01,2024-08-30)')
, (6, '[2024-08-30,2024-09-03)')
, (8, '[2024-09-03,2024-09-08)')
values (4, daterange(current_date, current_date + 60))
, (6, daterange(current_date + 60, current_date + 64))
, (8, daterange(current_date + 64, current_date + 69))
;
insert into media_content (media_type, bytes)
@ -96,8 +96,8 @@ values (16, 4, 800)
;
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, '7cccfe16-695e-486d-a6a5-1162fb85cafb', 2, 12, '2024-08-30', '2024-09-01', 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, '', 'draft', '2024-01-01 01:01:01', '2024-01-01 01:01:01')
, (24, '6eeae04c-2fea-4d67-97dc-a4b8a83df99f', 2, 12, '2024-08-31', '2024-09-01', 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, '', 'pending', '2024-01-01 02:02:02', '2024-01-01 02:02:02')
values (22, '7cccfe16-695e-486d-a6a5-1162fb85cafb', 2, 12, current_date + 60, current_date + 62, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, '', 'draft', '2024-01-01 01:01:01', '2024-01-01 01:01:01')
, (24, '6eeae04c-2fea-4d67-97dc-a4b8a83df99f', 2, 12, current_date + 61, current_date + 62, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, '', 'pending', '2024-01-01 02:02:02', '2024-01-01 02:02:02')
;
insert into payment_option (payment_id, campsite_type_option_id, units, subtotal)
@ -107,27 +107,33 @@ values (22, 16, 1, 0)
;
select lives_ok(
$$ select draft_payment(null, '2024-08-29', '2024-09-03', 'b065f4e3-2cc8-491d-a413-d015d7d00183', 1, 2, 3, 0, null, null) $$,
$$ select draft_payment(null, current_date + 59, current_date + 64, 'b065f4e3-2cc8-491d-a413-d015d7d00183', 1, 2, 3, 0, null, null) $$,
'Should be able to create a new draft for Bungalows'
);
select lives_ok(
$$ select draft_payment('7cccfe16-695e-486d-a6a5-1162fb85cafb', '2024-08-28', '2024-09-04', 'c1b6f4fc-32c1-4cd5-b796-0c5059152a52', 2, 4, 6, 3, 'pref I before E', array[(16, 2), (20, 3)]::option_units[]) $$,
$$ select draft_payment(null, current_date + 6, current_date + 11, 'b065f4e3-2cc8-491d-a413-d015d7d00183', 1, 2, 3, 0, null, null) $$,
'A payment for a reservation in less than a week has a 100 % of downpayment'
);
select lives_ok(
$$ select draft_payment('7cccfe16-695e-486d-a6a5-1162fb85cafb', current_date + 58, current_date + 65, 'c1b6f4fc-32c1-4cd5-b796-0c5059152a52', 2, 4, 6, 3, 'pref I before E', array[(16, 2), (20, 3)]::option_units[]) $$,
'Should be able to update the draft for Plots'
);
select results_ne(
$$ select slug::text from draft_payment('6eeae04c-2fea-4d67-97dc-a4b8a83df99f', '2024-08-01', '2024-08-06', 'b065f4e3-2cc8-491d-a413-d015d7d00183', 2, 1, 1, 1, 'under a tree', array[(16, 1), (18, 1)]::option_units[]) $$,
$$ select slug::text from draft_payment('6eeae04c-2fea-4d67-97dc-a4b8a83df99f', current_date + 31, current_date + 36, 'b065f4e3-2cc8-491d-a413-d015d7d00183', 2, 1, 1, 1, 'under a tree', array[(16, 1), (18, 1)]::option_units[]) $$,
$$ values ('6eeae04c-2fea-4d67-97dc-a4b8a83df99f') $$,
'When trying to draft a payment already pending, completed, failed, or refunded, create a new instead'
);
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, 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', '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, '', 'draft', current_timestamp, current_timestamp)
, (2, 12, '2024-08-31', '2024-09-01', 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, '', 'pending', '2024-01-01 02:02:02', '2024-01-01 02:02:02')
, (2, 14, '2024-08-01', '2024-08-06', 85000, 2, 0, 1, 0, 1, 0, 1, 0, 3500, 96000, 'under a tree', 'draft', current_timestamp, current_timestamp)
$$ select 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, down_payment_percent, zone_preferences, payment_status, created_at, updated_at from payment $$,
$$ values (2, 12, current_date + 58, current_date + 65, 3200, 2, 10420, 4, 20840, 6, 25080, 3, 2450, 4900, 79160, 0.3, 'pref I before E', 'draft', '2024-01-01 01:01:01', current_timestamp)
, (2, 14, current_date + 59, current_date + 64, 71000, 1, 0, 2, 0, 3, 0, 0, 0, 1750, 72750, 0.3, '', 'draft', current_timestamp, current_timestamp)
, (2, 14, current_date + 6, current_date + 11, 85000, 1, 0, 2, 0, 3, 0, 0, 0, 1750, 86750, 1.0, '', 'draft', current_timestamp, current_timestamp)
, (2, 12, current_date + 61, current_date + 62, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.0, '', 'pending', '2024-01-01 02:02:02', '2024-01-01 02:02:02')
, (2, 14, current_date + 31, current_date + 36, 85000, 2, 0, 1, 0, 1, 0, 1, 0, 3500, 96000, 0.3, 'under a tree', 'draft', current_timestamp, current_timestamp)
$$,
'Should have added and updated payments'
);

View File

@ -5,7 +5,7 @@ reset client_min_messages;
begin;
select plan(103);
select plan(108);
set search_path to camper, public;
@ -108,6 +108,12 @@ select col_type_is('payment', 'total', 'nonnegative_integer');
select col_not_null('payment', 'total');
select col_hasnt_default('payment', 'total');
select has_column('payment', 'down_payment_percent');
select col_type_is('payment', 'down_payment_percent', 'percentage');
select col_not_null('payment', 'down_payment_percent');
select col_has_default('payment', 'down_payment_percent');
select col_default_is('payment', 'down_payment_percent', '1.0');
select has_column('payment', 'zone_preferences');
select col_type_is('payment', 'zone_preferences', 'text');
select col_not_null('payment', 'zone_preferences');

43
test/percentage.sql Normal file
View File

@ -0,0 +1,43 @@
-- Test percentage
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(10);
set search_path to camper, public;
select has_domain('percentage');
select domain_type_is('percentage', 'numeric');
select lives_ok($$ select 1.0::percentage $$);
select lives_ok($$ select 0.0::percentage $$);
select lives_ok($$ select 0.5::percentage $$);
select lives_ok($$ select 0.33::percentage $$);
select lives_ok($$ select 0.89::percentage $$);
select throws_ok(
$$ select 1.01::percentage $$,
23514, null,
'Maximum percentage is 100 %'
);
select throws_ok(
$$ select (-0.01)::percentage $$,
23514, null,
'Minimum percentage is 0 %'
);
select is(
0.001::percentage,
0.00::percentage,
'Percentage precission is 1 % (i.e., no decimals)'
);
select *
from finish();
rollback;

7
verify/down_payment.sql Normal file
View File

@ -0,0 +1,7 @@
-- Verify camper:down_payment on pg
begin;
select has_function_privilege('camper.down_payment(camper.payment)', 'execute');
rollback;

View File

@ -19,6 +19,7 @@ select payment_id
, subtotal_dogs
, subtotal_tourist_tax
, total
, down_payment_percent
, zone_preferences
, payment_status
, created_at

7
verify/percentage.sql Normal file
View File

@ -0,0 +1,7 @@
-- Verify camper:percentage on pg
begin;
select pg_catalog.has_type_privilege('camper.percentage', 'usage');
rollback;

View File

@ -43,6 +43,10 @@
{{ (printf ( gettext "Departure Date: <strong>%s</strong>") .DepartureDate) | raw }}
<br>
{{ (printf ( gettext "Total: <strong>%s</strong>") .Total) | raw }}
{{ if ne .Total .DownPayment -}}
<br>
{{ (printf ( gettext "Down payment: <strong>%s</strong>") .DownPayment) | raw }}
{{- end }}
</p>
<p style="font-family: Helvetica, sans-serif; font-size: 16px; font-weight: normal; margin: 0; margin-bottom: 16px;">
{{( gettext "Thank you for your booking, and see you soon!" )}}</p>

View File

@ -7,6 +7,7 @@
{{ printf ( gettext "Arrival Date: **%s**") .ArrivalDate }}
{{ printf ( gettext "Departure Date: **%s**") .DepartureDate }}
{{ printf ( gettext "Total: **%s**") .Total }}
{{ if ne .Total .DownPayment }} {{ printf ( gettext "Down payment: **%s**") .DownPayment }}{{ end }}
{{( gettext "Thank you for your booking, and see you soon!" )}}

View File

@ -239,6 +239,12 @@
<dt>{{( pgettext "Total" "cart" )}}</dt>
<dd>{{ formatPrice .Total }}</dd>
</div>
{{ if .DownPayment -}}
<div>
<dt>{{( pgettext "Down payment" "cart" )}}</dt>
<dd>{{ formatPrice .DownPayment }}</dd>
</div>
{{- end }}
</dl>
<div class="credit-cards">
<!-- @formatter:off -->

View File

@ -12,4 +12,10 @@
<dt>{{( pgettext "Total" "title" )}}</dt>
<dd>{{ .Total | formatPrice }}</dd>
</div>
{{ if ne .Total .DownPayment -}}
<div>
<dt>{{( pgettext "Down Payment" "title" )}}</dt>
<dd>{{ .DownPayment | formatPrice }}</dd>
</div>
{{- end }}
</dl>