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

110 lines
4.0 KiB
Go
Raw Normal View History

package repository
import (
"context"
"errors"
"fmt"
"github.com/google/uuid"
"gorm.io/gorm"
"apskel-pos-be/internal/entities"
)
// ErrWalletTransactionNotFound means the ledger row does not exist or belongs to
// another organization.
var ErrWalletTransactionNotFound = errors.New("wallet: transaction not found")
// WalletTraceRepository reads what tracing a ledger row lot by lot needs
// (docs/prd-point-coin.md F7, ยง8.1). Nothing here changes a balance.
type WalletTraceRepository interface {
// GetTransaction returns ErrWalletTransactionNotFound unless the row belongs to
// the organization.
GetTransaction(ctx context.Context, organizationID, id uuid.UUID) (*entities.WalletTransaction, error)
GetTransactions(ctx context.Context, ids []uuid.UUID) ([]entities.WalletTransaction, error)
// ListAllocations returns what a debit took from each lot, in the order it took.
ListAllocations(ctx context.Context, transactionID uuid.UUID) ([]entities.WalletLotAllocation, error)
// ListLotsBySource returns the lots a credit created, oldest first.
ListLotsBySource(ctx context.Context, transactionID uuid.UUID) ([]entities.WalletLot, error)
GetLots(ctx context.Context, ids []uuid.UUID) ([]entities.WalletLot, error)
CustomerNames(ctx context.Context, ids []uuid.UUID) (map[uuid.UUID]string, error)
}
type walletTraceRepository struct {
db *gorm.DB
names *walletAdminRepository
}
func NewWalletTraceRepository(db *gorm.DB) WalletTraceRepository {
return &walletTraceRepository{db: db, names: &walletAdminRepository{db: db}}
}
func (r *walletTraceRepository) GetTransaction(ctx context.Context, organizationID, id uuid.UUID) (*entities.WalletTransaction, error) {
var rows []entities.WalletTransaction
err := DBFromContext(ctx, r.db).WithContext(ctx).
Where("id = ? AND organization_id = ?", id, organizationID).
Limit(1).
Find(&rows).Error
if err != nil {
return nil, fmt.Errorf("failed to get wallet transaction: %w", err)
}
if len(rows) == 0 {
return nil, ErrWalletTransactionNotFound
}
return &rows[0], nil
}
func (r *walletTraceRepository) GetTransactions(ctx context.Context, ids []uuid.UUID) ([]entities.WalletTransaction, error) {
var rows []entities.WalletTransaction
if len(ids) == 0 {
return rows, nil
}
if err := DBFromContext(ctx, r.db).WithContext(ctx).Where("id IN ?", ids).Find(&rows).Error; err != nil {
return nil, fmt.Errorf("failed to get wallet transactions: %w", err)
}
return rows, nil
}
func (r *walletTraceRepository) ListAllocations(ctx context.Context, transactionID uuid.UUID) ([]entities.WalletLotAllocation, error) {
var rows []entities.WalletLotAllocation
// A debit draws in K9 order, so the lots' order is the order it took from them.
err := DBFromContext(ctx, r.db).WithContext(ctx).
Table("wallet_lot_allocations a").
Select("a.*").
Joins("JOIN wallet_lots l ON l.id = a.lot_id").
Where("a.transaction_id = ?", transactionID).
Order("l.expires_at NULLS LAST, l.created_at, l.id").
Scan(&rows).Error
if err != nil {
return nil, fmt.Errorf("failed to list wallet lot allocations: %w", err)
}
return rows, nil
}
func (r *walletTraceRepository) ListLotsBySource(ctx context.Context, transactionID uuid.UUID) ([]entities.WalletLot, error) {
var lots []entities.WalletLot
err := DBFromContext(ctx, r.db).WithContext(ctx).
Where("source_transaction_id = ?", transactionID).
Order("created_at, id").
Find(&lots).Error
if err != nil {
return nil, fmt.Errorf("failed to list wallet lots: %w", err)
}
return lots, nil
}
func (r *walletTraceRepository) GetLots(ctx context.Context, ids []uuid.UUID) ([]entities.WalletLot, error) {
var lots []entities.WalletLot
if len(ids) == 0 {
return lots, nil
}
if err := DBFromContext(ctx, r.db).WithContext(ctx).Where("id IN ?", ids).Find(&lots).Error; err != nil {
return nil, fmt.Errorf("failed to get wallet lots: %w", err)
}
return lots, nil
}
func (r *walletTraceRepository) CustomerNames(ctx context.Context, ids []uuid.UUID) (map[uuid.UUID]string, error) {
return r.names.CustomerNames(ctx, ids)
}