camper/pkg/booking/cart.go

204 lines
4.7 KiB
Go
Raw Normal View History

package booking
import (
"context"
"strconv"
"time"
"dev.tandem.ws/tandem/camper/pkg/database"
"dev.tandem.ws/tandem/camper/pkg/locale"
)
type bookingCart struct {
Lines []*cartLine
Total string
DownPayment string
DownPaymentPercent int
Enabled bool
}
type cartLine struct {
Concept string
Units int
Subtotal string
}
func newBookingCart(ctx context.Context, conn *database.Conn, f *bookingForm, campsiteType string) (*bookingCart, error) {
cart := &bookingCart{
Total: "0.0",
}
if f.Dates == nil {
return cart, nil
}
arrivalDate, err := time.Parse(database.ISODateFormat, f.Dates.ArrivalDate.Val)
if err != nil {
return cart, nil
}
departureDate, err := time.Parse(database.ISODateFormat, f.Dates.DepartureDate.Val)
if err != nil {
return cart, nil
}
if f.Guests == nil {
return cart, nil
}
numAdults, err := strconv.Atoi(f.Guests.NumberAdults.Val)
if err != nil {
return cart, nil
}
numTeenagers, err := strconv.Atoi(f.Guests.NumberTeenagers.Val)
if err != nil {
return cart, nil
}
numChildren, err := strconv.Atoi(f.Guests.NumberChildren.Val)
if err != nil {
return cart, nil
}
numDogs := 0
if f.Guests.NumberDogs != nil {
numDogs, err = strconv.Atoi(f.Guests.NumberDogs.Val)
if err != nil {
return cart, nil
}
}
Add payment relation and use it to compute the booking’s cart I had to add the payment concept separate from the booking, unlike other eCommerce solutions that subsume the two into a single “order”, like WooCommerce, because bookings should be done in a separate Camper instance that will sync to the public instance, but the payment is done by the public instance. There will be a queue or something between the public and the private instance to pass along the booking information once the payment is complete, but the public instance still needs to keep track of payments without creating bookings. To compute the total for that payment i had to do the same as was doing until now for the cart. To prevent duplications, or having functions with complex return types, i now create a “draft” payment while the user is filling in the form, and compute the cart there; from Go i only have to retrieve the data from the relation, that simplifies the work, actually. Since the payment is computed way before customers enter their details, i can not have that data in the same payment relation, unless i allow NULL values. Allowing NULL values means that i can create a payment without customer, thus i moved all customer details to a separate relation. It still allows payment without customer, but at least there are no NULL values. Draft payments should be removed after a time, but i believe this needs to be done in a cronjob or similar, not in the Go application. To update the same payment while filling the same booking form, i now have a hidden field with the payment slug. A competent developer would have used a cookie or something like that; i am not competent.
2024-02-12 04:21:00 +00:00
zonePreferences := ""
if f.Options != nil && f.Options.ZonePreferences != nil {
zonePreferences = f.Options.ZonePreferences.Val
}
optionMap := make(map[int]*campsiteTypeOption)
var typeOptions []*campsiteTypeOption
if f.Options != nil {
typeOptions = f.Options.Options
}
Add payment relation and use it to compute the booking’s cart I had to add the payment concept separate from the booking, unlike other eCommerce solutions that subsume the two into a single “order”, like WooCommerce, because bookings should be done in a separate Camper instance that will sync to the public instance, but the payment is done by the public instance. There will be a queue or something between the public and the private instance to pass along the booking information once the payment is complete, but the public instance still needs to keep track of payments without creating bookings. To compute the total for that payment i had to do the same as was doing until now for the cart. To prevent duplications, or having functions with complex return types, i now create a “draft” payment while the user is filling in the form, and compute the cart there; from Go i only have to retrieve the data from the relation, that simplifies the work, actually. Since the payment is computed way before customers enter their details, i can not have that data in the same payment relation, unless i allow NULL values. Allowing NULL values means that i can create a payment without customer, thus i moved all customer details to a separate relation. It still allows payment without customer, but at least there are no NULL values. Draft payments should be removed after a time, but i believe this needs to be done in a cronjob or similar, not in the Go application. To update the same payment while filling the same booking form, i now have a hidden field with the payment slug. A competent developer would have used a cookie or something like that; i am not competent.
2024-02-12 04:21:00 +00:00
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
Add payment relation and use it to compute the booking’s cart I had to add the payment concept separate from the booking, unlike other eCommerce solutions that subsume the two into a single “order”, like WooCommerce, because bookings should be done in a separate Camper instance that will sync to the public instance, but the payment is done by the public instance. There will be a queue or something between the public and the private instance to pass along the booking information once the payment is complete, but the public instance still needs to keep track of payments without creating bookings. To compute the total for that payment i had to do the same as was doing until now for the cart. To prevent duplications, or having functions with complex return types, i now create a “draft” payment while the user is filling in the form, and compute the cart there; from Go i only have to retrieve the data from the relation, that simplifies the work, actually. Since the payment is computed way before customers enter their details, i can not have that data in the same payment relation, unless i allow NULL values. Allowing NULL values means that i can create a payment without customer, thus i moved all customer details to a separate relation. It still allows payment without customer, but at least there are no NULL values. Draft payments should be removed after a time, but i believe this needs to be done in a cronjob or similar, not in the Go application. To update the same payment while filling the same booking form, i now have a hidden field with the payment slug. A competent developer would have used a cookie or something like that; i am not competent.
2024-02-12 04:21:00 +00:00
optionUnits = append(optionUnits, &database.OptionUnits{
OptionID: option.ID,
Units: units,
})
}
row := conn.QueryRow(ctx, `
select payment.slug
, payment_id
Add payment relation and use it to compute the booking’s cart I had to add the payment concept separate from the booking, unlike other eCommerce solutions that subsume the two into a single “order”, like WooCommerce, because bookings should be done in a separate Camper instance that will sync to the public instance, but the payment is done by the public instance. There will be a queue or something between the public and the private instance to pass along the booking information once the payment is complete, but the public instance still needs to keep track of payments without creating bookings. To compute the total for that payment i had to do the same as was doing until now for the cart. To prevent duplications, or having functions with complex return types, i now create a “draft” payment while the user is filling in the form, and compute the cart there; from Go i only have to retrieve the data from the relation, that simplifies the work, actually. Since the payment is computed way before customers enter their details, i can not have that data in the same payment relation, unless i allow NULL values. Allowing NULL values means that i can create a payment without customer, thus i moved all customer details to a separate relation. It still allows payment without customer, but at least there are no NULL values. Draft payments should be removed after a time, but i believe this needs to be done in a cronjob or similar, not in the Go application. To update the same payment while filling the same booking form, i now have a hidden field with the payment slug. A competent developer would have used a cookie or something like that; i am not competent.
2024-02-12 04:21:00 +00:00
, 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) as payment
Add payment relation and use it to compute the booking’s cart I had to add the payment concept separate from the booking, unlike other eCommerce solutions that subsume the two into a single “order”, like WooCommerce, because bookings should be done in a separate Camper instance that will sync to the public instance, but the payment is done by the public instance. There will be a queue or something between the public and the private instance to pass along the booking information once the payment is complete, but the public instance still needs to keep track of payments without creating bookings. To compute the total for that payment i had to do the same as was doing until now for the cart. To prevent duplications, or having functions with complex return types, i now create a “draft” payment while the user is filling in the form, and compute the cart there; from Go i only have to retrieve the data from the relation, that simplifies the work, actually. Since the payment is computed way before customers enter their details, i can not have that data in the same payment relation, unless i allow NULL values. Allowing NULL values means that i can create a payment without customer, thus i moved all customer details to a separate relation. It still allows payment without customer, but at least there are no NULL values. Draft payments should be removed after a time, but i believe this needs to be done in a cronjob or similar, not in the Go application. To update the same payment while filling the same booking form, i now have a hidden field with the payment slug. A competent developer would have used a cookie or something like that; i am not competent.
2024-02-12 04:21:00 +00:00
join currency using (currency_code)
`,
database.ZeroNullUUID(f.PaymentSlug.Val),
arrivalDate,
departureDate,
campsiteType,
numAdults,
numTeenagers,
numChildren,
numDogs,
zonePreferences,
database.OptionUnitsArray(optionUnits),
)
Add payment relation and use it to compute the booking’s cart I had to add the payment concept separate from the booking, unlike other eCommerce solutions that subsume the two into a single “order”, like WooCommerce, because bookings should be done in a separate Camper instance that will sync to the public instance, but the payment is done by the public instance. There will be a queue or something between the public and the private instance to pass along the booking information once the payment is complete, but the public instance still needs to keep track of payments without creating bookings. To compute the total for that payment i had to do the same as was doing until now for the cart. To prevent duplications, or having functions with complex return types, i now create a “draft” payment while the user is filling in the form, and compute the cart there; from Go i only have to retrieve the data from the relation, that simplifies the work, actually. Since the payment is computed way before customers enter their details, i can not have that data in the same payment relation, unless i allow NULL values. Allowing NULL values means that i can create a payment without customer, thus i moved all customer details to a separate relation. It still allows payment without customer, but at least there are no NULL values. Draft payments should be removed after a time, but i believe this needs to be done in a cronjob or similar, not in the Go application. To update the same payment while filling the same booking form, i now have a hidden field with the payment slug. A competent developer would have used a cookie or something like that; i am not competent.
2024-02-12 04:21:00 +00:00
var paymentID int
var numNights int
var nights string
var adults string
var teenagers string
var children string
var dogs string
var touristTax string
var total string
var downPayment string
if err = row.Scan(
&f.PaymentSlug.Val,
&paymentID,
&numNights,
&nights,
&adults,
&teenagers,
&children,
&dogs,
&touristTax,
&total,
&downPayment,
&cart.DownPaymentPercent,
); err != nil {
if database.ErrorIsNotFound(err) {
return cart, nil
}
return nil, err
}
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(numNights, nights, locale.PgettextNoop("Night", "cart"))
maybeAddLine(numAdults, adults, locale.PgettextNoop("Adult", "cart"))
maybeAddLine(numTeenagers, teenagers, locale.PgettextNoop("Teenager", "cart"))
maybeAddLine(numChildren, children, locale.PgettextNoop("Child", "cart"))
maybeAddLine(numDogs, dogs, locale.PgettextNoop("Dog", "cart"))
Add payment relation and use it to compute the booking’s cart I had to add the payment concept separate from the booking, unlike other eCommerce solutions that subsume the two into a single “order”, like WooCommerce, because bookings should be done in a separate Camper instance that will sync to the public instance, but the payment is done by the public instance. There will be a queue or something between the public and the private instance to pass along the booking information once the payment is complete, but the public instance still needs to keep track of payments without creating bookings. To compute the total for that payment i had to do the same as was doing until now for the cart. To prevent duplications, or having functions with complex return types, i now create a “draft” payment while the user is filling in the form, and compute the cart there; from Go i only have to retrieve the data from the relation, that simplifies the work, actually. Since the payment is computed way before customers enter their details, i can not have that data in the same payment relation, unless i allow NULL values. Allowing NULL values means that i can create a payment without customer, thus i moved all customer details to a separate relation. It still allows payment without customer, but at least there are no NULL values. Draft payments should be removed after a time, but i believe this needs to be done in a cronjob or similar, not in the Go application. To update the same payment while filling the same booking form, i now have a hidden field with the payment slug. A competent developer would have used a cookie or something like that; i am not competent.
2024-02-12 04:21:00 +00:00
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
`, 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
}
Add payment relation and use it to compute the booking’s cart I had to add the payment concept separate from the booking, unlike other eCommerce solutions that subsume the two into a single “order”, like WooCommerce, because bookings should be done in a separate Camper instance that will sync to the public instance, but the payment is done by the public instance. There will be a queue or something between the public and the private instance to pass along the booking information once the payment is complete, but the public instance still needs to keep track of payments without creating bookings. To compute the total for that payment i had to do the same as was doing until now for the cart. To prevent duplications, or having functions with complex return types, i now create a “draft” payment while the user is filling in the form, and compute the cart there; from Go i only have to retrieve the data from the relation, that simplifies the work, actually. Since the payment is computed way before customers enter their details, i can not have that data in the same payment relation, unless i allow NULL values. Allowing NULL values means that i can create a payment without customer, thus i moved all customer details to a separate relation. It still allows payment without customer, but at least there are no NULL values. Draft payments should be removed after a time, but i believe this needs to be done in a cronjob or similar, not in the Go application. To update the same payment while filling the same booking form, i now have a hidden field with the payment slug. A competent developer would have used a cookie or something like that; i am not competent.
2024-02-12 04:21:00 +00:00
option := optionMap[optionID]
if option == nil {
continue
}
Add payment relation and use it to compute the booking’s cart I had to add the payment concept separate from the booking, unlike other eCommerce solutions that subsume the two into a single “order”, like WooCommerce, because bookings should be done in a separate Camper instance that will sync to the public instance, but the payment is done by the public instance. There will be a queue or something between the public and the private instance to pass along the booking information once the payment is complete, but the public instance still needs to keep track of payments without creating bookings. To compute the total for that payment i had to do the same as was doing until now for the cart. To prevent duplications, or having functions with complex return types, i now create a “draft” payment while the user is filling in the form, and compute the cart there; from Go i only have to retrieve the data from the relation, that simplifies the work, actually. Since the payment is computed way before customers enter their details, i can not have that data in the same payment relation, unless i allow NULL values. Allowing NULL values means that i can create a payment without customer, thus i moved all customer details to a separate relation. It still allows payment without customer, but at least there are no NULL values. Draft payments should be removed after a time, but i believe this needs to be done in a cronjob or similar, not in the Go application. To update the same payment while filling the same booking form, i now have a hidden field with the payment slug. A competent developer would have used a cookie or something like that; i am not competent.
2024-02-12 04:21:00 +00:00
maybeAddLine(units, subtotal, option.Label)
}
if rows.Err() != nil {
return nil, rows.Err()
}
maybeAddLine(numAdults, touristTax, locale.PgettextNoop("Tourist tax", "cart"))
Add payment relation and use it to compute the booking’s cart I had to add the payment concept separate from the booking, unlike other eCommerce solutions that subsume the two into a single “order”, like WooCommerce, because bookings should be done in a separate Camper instance that will sync to the public instance, but the payment is done by the public instance. There will be a queue or something between the public and the private instance to pass along the booking information once the payment is complete, but the public instance still needs to keep track of payments without creating bookings. To compute the total for that payment i had to do the same as was doing until now for the cart. To prevent duplications, or having functions with complex return types, i now create a “draft” payment while the user is filling in the form, and compute the cart there; from Go i only have to retrieve the data from the relation, that simplifies the work, actually. Since the payment is computed way before customers enter their details, i can not have that data in the same payment relation, unless i allow NULL values. Allowing NULL values means that i can create a payment without customer, thus i moved all customer details to a separate relation. It still allows payment without customer, but at least there are no NULL values. Draft payments should be removed after a time, but i believe this needs to be done in a cronjob or similar, not in the Go application. To update the same payment while filling the same booking form, i now have a hidden field with the payment slug. A competent developer would have used a cookie or something like that; i am not competent.
2024-02-12 04:21:00 +00:00
if total != "0.0" {
cart.Total = total
cart.Enabled = f.Guests.Error == nil
if downPayment != total {
cart.DownPayment = downPayment
}
}
return cart, nil
}