rentease/internal/service/booking/models.go
Ruidy 8384d85e3e
feat(stripe): add Stripe payment sync and webhook support
Introduce Stripe integration for automatic payment ingestion and refund
tracking. Adds new fields to the payment model for Stripe IDs and
status,
Stripe client driver, sync service, cron job, manual API endpoint, and
public webhook handler for real-time updates. Includes tests and
documentation. Manual cash entry remains supported.
2025-10-03 21:39:59 +02:00

135 lines
3.6 KiB
Go

package booking
import (
"fmt"
"strconv"
"time"
u "github.com/rjNemo/underscore"
"gorm.io/gorm"
"github.com/rjNemo/rentease/internal/config"
)
type Booking struct {
From time.Time
To time.Time
gorm.Model
Name string `gorm:"column:customer_name"`
PhoneNumber string
Email string
Platform config.Platform
ExternalID *string `gorm:"uniqueIndex:booking_external_id"`
Items []Item
Payments []Payment `gorm:"foreignKey:BookingID"`
ID int
CustomerNumber int `gorm:"column:customers"`
PlatformFees float64 `gorm:"type:decimal(10,2)"`
Canceled bool `gorm:"default:false"`
}
// NewBooking creates a new booking with the given parameters
func NewBooking(from, to time.Time, name, phoneNumber, email, platform string,
customerNumber int, platformFees float64, externalID *string,
) *Booking {
return &Booking{
From: from,
To: to,
Name: name,
PhoneNumber: phoneNumber,
CustomerNumber: customerNumber,
Email: email,
Platform: config.Platform(platform),
PlatformFees: platformFees,
ExternalID: externalID,
}
}
func (b Booking) InvoiceNumber(hc *config.Host) string {
return fmt.Sprintf("%s%04s", hc.InvoicePrefix, strconv.Itoa(b.ID+hc.CustomerSeed))
}
func (b Booking) ToInvoice(hc *config.Host) Invoice {
total := u.Reduce(b.Items, func(i Item, sum float64) float64 {
return sum + i.Price*float64(i.Quantity)
}, 0.0)
amountPaid := u.Reduce(b.Payments, func(i Payment, sum float64) float64 {
return sum + i.Amount
}, 0.0)
return Invoice{
Host: *hc,
Name: b.Name,
PhoneNumber: b.PhoneNumber,
CustomersNumber: b.CustomerNumber,
Platform: b.Platform.ToFrench(),
ID: b.InvoiceNumber(hc),
From: b.From.Format("02/01/2006"),
To: b.To.Format("02/01/2006"),
Total: strconv.FormatFloat(total, 'f', 2, 64),
AmountPaid: strconv.FormatFloat(amountPaid, 'f', 2, 64),
BalanceDue: strconv.FormatFloat(total-amountPaid, 'f', 2, 64),
Lines: u.Map(b.Items, func(i Item) InvoiceLine {
return InvoiceLine{
Name: i.ToFrench(),
Quantity: i.Quantity,
Price: strconv.FormatFloat(i.Price, 'f', 2, 64),
Total: strconv.FormatFloat(i.Price*float64(i.Quantity), 'f', 2, 64),
}
}),
Payments: u.Map(b.Payments, func(p Payment) PaymentLine {
return PaymentLine{
Date: p.CreatedAt.Format("02/01/2006"),
Method: p.PaymentMethod.ToFrench(),
Amount: strconv.FormatFloat(p.Amount, 'f', 2, 64),
}
}),
}
}
// WithID returns a copy of the booking with the given ID
func (b *Booking) WithID(id int) *Booking {
b.ID = id
return b
}
type Item struct {
gorm.Model
Item string
PaymentMethod string
PaymentStatus string `gorm:"default:Pending"`
ID int
BookingID int
Quantity int
Price float64 `gorm:"type:decimal(10,2)"`
}
// ToFrench translates the item name to french
func (i Item) ToFrench() string {
switch i.Item {
case "T2":
return "T2"
case "T3":
return "T3"
case "Airport":
return "Transport Aéroport"
case "Port":
return "Transport Gare Maritime"
case "Transport":
return "Transport"
case "Taxes":
return "Taxes de sejour"
default:
return i.Item
}
}
type Payment struct {
gorm.Model
BookingID uint `gorm:"not null;index"`
Booking Booking `gorm:"foreignKey:BookingID;constraint:OnDelete:CASCADE"`
Amount float64
PaymentMethod config.PaymentMethod
StripePaymentID *string `gorm:"size:255;uniqueIndex"`
StripeStatus *string `gorm:"size:32"`
}