114 lines
4.7 KiB
Go
114 lines
4.7 KiB
Go
package entities
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// CustomerWallet holds a customer's EnakPoint and EnakCoin balances. The row is also
|
|
// the lock every wallet operation for the customer takes first, so concurrent
|
|
// operations on one customer queue up instead of spending the same balance twice.
|
|
//
|
|
// Balances are never written directly: they only move together with a ledger row, and
|
|
// only through the wallet processor.
|
|
type CustomerWallet struct {
|
|
CustomerID uuid.UUID `gorm:"type:uuid;primary_key" json:"customer_id"`
|
|
OrganizationID uuid.UUID `gorm:"type:uuid;not null" json:"organization_id"`
|
|
PointBalance int64 `gorm:"not null;default:0" json:"point_balance"`
|
|
CoinBalance int64 `gorm:"not null;default:0" json:"coin_balance"`
|
|
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
|
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
|
}
|
|
|
|
func (CustomerWallet) TableName() string {
|
|
return "customer_wallets"
|
|
}
|
|
|
|
// WalletTransaction is one ledger row. The ledger is append-only: a correction is a
|
|
// new row pointing at the one it corrects, never an update.
|
|
type WalletTransaction struct {
|
|
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
|
OrganizationID uuid.UUID `gorm:"type:uuid;not null" json:"organization_id"`
|
|
CustomerID uuid.UUID `gorm:"type:uuid;not null" json:"customer_id"`
|
|
Currency string `gorm:"not null;size:10" json:"currency"`
|
|
Type string `gorm:"not null;size:30" json:"type"`
|
|
// Signed: positive credits the wallet, negative debits it.
|
|
Amount int64 `gorm:"not null" json:"amount"`
|
|
BalanceAfter int64 `gorm:"not null" json:"balance_after"`
|
|
GroupID *uuid.UUID `gorm:"type:uuid" json:"group_id"`
|
|
|
|
// Where the value came from (credit) or went to (debit).
|
|
ReferenceType string `gorm:"not null;size:30" json:"reference_type"`
|
|
ReferenceID uuid.UUID `gorm:"type:uuid;not null" json:"reference_id"`
|
|
|
|
CounterpartyCustomerID *uuid.UUID `gorm:"type:uuid" json:"counterparty_customer_id"`
|
|
ReversesTransactionID *uuid.UUID `gorm:"type:uuid" json:"reverses_transaction_id"`
|
|
OutletID *uuid.UUID `gorm:"type:uuid" json:"outlet_id"`
|
|
CreatedByUser *uuid.UUID `gorm:"type:uuid" json:"created_by_user"`
|
|
Reason *string `gorm:"size:255" json:"reason"`
|
|
|
|
// Frozen at creation, so later renames do not rewrite history.
|
|
Description string `gorm:"not null;size:255" json:"description"`
|
|
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"`
|
|
IdempotencyKey *string `gorm:"size:100;unique" json:"idempotency_key"`
|
|
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
|
}
|
|
|
|
func (t *WalletTransaction) BeforeCreate(tx *gorm.DB) error {
|
|
if t.ID == uuid.Nil {
|
|
t.ID = uuid.New()
|
|
}
|
|
// A nil map would be stored as JSON null rather than an empty object.
|
|
if t.Metadata == nil {
|
|
t.Metadata = Metadata{}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (WalletTransaction) TableName() string {
|
|
return "wallet_transactions"
|
|
}
|
|
|
|
// WalletLot is one credited piece of balance with its own expiry (K9). Debits draw from
|
|
// the lots that expire soonest. A lot created by a transfer, exchange or refund carries
|
|
// the expiry of the lot it came from and points back at it through OriginLotID.
|
|
type WalletLot struct {
|
|
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
|
OrganizationID uuid.UUID `gorm:"type:uuid;not null" json:"organization_id"`
|
|
CustomerID uuid.UUID `gorm:"type:uuid;not null" json:"customer_id"`
|
|
Currency string `gorm:"not null;size:10" json:"currency"`
|
|
SourceTransactionID uuid.UUID `gorm:"type:uuid;not null" json:"source_transaction_id"`
|
|
OriginLotID *uuid.UUID `gorm:"type:uuid" json:"origin_lot_id"`
|
|
OriginalAmount int64 `gorm:"not null" json:"original_amount"`
|
|
// A cache of OriginalAmount minus the lot's allocations, and the only wallet column
|
|
// that is ever updated.
|
|
RemainingAmount int64 `gorm:"not null" json:"remaining_amount"`
|
|
// Nil means the lot never expires.
|
|
ExpiresAt *time.Time `json:"expires_at"`
|
|
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
|
}
|
|
|
|
func (l *WalletLot) BeforeCreate(tx *gorm.DB) error {
|
|
if l.ID == uuid.Nil {
|
|
l.ID = uuid.New()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (WalletLot) TableName() string {
|
|
return "wallet_lots"
|
|
}
|
|
|
|
// WalletLotAllocation records how much a debit ledger row drew from one lot.
|
|
type WalletLotAllocation struct {
|
|
TransactionID uuid.UUID `gorm:"type:uuid;primary_key" json:"transaction_id"`
|
|
LotID uuid.UUID `gorm:"type:uuid;primary_key" json:"lot_id"`
|
|
Amount int64 `gorm:"not null" json:"amount"`
|
|
}
|
|
|
|
func (WalletLotAllocation) TableName() string {
|
|
return "wallet_lot_allocations"
|
|
}
|