Files
apskel-pos-backend/internal/repository/wallet_expiry_repository.go
T

55 lines
1.7 KiB
Go
Raw Normal View History

package repository
import (
"context"
"fmt"
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
// DueLot is a lot whose expiry has passed and that still holds something.
type DueLot struct {
ID uuid.UUID
CustomerID uuid.UUID
Currency string
Remaining int64
ExpiresAt time.Time
// The description of the row that created the lot, for the EXPIRE row's.
SourceDescription string
}
// WalletExpiryRepository finds what the expiry job has to do (docs/prd-point-coin.md
// F12). Balances only change through WalletProcessor.
type WalletExpiryRepository interface {
// ListDueLots returns up to limit lots due at asOf, the longest overdue first. It
// takes no lock: locking a lot before its wallet would deadlock against payments,
// which lock the wallet first. WalletProcessor.ExpireLot locks and reads again.
ListDueLots(ctx context.Context, asOf time.Time, limit int) ([]DueLot, error)
}
type walletExpiryRepository struct {
db *gorm.DB
}
func NewWalletExpiryRepository(db *gorm.DB) WalletExpiryRepository {
return &walletExpiryRepository{db: db}
}
func (r *walletExpiryRepository) ListDueLots(ctx context.Context, asOf time.Time, limit int) ([]DueLot, error) {
var lots []DueLot
err := DBFromContext(ctx, r.db).WithContext(ctx).Raw(`
SELECT l.id, l.customer_id, l.currency, l.remaining_amount AS remaining, l.expires_at,
t.description AS source_description
FROM wallet_lots l
JOIN wallet_transactions t ON t.id = l.source_transaction_id
WHERE l.remaining_amount > 0 AND l.expires_at <= ?
ORDER BY l.expires_at, l.id
LIMIT ?`, asOf, limit).Scan(&lots).Error
if err != nil {
return nil, fmt.Errorf("failed to list due wallet lots: %w", err)
}
return lots, nil
}