mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 18:52:56 +00:00
hot fix #1
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// TicketType represents a type of ticket with pricing and rules
|
||||
type TicketType struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Description string `gorm:"type:text" json:"description"`
|
||||
PriceCents int64 `gorm:"not null" json:"price_cents"`
|
||||
Currency string `gorm:"size:10;default:'CZK'" json:"currency"`
|
||||
Color string `gorm:"size:20;default:'primary'" json:"color"` // primary, secondary, success, warning, danger
|
||||
DisplayOrder int `gorm:"default:0" json:"display_order"`
|
||||
Active bool `gorm:"default:true;index" json:"active"`
|
||||
MaxTicketsPerOrder int `gorm:"default:10" json:"max_tickets_per_order"`
|
||||
SaleStartTime *time.Time `json:"sale_start_time"`
|
||||
SaleEndTime *time.Time `json:"sale_end_time"`
|
||||
RequiresMembership bool `gorm:"default:false" json:"requires_membership"`
|
||||
MinAge *int `json:"min_age"`
|
||||
}
|
||||
|
||||
func (TicketType) TableName() string { return "ticket_types" }
|
||||
|
||||
// TicketCampaign represents a ticket sales campaign for a specific match or event
|
||||
type TicketCampaign struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
|
||||
Title string `gorm:"not null" json:"title"`
|
||||
Description string `gorm:"type:text" json:"description"`
|
||||
|
||||
// Match information
|
||||
ExternalMatchID *string `gorm:"index" json:"external_match_id"` // FACR match ID
|
||||
CompetitionCode *string `json:"competition_code"`
|
||||
MatchDateTime *time.Time `json:"match_date_time"`
|
||||
HomeTeam *string `json:"home_team"`
|
||||
AwayTeam *string `json:"away_team"`
|
||||
Venue *string `json:"venue"`
|
||||
|
||||
// Campaign settings
|
||||
Active bool `gorm:"default:true;index" json:"active"`
|
||||
SaleStartTime time.Time `gorm:"not null" json:"sale_start_time"`
|
||||
SaleEndTime time.Time `gorm:"not null" json:"sale_end_time"`
|
||||
MaxTotalTickets *int `json:"max_total_tickets"`
|
||||
|
||||
// Relationships
|
||||
TicketTypes []TicketType `gorm:"many2many:campaign_ticket_types;" json:"ticket_types,omitempty"`
|
||||
CampaignTicketTypes []CampaignTicketType `gorm:"foreignKey:CampaignID" json:"campaign_ticket_types,omitempty"`
|
||||
Tickets []Ticket `gorm:"foreignKey:CampaignID" json:"tickets,omitempty"`
|
||||
}
|
||||
|
||||
func (TicketCampaign) TableName() string { return "ticket_campaigns" }
|
||||
|
||||
// CampaignTicketType links ticket types to campaigns with campaign-specific overrides
|
||||
type CampaignTicketType struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
CampaignID uint `gorm:"not null;index" json:"campaign_id"`
|
||||
Campaign TicketCampaign `gorm:"foreignKey:CampaignID" json:"-"`
|
||||
TicketTypeID uint `gorm:"not null;index" json:"ticket_type_id"`
|
||||
TicketType TicketType `gorm:"foreignKey:TicketTypeID" json:"-"`
|
||||
|
||||
// Campaign-specific overrides
|
||||
PriceCents *int64 `json:"price_cents"` // Override default price if set
|
||||
MaxQuantity *int `json:"max_quantity"` // Campaign-specific quantity limit
|
||||
}
|
||||
|
||||
func (CampaignTicketType) TableName() string { return "campaign_ticket_types" }
|
||||
|
||||
// Ticket represents an actual sold/reserved ticket
|
||||
type Ticket struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
OrderID *uint `gorm:"index" json:"order_id,omitempty"`
|
||||
Order *EshopOrder `gorm:"foreignKey:OrderID" json:"order,omitempty"`
|
||||
CampaignID uint `gorm:"not null;index" json:"campaign_id"`
|
||||
Campaign TicketCampaign `gorm:"foreignKey:CampaignID" json:"campaign,omitempty"`
|
||||
TicketTypeID uint `gorm:"not null;index" json:"ticket_type_id"`
|
||||
TicketType TicketType `gorm:"foreignKey:TicketTypeID" json:"ticket_type,omitempty"`
|
||||
|
||||
// Ticket holder information
|
||||
HolderName string `gorm:"not null" json:"holder_name"`
|
||||
HolderEmail string `gorm:"not null;index" json:"holder_email"`
|
||||
HolderPhone string `gorm:"size:50" json:"holder_phone"`
|
||||
|
||||
// Ticket details
|
||||
Quantity int `gorm:"not null;default:1" json:"quantity"`
|
||||
UnitPriceCents int64 `gorm:"not null" json:"unit_price_cents"`
|
||||
TotalPriceCents int64 `gorm:"not null" json:"total_price_cents"`
|
||||
Currency string `gorm:"size:10;default:'CZK'" json:"currency"`
|
||||
|
||||
// Status tracking
|
||||
Status string `gorm:"size:20;default:'reserved';index" json:"status"` // reserved, paid, cancelled, used
|
||||
Barcode string `gorm:"unique" json:"barcode"`
|
||||
QRCodeData string `gorm:"type:text" json:"qr_code_data"`
|
||||
|
||||
// Usage tracking
|
||||
UsedAt *time.Time `json:"used_at"`
|
||||
UsedBy *string `json:"used_by"`
|
||||
}
|
||||
|
||||
func (Ticket) TableName() string { return "tickets" }
|
||||
|
||||
// TicketAvailability tracks available tickets per campaign/type
|
||||
type TicketAvailability struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
CampaignID uint `gorm:"not null;uniqueIndex:idx_campaign_type" json:"campaign_id"`
|
||||
Campaign TicketCampaign `gorm:"foreignKey:CampaignID" json:"-"`
|
||||
TicketTypeID uint `gorm:"not null;uniqueIndex:idx_campaign_type" json:"ticket_type_id"`
|
||||
TicketType TicketType `gorm:"foreignKey:TicketTypeID" json:"-"`
|
||||
|
||||
TotalCapacity int `gorm:"not null;default:0" json:"total_capacity"`
|
||||
SoldQuantity int `gorm:"not null;default:0" json:"sold_quantity"`
|
||||
ReservedQuantity int `gorm:"not null;default:0" json:"reserved_quantity"`
|
||||
AvailableQuantity int `gorm:"-" json:"available_quantity"` // Computed field
|
||||
}
|
||||
|
||||
func (TicketAvailability) TableName() string { return "ticket_availability" }
|
||||
|
||||
// AvailableTicketView represents the database view for available tickets
|
||||
type AvailableTicketView struct {
|
||||
CampaignID uint `json:"campaign_id"`
|
||||
CampaignTitle string `json:"campaign_title"`
|
||||
CampaignDescription string `json:"campaign_description"`
|
||||
ExternalMatchID *string `json:"external_match_id"`
|
||||
CompetitionCode *string `json:"competition_code"`
|
||||
MatchDateTime *time.Time `json:"match_date_time"`
|
||||
HomeTeam *string `json:"home_team"`
|
||||
AwayTeam *string `json:"away_team"`
|
||||
Venue *string `json:"venue"`
|
||||
SaleStartTime time.Time `json:"sale_start_time"`
|
||||
SaleEndTime time.Time `json:"sale_end_time"`
|
||||
|
||||
TicketTypeID uint `json:"ticket_type_id"`
|
||||
TicketTypeName string `json:"ticket_type_name"`
|
||||
TicketTypeDesc string `json:"ticket_type_description"`
|
||||
PriceCents int64 `json:"price_cents"`
|
||||
MaxPerOrder int `json:"max_per_order"`
|
||||
Color string `json:"color"`
|
||||
AvailableQuantity int `json:"available_quantity"`
|
||||
TotalCapacity int `json:"total_capacity"`
|
||||
SaleStatus string `json:"sale_status"` // upcoming, available, sold_out, ended
|
||||
}
|
||||
|
||||
func (AvailableTicketView) TableName() string { return "available_tickets_view" }
|
||||
|
||||
// Helper methods
|
||||
|
||||
// IsAvailable checks if tickets are currently available for purchase
|
||||
func (atv AvailableTicketView) IsAvailable() bool {
|
||||
return atv.SaleStatus == "available" && atv.AvailableQuantity > 0
|
||||
}
|
||||
|
||||
// GetPriceInCZK returns price formatted for display
|
||||
func (atv AvailableTicketView) GetPriceInCZK() float64 {
|
||||
return float64(atv.PriceCents) / 100.0
|
||||
}
|
||||
|
||||
// GetFormattedPrice returns price as string with CZK
|
||||
func (atv AvailableTicketView) GetFormattedPrice() string {
|
||||
return fmt.Sprintf("%.0f Kč", atv.GetPriceInCZK())
|
||||
}
|
||||
|
||||
// BeforeCreate hook for Ticket to generate barcode
|
||||
func (t *Ticket) BeforeCreate(tx *gorm.DB) error {
|
||||
if t.Barcode == "" {
|
||||
// Generate unique barcode
|
||||
t.Barcode = generateTicketBarcode()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateTicketBarcode creates a unique barcode for tickets
|
||||
func generateTicketBarcode() string {
|
||||
// Generate random number for uniqueness
|
||||
n, _ := rand.Int(rand.Reader, big.NewInt(10000))
|
||||
// Simple implementation - in production, use proper barcode generation
|
||||
return fmt.Sprintf("TKT-%d-%d", time.Now().Unix(), n.Int64())
|
||||
}
|
||||
Reference in New Issue
Block a user