255 lines
6.0 KiB
Go
255 lines
6.0 KiB
Go
package booking
|
|
|
|
import (
|
|
"context"
|
|
"strconv"
|
|
"time"
|
|
|
|
"dev.tandem.ws/tandem/camper/pkg/database"
|
|
"dev.tandem.ws/tandem/camper/pkg/locale"
|
|
)
|
|
|
|
type bookingCart struct {
|
|
Draft *paymentDraft
|
|
Lines []*cartLine
|
|
Total string
|
|
DownPayment string
|
|
DownPaymentPercent int
|
|
Enabled bool
|
|
}
|
|
|
|
type cartLine struct {
|
|
Concept string
|
|
Units int
|
|
Subtotal string
|
|
}
|
|
|
|
type paymentDraft struct {
|
|
NumAdults int
|
|
NumTeenagers int
|
|
NumChildren int
|
|
NumDogs int
|
|
NumNights int
|
|
Nights string
|
|
Adults string
|
|
Teenagers string
|
|
Children string
|
|
Dogs string
|
|
TouristTax string
|
|
Total string
|
|
DownPaymentPercent int
|
|
DownPayment string
|
|
Options []*paymentOption
|
|
}
|
|
|
|
type paymentOption struct {
|
|
ID int
|
|
Label string
|
|
Units int
|
|
Subtotal string
|
|
}
|
|
|
|
func draftPayment(ctx context.Context, conn *database.Conn, f *bookingForm, campsiteType string) (*paymentDraft, error) {
|
|
if f.Dates == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
if f.Guests == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
arrivalDate, err := time.Parse(database.ISODateFormat, f.Dates.ArrivalDate.Val)
|
|
if err != nil {
|
|
return nil, nil
|
|
}
|
|
departureDate, err := time.Parse(database.ISODateFormat, f.Dates.DepartureDate.Val)
|
|
if err != nil {
|
|
return nil, nil
|
|
}
|
|
|
|
draft := &paymentDraft{}
|
|
draft.NumAdults, err = strconv.Atoi(f.Guests.NumberAdults.Val)
|
|
if err != nil {
|
|
return nil, nil
|
|
}
|
|
draft.NumTeenagers, err = strconv.Atoi(f.Guests.NumberTeenagers.Val)
|
|
if err != nil {
|
|
return nil, nil
|
|
}
|
|
draft.NumChildren, err = strconv.Atoi(f.Guests.NumberChildren.Val)
|
|
if err != nil {
|
|
return nil, nil
|
|
}
|
|
if f.Guests.NumberDogs != nil {
|
|
draft.NumDogs, err = strconv.Atoi(f.Guests.NumberDogs.Val)
|
|
if err != nil {
|
|
return nil, nil
|
|
}
|
|
}
|
|
|
|
var zonePreferences string
|
|
if f.Options != nil && f.Options.ZonePreferences != nil {
|
|
zonePreferences = f.Options.ZonePreferences.Val
|
|
}
|
|
|
|
var acsiCard bool
|
|
if f.Guests.ACSICard != nil {
|
|
acsiCard = f.Guests.ACSICard.Checked
|
|
}
|
|
|
|
optionMap := make(map[int]*campsiteTypeOption)
|
|
var typeOptions []*campsiteTypeOption
|
|
if f.Options != nil {
|
|
typeOptions = f.Options.Options
|
|
}
|
|
optionUnits := make([]*database.OptionUnits, 0, len(typeOptions))
|
|
for _, option := range typeOptions {
|
|
units, _ := strconv.Atoi(option.Input.Val)
|
|
if units < 1 {
|
|
continue
|
|
}
|
|
optionMap[option.ID] = option
|
|
optionUnits = append(optionUnits, &database.OptionUnits{
|
|
OptionID: option.ID,
|
|
Units: units,
|
|
})
|
|
}
|
|
|
|
row := conn.QueryRow(ctx, `
|
|
select payment.slug
|
|
, payment_id
|
|
, departure_date - arrival_date
|
|
, to_price(subtotal_nights, decimal_digits)
|
|
, to_price(subtotal_adults, decimal_digits)
|
|
, to_price(subtotal_teenagers, decimal_digits)
|
|
, to_price(subtotal_children, decimal_digits)
|
|
, 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)
|
|
, (payment.down_payment_percent * 100)::int
|
|
from draft_payment($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) as payment
|
|
join currency using (currency_code)
|
|
`,
|
|
database.ZeroNullUUID(f.PaymentSlug.Val),
|
|
arrivalDate,
|
|
departureDate,
|
|
campsiteType,
|
|
draft.NumAdults,
|
|
draft.NumTeenagers,
|
|
draft.NumChildren,
|
|
draft.NumDogs,
|
|
zonePreferences,
|
|
acsiCard,
|
|
database.OptionUnitsArray(optionUnits),
|
|
)
|
|
var paymentID int
|
|
if err = row.Scan(
|
|
&f.PaymentSlug.Val,
|
|
&paymentID,
|
|
&draft.NumNights,
|
|
&draft.Nights,
|
|
&draft.Adults,
|
|
&draft.Teenagers,
|
|
&draft.Children,
|
|
&draft.Dogs,
|
|
&draft.TouristTax,
|
|
&draft.Total,
|
|
&draft.DownPayment,
|
|
&draft.DownPaymentPercent,
|
|
); err != nil {
|
|
if database.ErrorIsNotFound(err) {
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
rows, err := conn.Query(ctx, `
|
|
select campsite_type_option_id
|
|
, units
|
|
, to_price(subtotal, decimal_digits)
|
|
from payment_option
|
|
join payment using (payment_id)
|
|
join currency using (currency_code)
|
|
where payment_id = $1
|
|
order by campsite_type_option_id
|
|
`, paymentID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
var optionID int
|
|
var units int
|
|
var subtotal string
|
|
err = rows.Scan(&optionID, &units, &subtotal)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
option := optionMap[optionID]
|
|
if option == nil {
|
|
continue
|
|
}
|
|
draft.Options = append(draft.Options, &paymentOption{
|
|
ID: option.ID,
|
|
Label: option.Label,
|
|
Units: units,
|
|
Subtotal: subtotal,
|
|
})
|
|
}
|
|
if rows.Err() != nil {
|
|
return nil, rows.Err()
|
|
}
|
|
|
|
return draft, nil
|
|
}
|
|
|
|
func newBookingCart(ctx context.Context, conn *database.Conn, f *bookingForm, campsiteType string) (*bookingCart, error) {
|
|
cart := &bookingCart{
|
|
Total: "0.0",
|
|
}
|
|
|
|
draft, err := draftPayment(ctx, conn, f, campsiteType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if draft == nil {
|
|
return cart, nil
|
|
}
|
|
cart.Draft = draft
|
|
cart.DownPaymentPercent = draft.DownPaymentPercent
|
|
|
|
maybeAddLine := func(units int, subtotal string, concept string) {
|
|
if units > 0 && subtotal != "" {
|
|
cart.Lines = append(cart.Lines, &cartLine{
|
|
Concept: concept,
|
|
Units: units,
|
|
Subtotal: subtotal,
|
|
})
|
|
}
|
|
}
|
|
maybeAddLine(draft.NumNights, draft.Nights, locale.PgettextNoop("Night", "cart"))
|
|
maybeAddLine(draft.NumAdults, draft.Adults, locale.PgettextNoop("Adult", "cart"))
|
|
maybeAddLine(draft.NumTeenagers, draft.Teenagers, locale.PgettextNoop("Teenager", "cart"))
|
|
maybeAddLine(draft.NumChildren, draft.Children, locale.PgettextNoop("Child", "cart"))
|
|
maybeAddLine(draft.NumDogs, draft.Dogs, locale.PgettextNoop("Dog", "cart"))
|
|
|
|
for _, option := range draft.Options {
|
|
maybeAddLine(option.Units, option.Subtotal, option.Label)
|
|
}
|
|
|
|
maybeAddLine(draft.NumAdults, draft.TouristTax, locale.PgettextNoop("Tourist tax", "cart"))
|
|
|
|
if draft.Total != "0.0" {
|
|
cart.Total = draft.Total
|
|
cart.Enabled = f.Guests.Error == nil
|
|
|
|
if draft.DownPayment != draft.Total {
|
|
cart.DownPayment = draft.DownPayment
|
|
}
|
|
}
|
|
|
|
return cart, nil
|
|
}
|