Change draft_payment return type to row of payment
This way i can use the function in the from clause of the query that i already had to do to get the totals formatted with to_price. In this case, i believe it is better to leave out Go’s function because it would force me to perform two queries. Instead of binding a nullable string pointer with the payment’s slug, i wanted to use pgtype’s zeronull.Text type, but it can not work in this case because it encodes the value as a text, while the parameters is uuid. I can not use zero.UUID, because it is a [16]byte array, while i have it in a string. Thus, had to create my own ZeroNullUUID type that use a string as a base but encodes it as a UUID.
This commit is contained in:
parent
77a3f78176
commit
990a614897
|
@ -17,10 +17,10 @@ set search_path to camper, public;
|
|||
|
||||
create type option_units as (option_id integer, units integer);
|
||||
|
||||
create or replace function draft_payment(payment_slug uuid, arrival_date date, departure_date date, campsite_type_slug uuid, num_adults integer, num_teenagers integer, num_children integer, num_dogs integer, zone_preferences text, options option_units[]) returns uuid as
|
||||
create or replace function draft_payment(payment_slug uuid, arrival_date date, departure_date date, campsite_type_slug uuid, num_adults integer, num_teenagers integer, num_children integer, num_dogs integer, zone_preferences text, options option_units[]) returns payment as
|
||||
$$
|
||||
declare
|
||||
pid integer;
|
||||
p payment;
|
||||
begin
|
||||
insert into payment (
|
||||
slug
|
||||
|
@ -86,14 +86,14 @@ begin
|
|||
, total = excluded.total
|
||||
, zone_preferences = excluded.zone_preferences
|
||||
, updated_at = current_timestamp
|
||||
returning payment_id, payment.slug
|
||||
into pid, payment_slug
|
||||
returning *
|
||||
into p
|
||||
;
|
||||
|
||||
if array_length(coalesce(options, array[]::option_units[]), 1) > 0 then
|
||||
delete
|
||||
from payment_option
|
||||
where payment_id = pid
|
||||
where payment_id = p.payment_id
|
||||
and campsite_type_option_id not in (
|
||||
select campsite_type_option_id
|
||||
from unnest(options) as option(campsite_type_option_id, units)
|
||||
|
@ -105,7 +105,7 @@ begin
|
|||
, units
|
||||
, subtotal
|
||||
)
|
||||
select pid
|
||||
select p.payment_id
|
||||
, campsite_type_option_id
|
||||
, units
|
||||
, case when per_night then sum(cost * units)::integer else max(cost * units)::integer end
|
||||
|
@ -125,26 +125,26 @@ begin
|
|||
with option as (
|
||||
select sum(subtotal)::integer as subtotal
|
||||
from payment_option
|
||||
where payment_id = pid
|
||||
where payment_id = p.payment_id
|
||||
)
|
||||
update payment
|
||||
set total = subtotal_nights + subtotal_adults + subtotal_teenagers + subtotal_children + subtotal_dogs + subtotal_tourist_tax + coalesce(option.subtotal, 0)
|
||||
from option
|
||||
where payment_id = pid
|
||||
where payment_id = p.payment_id
|
||||
;
|
||||
else
|
||||
delete
|
||||
from payment_option
|
||||
where payment_id = pid;
|
||||
where payment_id = p.payment_id;
|
||||
|
||||
update payment
|
||||
set total = subtotal_nights + subtotal_adults + subtotal_teenagers + subtotal_children + subtotal_dogs + subtotal_tourist_tax
|
||||
where payment_id = pid
|
||||
where payment_id = p.payment_id
|
||||
;
|
||||
end if;
|
||||
|
||||
|
||||
return payment_slug;
|
||||
return p;
|
||||
end;
|
||||
$$
|
||||
language plpgsql
|
||||
|
|
|
@ -82,26 +82,9 @@ func newBookingCart(ctx context.Context, conn *database.Conn, f *bookingForm, ca
|
|||
})
|
||||
}
|
||||
|
||||
paymentSlug, err := conn.DraftPayment(
|
||||
ctx,
|
||||
f.PaymentSlug.Val,
|
||||
arrivalDate,
|
||||
departureDate,
|
||||
campsiteType,
|
||||
numAdults,
|
||||
numTeenagers,
|
||||
numChildren,
|
||||
numDogs,
|
||||
zonePreferences,
|
||||
optionUnits,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.PaymentSlug.Val = paymentSlug
|
||||
|
||||
row := conn.QueryRow(ctx, `
|
||||
select payment_id
|
||||
select payment.slug
|
||||
, payment_id
|
||||
, departure_date - arrival_date
|
||||
, to_price(subtotal_nights, decimal_digits)
|
||||
, to_price(subtotal_adults, decimal_digits)
|
||||
|
@ -110,12 +93,21 @@ 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)
|
||||
from payment
|
||||
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)
|
||||
where payment.slug = $1
|
||||
`, paymentSlug)
|
||||
|
||||
`,
|
||||
database.ZeroNullUUID(f.PaymentSlug.Val),
|
||||
arrivalDate,
|
||||
departureDate,
|
||||
campsiteType,
|
||||
numAdults,
|
||||
numTeenagers,
|
||||
numChildren,
|
||||
numDogs,
|
||||
zonePreferences,
|
||||
database.OptionUnitsArray(optionUnits),
|
||||
)
|
||||
var paymentID int
|
||||
var numNights int
|
||||
var nights string
|
||||
|
@ -125,7 +117,7 @@ func newBookingCart(ctx context.Context, conn *database.Conn, f *bookingForm, ca
|
|||
var dogs string
|
||||
var touristTax string
|
||||
var total string
|
||||
if err = row.Scan(&paymentID, &numNights, &nights, &adults, &teenagers, &children, &dogs, &touristTax, &total); err != nil {
|
||||
if err = row.Scan(&f.PaymentSlug.Val, &paymentID, &numNights, &nights, &adults, &teenagers, &children, &dogs, &touristTax, &total); err != nil {
|
||||
if database.ErrorIsNotFound(err) {
|
||||
return cart, nil
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@ package database
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
|
@ -350,14 +348,6 @@ func (tx *Tx) TranslateHome(ctx context.Context, companyID int, langTag language
|
|||
return err
|
||||
}
|
||||
|
||||
func (c *Conn) DraftPayment(ctx context.Context, paymentSlug string, arrivalDate time.Time, departureDate time.Time, campsiteTypeSlug string, numAdults int, numTeenagers int, numChildren int, numDogs int, zonePreferences string, options OptionUnitsArray) (string, error) {
|
||||
var paymentSlugParam *string
|
||||
if paymentSlug != "" {
|
||||
paymentSlugParam = &paymentSlug
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/jackc/pgtype"
|
||||
|
||||
"dev.tandem.ws/tandem/camper/pkg/uuid"
|
||||
)
|
||||
|
||||
type ZeroNullUUID string
|
||||
|
||||
func (src ZeroNullUUID) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) {
|
||||
if src == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
bytes, err := uuid.Parse(string(src))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nullable := pgtype.UUID{
|
||||
Bytes: bytes,
|
||||
Status: pgtype.Present,
|
||||
}
|
||||
return nullable.EncodeBinary(ci, buf)
|
||||
}
|
|
@ -5,7 +5,12 @@
|
|||
|
||||
package uuid
|
||||
|
||||
import "dev.tandem.ws/tandem/camper/pkg/hex"
|
||||
import (
|
||||
gohex "encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"dev.tandem.ws/tandem/camper/pkg/hex"
|
||||
)
|
||||
|
||||
func Valid(s string) bool {
|
||||
if len(s) != 36 {
|
||||
|
@ -27,3 +32,23 @@ func Valid(s string) bool {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func Parse(src string) (dst [16]byte, err error) {
|
||||
switch len(src) {
|
||||
case 36:
|
||||
src = src[0:8] + src[9:13] + src[14:18] + src[19:23] + src[24:]
|
||||
case 32:
|
||||
// dashes already stripped, assume valid
|
||||
default:
|
||||
// assume invalid.
|
||||
return dst, fmt.Errorf("cannot parse UUID %v", src)
|
||||
}
|
||||
|
||||
buf, err := gohex.DecodeString(src)
|
||||
if err != nil {
|
||||
return dst, err
|
||||
}
|
||||
|
||||
copy(dst[:], buf)
|
||||
return dst, err
|
||||
}
|
||||
|
|
|
@ -13,15 +13,16 @@ import (
|
|||
type test struct {
|
||||
in string
|
||||
isUuid bool
|
||||
bytes [16]byte
|
||||
}
|
||||
|
||||
var tests = []test{
|
||||
{"f47ac10b-58cc-0372-8567-0e02b2c3d479", true},
|
||||
{"2bc1be74-169d-4300-a239-49a1196a045d", true},
|
||||
{"12bc1be74-169d-4300-a239-49a1196a045d", false},
|
||||
{"2bc1be74-169d-4300-a239-49a1196a045", false},
|
||||
{"2bc1be74-1x9d-4300-a239-49a1196a045d", false},
|
||||
{"2bc1be74-169d-4300-a239-49a1196ag45d", false},
|
||||
{"f47ac10b-58cc-0372-8567-0e02b2c3d479", true, [16]byte{0xf4, 0x7a, 0xc1, 0x0b, 0x58, 0xcc, 0x03, 0x72, 0x85, 0x67, 0x0e, 0x02, 0xb2, 0xc3, 0xd4, 0x79}},
|
||||
{"2bc1be74-169d-4300-a239-49a1196a045d", true, [16]byte{0x2b, 0xc1, 0xbe, 0x74, 0x16, 0x9d, 0x43, 0x00, 0xa2, 0x39, 0x49, 0xa1, 0x19, 0x6a, 0x04, 0x5d}},
|
||||
{"12bc1be74-169d-4300-a239-49a1196a045d", false, [16]byte{}},
|
||||
{"2bc1be74-169d-4300-a239-49a1196a045", false, [16]byte{}},
|
||||
{"2bc1be74-1x9d-4300-a239-49a1196a045d", false, [16]byte{}},
|
||||
{"2bc1be74-169d-4300-a239-49a1196ag45d", false, [16]byte{}},
|
||||
}
|
||||
|
||||
func testValid(t *testing.T, in string, isUuid bool) {
|
||||
|
@ -30,9 +31,22 @@ func testValid(t *testing.T, in string, isUuid bool) {
|
|||
}
|
||||
}
|
||||
|
||||
func testParse(t *testing.T, in string, expected [16]byte, isUuid bool) {
|
||||
actual, err := Parse(in)
|
||||
if isUuid && err != nil {
|
||||
t.Errorf("Parse(%s) unexpected error %v", in, err)
|
||||
} else if !isUuid && err == nil {
|
||||
t.Errorf("Parse(%s) expected to fail but got %v", in, actual)
|
||||
} else if actual != expected {
|
||||
t.Errorf("Parse(%s) got %v expected %v", in, actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUUID(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
testValid(t, tt.in, tt.isUuid)
|
||||
testValid(t, strings.ToUpper(tt.in), tt.isUuid)
|
||||
testParse(t, tt.in, tt.bytes, tt.isUuid)
|
||||
testParse(t, strings.ToUpper(tt.in), tt.bytes, tt.isUuid)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ set search_path to camper, public;
|
|||
|
||||
select has_function('camper', 'draft_payment', array['uuid', 'date', 'date', 'uuid', 'integer', 'integer', 'integer', 'integer', 'text', 'option_units[]']);
|
||||
select function_lang_is('camper', 'draft_payment', array['uuid', 'date', 'date', 'uuid', 'integer', 'integer', 'integer', 'integer', 'text', 'option_units[]'], 'plpgsql');
|
||||
select function_returns('camper', 'draft_payment', array['uuid', 'date', 'date', 'uuid', 'integer', 'integer', 'integer', 'integer', 'text', 'option_units[]'], 'uuid');
|
||||
select function_returns('camper', 'draft_payment', array['uuid', 'date', 'date', 'uuid', 'integer', 'integer', 'integer', 'integer', 'text', 'option_units[]'], 'payment');
|
||||
select isnt_definer('camper', 'draft_payment', array['uuid', 'date', 'date', 'uuid', 'integer', 'integer', 'integer', 'integer', 'text', 'option_units[]']);
|
||||
select volatility_is('camper', 'draft_payment', array['uuid', 'date', 'date', 'uuid', 'integer', 'integer', 'integer', 'integer', 'text', 'option_units[]'], 'volatile');
|
||||
select function_privs_are('camper', 'draft_payment', array ['uuid', 'date', 'date', 'uuid', 'integer', 'integer', 'integer', 'integer', 'text', 'option_units[]'], 'guest', array['EXECUTE']);
|
||||
|
|
Loading…
Reference in New Issue