83 lines
2.8 KiB
Go
83 lines
2.8 KiB
Go
package repository
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/google/uuid"
|
||
|
|
"gorm.io/gorm"
|
||
|
|
|
||
|
|
"apskel-pos-be/internal/constants"
|
||
|
|
)
|
||
|
|
|
||
|
|
// WalletMoveCustomer is a customer on either side of an exchange or a transfer.
|
||
|
|
type WalletMoveCustomer struct {
|
||
|
|
ID uuid.UUID
|
||
|
|
OrganizationID uuid.UUID
|
||
|
|
Name string
|
||
|
|
PhoneNumber *string
|
||
|
|
IsDefault bool
|
||
|
|
IsActive bool
|
||
|
|
}
|
||
|
|
|
||
|
|
// WalletMoveRepository reads what exchanges and transfers check before moving a
|
||
|
|
// balance (docs/prd-point-coin.md F4, F5). The balance itself only moves through
|
||
|
|
// WalletProcessor.
|
||
|
|
type WalletMoveRepository interface {
|
||
|
|
// GetCustomer returns ErrWalletNotFound when the customer does not exist.
|
||
|
|
GetCustomer(ctx context.Context, customerID uuid.UUID) (*WalletMoveCustomer, error)
|
||
|
|
// FindCustomerByPhone returns ErrWalletNotFound when no customer has the number.
|
||
|
|
// Phone numbers are unique across organizations, so there is at most one.
|
||
|
|
FindCustomerByPhone(ctx context.Context, phoneNumber string) (*WalletMoveCustomer, error)
|
||
|
|
// TransferredOutSince sums what a customer has sent in one currency since a time.
|
||
|
|
// Call it under the sender's wallet lock, so transfers at the same time count.
|
||
|
|
TransferredOutSince(ctx context.Context, customerID uuid.UUID, currency string, since time.Time) (int64, error)
|
||
|
|
}
|
||
|
|
|
||
|
|
type walletMoveRepository struct {
|
||
|
|
db *gorm.DB
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewWalletMoveRepository(db *gorm.DB) WalletMoveRepository {
|
||
|
|
return &walletMoveRepository{db: db}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *walletMoveRepository) GetCustomer(ctx context.Context, customerID uuid.UUID) (*WalletMoveCustomer, error) {
|
||
|
|
return r.customer(ctx, "id = ?", customerID)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *walletMoveRepository) FindCustomerByPhone(ctx context.Context, phoneNumber string) (*WalletMoveCustomer, error) {
|
||
|
|
return r.customer(ctx, "phone_number = ?", phoneNumber)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *walletMoveRepository) TransferredOutSince(ctx context.Context, customerID uuid.UUID, currency string, since time.Time) (int64, error) {
|
||
|
|
var total int64
|
||
|
|
err := DBFromContext(ctx, r.db).WithContext(ctx).
|
||
|
|
Table("wallet_transactions").
|
||
|
|
Select("COALESCE(SUM(-amount), 0)").
|
||
|
|
Where("customer_id = ? AND currency = ? AND type = ? AND created_at >= ?", customerID, currency, constants.WalletTxTypeTransferOut, since).
|
||
|
|
Scan(&total).Error
|
||
|
|
if err != nil {
|
||
|
|
return 0, fmt.Errorf("failed to sum transfers: %w", err)
|
||
|
|
}
|
||
|
|
return total, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *walletMoveRepository) customer(ctx context.Context, where string, arg interface{}) (*WalletMoveCustomer, error) {
|
||
|
|
var c WalletMoveCustomer
|
||
|
|
err := DBFromContext(ctx, r.db).WithContext(ctx).
|
||
|
|
Table("customers").
|
||
|
|
Select("id, organization_id, name, phone_number, is_default, is_active").
|
||
|
|
Where(where, arg).
|
||
|
|
Take(&c).Error
|
||
|
|
if err != nil {
|
||
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
|
|
return nil, ErrWalletNotFound
|
||
|
|
}
|
||
|
|
return nil, fmt.Errorf("failed to get customer: %w", err)
|
||
|
|
}
|
||
|
|
return &c, nil
|
||
|
|
}
|