rentease/internal/repository/booking/pg_store.go
Ruidy afc61e02f1
Some checks failed
CI / checks (push) Has been cancelled
refactor: improve booking error handling and responses
Refactor booking retrieval to return errors instead of nil values,
enabling more robust error handling throughout the booking, payment,
and PDF endpoints. Add custom HTTP error page rendering for not found
and internal server errors. Update interfaces and tests to match new
method signatures. This improves user feedback and code maintainability.
2025-11-17 19:26:45 +01:00

224 lines
5.9 KiB
Go

package booking
import (
"errors"
"fmt"
"time"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"github.com/rjNemo/rentease/internal/service/booking"
)
type PgStore struct {
db *gorm.DB
}
func NewPgStore(db *gorm.DB) *PgStore {
return &PgStore{db}
}
func (ps *PgStore) All() []*booking.Line {
bookings := make([]*booking.Line, 0)
ps.db.Raw(`
select bookings.id, customer_name, "from", "to", platform, sum(price * quantity) as total, canceled
from bookings
left join items on bookings.id = items.booking_id
group by bookings.id
order by id desc;
`).
Scan(&bookings)
return bookings
}
func (ps *PgStore) Search(value string) []*booking.Line {
bookings := make([]*booking.Line, 0)
ps.db.Raw(`
select bookings.id, customer_name, "from", "to", platform, sum(price * quantity) as total, canceled
from bookings
left join items on bookings.id = items.booking_id
where customer_name ilike ?
group by bookings.id
order by id desc;
`, fmt.Sprintf("%%%s%%", value)).
Scan(&bookings)
return bookings
}
func (ps *PgStore) List(from, to time.Time) ([]*booking.Line, error) {
bookings := make([]*booking.Line, 0)
if err := ps.db.Raw(`
select bookings.id, customer_name, "from", "to", platform, sum(price * quantity) as total, platform_fees
from bookings
join items on bookings.id = items.booking_id
where "to" between ? and ?
and canceled = false
group by bookings.id
order by id desc;
`, from, to).
Scan(&bookings).Error; err != nil {
return nil, fmt.Errorf("failed to list bookings: %w", err)
}
return bookings, nil
}
func (ps *PgStore) CardTotal(from, to time.Time) (float64, error) {
var total float64
if err := ps.db.Raw(`
select sum(total)
from (select sum(amount) as total
from bookings as b
join payments as p on b.id = p.booking_id
where "to" between ? and ?
and canceled = false
and p.payment_method = 'Card'
group by b.id) as t;
`, from, to).
Scan(&total).Error; err != nil {
return 0, fmt.Errorf("failed to get card total: %w", err)
}
return total, nil
}
func (ps *PgStore) Get(id int) (*booking.Booking, error) {
var b booking.Booking
res := ps.db.Preload("Items").Preload("Payments").First(&b, id)
if err := res.Error; err != nil {
return nil, err
}
return &b, nil
}
func (ps *PgStore) Create(b *booking.Booking) error {
return ps.db.Create(b).Error
}
func (ps *PgStore) Update(b *booking.Booking) error {
return ps.db.Model(&booking.Booking{}).Where("id = ?", b.ID).Updates(map[string]any{
"from": b.From,
"to": b.To,
"customer_name": b.Name,
"phone_number": b.PhoneNumber,
"email": b.Email,
"platform": b.Platform,
"external_id": b.ExternalID,
"customers": b.CustomerNumber,
"platform_fees": b.PlatformFees,
}).Error
}
func (ps *PgStore) Cancel(id int) error {
return ps.db.Model(&booking.Booking{}).Where("id = ?", id).Update("canceled", true).Error
}
// Item methods
func (ps *PgStore) CreateItem(i *booking.Item) error {
return ps.db.Create(i).Error
}
func (ps *PgStore) PayItem(id int) (*booking.Item, error) {
i := new(booking.Item)
if err := ps.db.Model(i).
Clauses(clause.Returning{}).
Where("id = ?", id).
Update("payment_status", "Completed").
Error; err != nil {
return nil, err
}
return i, nil
}
func (ps *PgStore) GetItem(id int) (*booking.Item, error) {
i := &booking.Item{ID: id}
err := ps.db.First(i).Error
return i, err
}
func (ps *PgStore) UpdateItem(id int, item string, paymentMethod string, paymentStatus string, qty int, price float64) (*booking.Item, error) {
i := new(booking.Item)
err := ps.db.Model(i).
Clauses(clause.Returning{}).
Where("id = ?", id).
Updates(map[string]any{
"item": item,
"payment_method": paymentMethod,
"payment_status": paymentStatus,
"quantity": qty,
"price": price,
}).
Error
return i, err
}
func (ps *PgStore) CreatePayment(p *booking.Payment) (*booking.Payment, error) {
if err := ps.db.Create(p).Error; err != nil {
return nil, fmt.Errorf("failed to create payment: %w", err)
}
return p, nil
}
func (ps *PgStore) GetPayment(id int) (*booking.Payment, error) {
p := &booking.Payment{}
err := ps.db.First(p, id).Error
return p, err
}
func (ps *PgStore) UpdatePayment(id int, amount float64, paymentMethod string) (*booking.Payment, error) {
p := new(booking.Payment)
err := ps.db.Model(p).
Clauses(clause.Returning{}).
Where("id = ?", id).
Updates(map[string]any{
"amount": amount,
"payment_method": paymentMethod,
}).
Error
return p, err
}
func (ps *PgStore) UpsertStripePayment(p *booking.Payment) (*booking.Payment, error) {
if p.StripePaymentID == nil || *p.StripePaymentID == "" {
return nil, fmt.Errorf("stripe payment id is required")
}
existing := new(booking.Payment)
stripeID := *p.StripePaymentID
if err := ps.db.Where("stripe_payment_id = ?", stripeID).First(existing).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
if err := ps.db.Create(p).Error; err != nil {
return nil, fmt.Errorf("failed to create stripe payment: %w", err)
}
return p, nil
}
return nil, fmt.Errorf("failed to lookup stripe payment: %w", err)
}
updates := map[string]any{
"amount": p.Amount,
"payment_method": p.PaymentMethod,
"stripe_status": p.StripeStatus,
"booking_id": p.BookingID,
}
if err := ps.db.Model(existing).
Clauses(clause.Returning{}).
Updates(updates).
Error; err != nil {
return nil, fmt.Errorf("failed to update stripe payment: %w", err)
}
return existing, nil
}
func (ps *PgStore) FindStripePayment(stripePaymentID string) (*booking.Payment, error) {
if stripePaymentID == "" {
return nil, fmt.Errorf("stripe payment id is required")
}
p := new(booking.Payment)
if err := ps.db.Where("stripe_payment_id = ?", stripePaymentID).First(p).Error; err != nil {
return nil, err
}
return p, nil
}