ledger accounts improvement
This commit is contained in:
@@ -3,13 +3,15 @@ package store
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/tech/sendico/ledger/storage"
|
||||
"github.com/tech/sendico/ledger/storage/model"
|
||||
"github.com/tech/sendico/pkg/db/repository"
|
||||
ri "github.com/tech/sendico/pkg/db/repository/index"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
pkm "github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
@@ -22,7 +24,7 @@ type accountsStore struct {
|
||||
}
|
||||
|
||||
func NewAccounts(logger mlogger.Logger, db *mongo.Database) (storage.AccountsStore, error) {
|
||||
repo := repository.CreateMongoRepository(db, model.AccountsCollection)
|
||||
repo := repository.CreateMongoRepository(db, mservice.LedgerAccounts)
|
||||
|
||||
// Create compound index on organizationRef + accountCode + currency (unique)
|
||||
uniqueIndex := &ri.Definition{
|
||||
@@ -34,7 +36,40 @@ func NewAccounts(logger mlogger.Logger, db *mongo.Database) (storage.AccountsSto
|
||||
Unique: true,
|
||||
}
|
||||
if err := repo.CreateIndex(uniqueIndex); err != nil {
|
||||
logger.Error("failed to ensure accounts unique index", zap.Error(err))
|
||||
logger.Error("Failed to ensure accounts unique index", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create compound index on organizationRef + currency + role (unique)
|
||||
roleIndex := &ri.Definition{
|
||||
Keys: []ri.Key{
|
||||
{Field: "organizationRef", Sort: ri.Asc},
|
||||
{Field: "currency", Sort: ri.Asc},
|
||||
{Field: "role", Sort: ri.Asc},
|
||||
},
|
||||
Unique: true,
|
||||
PartialFilter: repository.Filter(
|
||||
"scope",
|
||||
pkm.LedgerAccountScopeOrganization,
|
||||
),
|
||||
}
|
||||
if err := repo.CreateIndex(roleIndex); err != nil {
|
||||
logger.Error("Failed to ensure accounts role index", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create compound index on scope + systemPurpose + currency (unique) for system accounts
|
||||
systemIndex := &ri.Definition{
|
||||
Keys: []ri.Key{
|
||||
{Field: "scope", Sort: ri.Asc},
|
||||
{Field: "systemPurpose", Sort: ri.Asc},
|
||||
{Field: "currency", Sort: ri.Asc},
|
||||
},
|
||||
Unique: true,
|
||||
PartialFilter: repository.Filter("scope", pkm.LedgerAccountScopeSystem),
|
||||
}
|
||||
if err := repo.CreateIndex(systemIndex); err != nil {
|
||||
logger.Error("Failed to ensure system accounts index", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -45,12 +80,12 @@ func NewAccounts(logger mlogger.Logger, db *mongo.Database) (storage.AccountsSto
|
||||
},
|
||||
}
|
||||
if err := repo.CreateIndex(orgIndex); err != nil {
|
||||
logger.Error("failed to ensure accounts organization index", zap.Error(err))
|
||||
logger.Error("Failed to ensure accounts organization index", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
childLogger := logger.Named(model.AccountsCollection)
|
||||
childLogger.Debug("accounts store initialised", zap.String("collection", model.AccountsCollection))
|
||||
childLogger := logger.Named(mservice.LedgerAccounts)
|
||||
childLogger.Info("Accounts store initialised", zap.String("collection", mservice.LedgerAccounts))
|
||||
|
||||
return &accountsStore{
|
||||
logger: childLogger,
|
||||
@@ -58,59 +93,58 @@ func NewAccounts(logger mlogger.Logger, db *mongo.Database) (storage.AccountsSto
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *accountsStore) Create(ctx context.Context, account *model.Account) error {
|
||||
func (a *accountsStore) Create(ctx context.Context, account *pkm.LedgerAccount) error {
|
||||
if account == nil {
|
||||
a.logger.Warn("attempt to create nil account")
|
||||
a.logger.Warn("Attempt to create nil account")
|
||||
return merrors.InvalidArgument("accountsStore: nil account")
|
||||
}
|
||||
|
||||
if err := a.repo.Insert(ctx, account, nil); err != nil {
|
||||
if mongo.IsDuplicateKeyError(err) {
|
||||
a.logger.Warn("duplicate account code", zap.String("accountCode", account.AccountCode),
|
||||
a.logger.Warn("Duplicate account code", zap.String("account_code", account.AccountCode),
|
||||
zap.String("currency", account.Currency))
|
||||
return merrors.DataConflict("account with this code and currency already exists")
|
||||
}
|
||||
a.logger.Warn("failed to create account", zap.Error(err))
|
||||
a.logger.Warn("Failed to create account", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
a.logger.Debug("account created", zap.String("accountCode", account.AccountCode),
|
||||
a.logger.Debug("Account created", zap.String("account_code", account.AccountCode),
|
||||
zap.String("currency", account.Currency))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *accountsStore) Get(ctx context.Context, accountRef primitive.ObjectID) (*model.Account, error) {
|
||||
func (a *accountsStore) Get(ctx context.Context, accountRef primitive.ObjectID) (*pkm.LedgerAccount, error) {
|
||||
if accountRef.IsZero() {
|
||||
a.logger.Warn("attempt to get account with zero ID")
|
||||
a.logger.Warn("Attempt to get account with zero ID")
|
||||
return nil, merrors.InvalidArgument("accountsStore: zero account ID")
|
||||
}
|
||||
|
||||
result := &model.Account{}
|
||||
result := &pkm.LedgerAccount{}
|
||||
if err := a.repo.Get(ctx, accountRef, result); err != nil {
|
||||
if errors.Is(err, merrors.ErrNoData) {
|
||||
a.logger.Debug("account not found", mzap.ObjRef("account_ref", accountRef))
|
||||
a.logger.Debug("Account not found", mzap.ObjRef("account_ref", accountRef))
|
||||
return nil, storage.ErrAccountNotFound
|
||||
}
|
||||
a.logger.Warn("failed to get account", zap.Error(err), mzap.ObjRef("account_ref", accountRef))
|
||||
a.logger.Warn("Failed to get account", zap.Error(err), mzap.ObjRef("account_ref", accountRef))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a.logger.Debug("account loaded", mzap.ObjRef("account_ref", accountRef),
|
||||
zap.String("accountCode", result.AccountCode))
|
||||
a.logger.Debug("Account loaded", mzap.ObjRef("account_ref", accountRef), zap.String("account_code", result.AccountCode))
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (a *accountsStore) GetByAccountCode(ctx context.Context, orgRef primitive.ObjectID, accountCode, currency string) (*model.Account, error) {
|
||||
func (a *accountsStore) GetByAccountCode(ctx context.Context, orgRef primitive.ObjectID, accountCode, currency string) (*pkm.LedgerAccount, error) {
|
||||
if orgRef.IsZero() {
|
||||
a.logger.Warn("attempt to get account with zero organization ID")
|
||||
a.logger.Warn("Attempt to get account with zero organization ID")
|
||||
return nil, merrors.InvalidArgument("accountsStore: zero organization ID")
|
||||
}
|
||||
if accountCode == "" {
|
||||
a.logger.Warn("attempt to get account with empty code")
|
||||
a.logger.Warn("Attempt to get account with empty code")
|
||||
return nil, merrors.InvalidArgument("accountsStore: empty account code")
|
||||
}
|
||||
if currency == "" {
|
||||
a.logger.Warn("attempt to get account with empty currency")
|
||||
a.logger.Warn("Attempt to get account with empty currency")
|
||||
return nil, merrors.InvalidArgument("accountsStore: empty currency")
|
||||
}
|
||||
|
||||
@@ -119,29 +153,100 @@ func (a *accountsStore) GetByAccountCode(ctx context.Context, orgRef primitive.O
|
||||
Filter(repository.Field("accountCode"), accountCode).
|
||||
Filter(repository.Field("currency"), currency)
|
||||
|
||||
result := &model.Account{}
|
||||
result := &pkm.LedgerAccount{}
|
||||
if err := a.repo.FindOneByFilter(ctx, query, result); err != nil {
|
||||
if errors.Is(err, merrors.ErrNoData) {
|
||||
a.logger.Debug("account not found by code", zap.String("accountCode", accountCode),
|
||||
zap.String("currency", currency))
|
||||
a.logger.Debug("Account not found by code", zap.String("account_code", accountCode), zap.String("currency", currency))
|
||||
return nil, storage.ErrAccountNotFound
|
||||
}
|
||||
a.logger.Warn("failed to get account by code", zap.Error(err), zap.String("accountCode", accountCode))
|
||||
a.logger.Warn("Failed to get account by code", zap.Error(err), zap.String("account_code", accountCode))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a.logger.Debug("account loaded by code", zap.String("accountCode", accountCode),
|
||||
zap.String("currency", currency))
|
||||
a.logger.Debug("Account loaded by code", zap.String("account_code", accountCode), zap.String("currency", currency))
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (a *accountsStore) GetDefaultSettlement(ctx context.Context, orgRef primitive.ObjectID, currency string) (*model.Account, error) {
|
||||
func (a *accountsStore) GetByRole(ctx context.Context, orgRef primitive.ObjectID, currency string, role pkm.AccountRole) (*pkm.LedgerAccount, error) {
|
||||
if orgRef.IsZero() {
|
||||
a.logger.Warn("attempt to get default settlement with zero organization ID")
|
||||
a.logger.Warn("Attempt to get account with zero organization ID")
|
||||
return nil, merrors.InvalidArgument("accountsStore: zero organization ID")
|
||||
}
|
||||
if currency == "" {
|
||||
a.logger.Warn("attempt to get default settlement with empty currency")
|
||||
a.logger.Warn("Attempt to get account with empty currency")
|
||||
return nil, merrors.InvalidArgument("accountsStore: empty currency")
|
||||
}
|
||||
if strings.TrimSpace(string(role)) == "" {
|
||||
a.logger.Warn("Attempt to get account with empty role")
|
||||
return nil, merrors.InvalidArgument("accountsStore: empty role")
|
||||
}
|
||||
|
||||
limit := int64(1)
|
||||
query := repository.Query().
|
||||
Filter(repository.Field("organizationRef"), orgRef).
|
||||
Filter(repository.Field("currency"), currency).
|
||||
Filter(repository.Field("role"), role).
|
||||
Limit(&limit)
|
||||
|
||||
result := &pkm.LedgerAccount{}
|
||||
if err := a.repo.FindOneByFilter(ctx, query, result); err != nil {
|
||||
if errors.Is(err, merrors.ErrNoData) {
|
||||
a.logger.Debug("Account not found by role", zap.String("currency", currency),
|
||||
zap.String("role", string(role)), mzap.ObjRef("organization_ref", orgRef))
|
||||
return nil, storage.ErrAccountNotFound
|
||||
}
|
||||
a.logger.Warn("Failed to get account by role", zap.Error(err), mzap.ObjRef("organization_ref", orgRef),
|
||||
zap.String("currency", currency), zap.String("role", string(role)))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a.logger.Debug("Account loaded by role", mzap.ObjRef("accountRef", *result.GetID()),
|
||||
zap.String("currency", currency), zap.String("role", string(role)))
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (a *accountsStore) GetSystemAccount(ctx context.Context, purpose pkm.SystemAccountPurpose, currency string) (*pkm.LedgerAccount, error) {
|
||||
if strings.TrimSpace(string(purpose)) == "" {
|
||||
a.logger.Warn("Attempt to get system account with empty purpose")
|
||||
return nil, merrors.InvalidArgument("accountsStore: empty system purpose")
|
||||
}
|
||||
normalizedCurrency := strings.ToUpper(strings.TrimSpace(currency))
|
||||
if normalizedCurrency == "" {
|
||||
a.logger.Warn("Attempt to get system account with empty currency")
|
||||
return nil, merrors.InvalidArgument("accountsStore: empty currency")
|
||||
}
|
||||
|
||||
limit := int64(1)
|
||||
query := repository.Query().
|
||||
Filter(repository.Field("scope"), pkm.LedgerAccountScopeSystem).
|
||||
Filter(repository.Field("systemPurpose"), purpose).
|
||||
Filter(repository.Field("currency"), normalizedCurrency).
|
||||
Limit(&limit)
|
||||
|
||||
result := &pkm.LedgerAccount{}
|
||||
if err := a.repo.FindOneByFilter(ctx, query, result); err != nil {
|
||||
if errors.Is(err, merrors.ErrNoData) {
|
||||
a.logger.Debug("System account not found", zap.String("currency", normalizedCurrency),
|
||||
zap.String("purpose", string(purpose)))
|
||||
return nil, storage.ErrAccountNotFound
|
||||
}
|
||||
a.logger.Warn("Failed to get system account", zap.Error(err),
|
||||
zap.String("currency", normalizedCurrency), zap.String("purpose", string(purpose)))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a.logger.Debug("System account loaded", mzap.ObjRef("accountRef", *result.GetID()),
|
||||
zap.String("currency", normalizedCurrency), zap.String("purpose", string(purpose)))
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (a *accountsStore) GetDefaultSettlement(ctx context.Context, orgRef primitive.ObjectID, currency string) (*pkm.LedgerAccount, error) {
|
||||
if orgRef.IsZero() {
|
||||
a.logger.Warn("Attempt to get default settlement with zero organization ID")
|
||||
return nil, merrors.InvalidArgument("accountsStore: zero organization ID")
|
||||
}
|
||||
if currency == "" {
|
||||
a.logger.Warn("Attempt to get default settlement with empty currency")
|
||||
return nil, merrors.InvalidArgument("accountsStore: empty currency")
|
||||
}
|
||||
|
||||
@@ -149,33 +254,31 @@ func (a *accountsStore) GetDefaultSettlement(ctx context.Context, orgRef primiti
|
||||
query := repository.Query().
|
||||
Filter(repository.Field("organizationRef"), orgRef).
|
||||
Filter(repository.Field("currency"), currency).
|
||||
Filter(repository.Field("isSettlement"), true).
|
||||
Filter(repository.Field("role"), pkm.AccountRoleSettlement).
|
||||
Limit(&limit)
|
||||
|
||||
result := &model.Account{}
|
||||
result := &pkm.LedgerAccount{}
|
||||
if err := a.repo.FindOneByFilter(ctx, query, result); err != nil {
|
||||
if errors.Is(err, merrors.ErrNoData) {
|
||||
a.logger.Debug("default settlement account not found",
|
||||
a.logger.Debug("Default settlement account not found",
|
||||
zap.String("currency", currency),
|
||||
mzap.ObjRef("organization_ref", orgRef))
|
||||
return nil, storage.ErrAccountNotFound
|
||||
}
|
||||
a.logger.Warn("failed to get default settlement account", zap.Error(err),
|
||||
a.logger.Warn("Failed to get default settlement account", zap.Error(err),
|
||||
mzap.ObjRef("organization_ref", orgRef),
|
||||
zap.String("currency", currency))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a.logger.Debug("default settlement account loaded",
|
||||
zap.String("accountRef", result.GetID().Hex()),
|
||||
zap.String("currency", currency))
|
||||
a.logger.Debug("Default settlement account loaded", mzap.ObjRef("accountRef", *result.GetID()), zap.String("currency", currency))
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (a *accountsStore) ListByOrganization(ctx context.Context, orgRef primitive.ObjectID, limit int, offset int) ([]*model.Account, error) {
|
||||
func (a *accountsStore) ListByOrganization(ctx context.Context, orgRef primitive.ObjectID, filter *storage.AccountsFilter, limit int, offset int) ([]*pkm.LedgerAccount, error) {
|
||||
if orgRef.IsZero() {
|
||||
a.logger.Warn("attempt to list accounts with zero organization ID")
|
||||
return nil, merrors.InvalidArgument("accountsStore: zero organization ID")
|
||||
a.logger.Warn("Attempt to list accounts with zero organization reference")
|
||||
return nil, merrors.InvalidArgument("accountsStore: zero organization reference")
|
||||
}
|
||||
|
||||
limit64 := int64(limit)
|
||||
@@ -185,9 +288,19 @@ func (a *accountsStore) ListByOrganization(ctx context.Context, orgRef primitive
|
||||
Limit(&limit64).
|
||||
Offset(&offset64)
|
||||
|
||||
accounts := make([]*model.Account, 0)
|
||||
if filter != nil && filter.OwnerRefFilter != nil {
|
||||
if filter.OwnerRefFilter.IsZero() {
|
||||
// Filter for accounts with nil owner_ref
|
||||
query = query.Filter(repository.Field("ownerRef"), nil)
|
||||
} else {
|
||||
// Filter for accounts matching owner_ref
|
||||
query = query.Filter(repository.Field("ownerRef"), *filter.OwnerRefFilter)
|
||||
}
|
||||
}
|
||||
|
||||
accounts := make([]*pkm.LedgerAccount, 0)
|
||||
err := a.repo.FindManyByFilter(ctx, query, func(cur *mongo.Cursor) error {
|
||||
doc := &model.Account{}
|
||||
doc := &pkm.LedgerAccount{}
|
||||
if err := cur.Decode(doc); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -195,27 +308,26 @@ func (a *accountsStore) ListByOrganization(ctx context.Context, orgRef primitive
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
a.logger.Warn("failed to list accounts", zap.Error(err))
|
||||
a.logger.Warn("Failed to list accounts", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a.logger.Debug("listed accounts", zap.Int("count", len(accounts)))
|
||||
a.logger.Debug("Listed accounts", zap.Int("count", len(accounts)))
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
func (a *accountsStore) UpdateStatus(ctx context.Context, accountRef primitive.ObjectID, status model.AccountStatus) error {
|
||||
func (a *accountsStore) UpdateStatus(ctx context.Context, accountRef primitive.ObjectID, status pkm.LedgerAccountStatus) error {
|
||||
if accountRef.IsZero() {
|
||||
a.logger.Warn("attempt to update account status with zero ID")
|
||||
return merrors.InvalidArgument("accountsStore: zero account ID")
|
||||
a.logger.Warn("Attempt to update account status with zero reference")
|
||||
return merrors.InvalidArgument("accountsStore: zero account reference")
|
||||
}
|
||||
|
||||
patch := repository.Patch().Set(repository.Field("status"), status)
|
||||
if err := a.repo.Patch(ctx, accountRef, patch); err != nil {
|
||||
a.logger.Warn("failed to update account status", zap.Error(err), mzap.ObjRef("account_ref", accountRef))
|
||||
a.logger.Warn("Failed to update account status", zap.Error(err), mzap.ObjRef("account_ref", accountRef))
|
||||
return err
|
||||
}
|
||||
|
||||
a.logger.Debug("account status updated", mzap.ObjRef("account_ref", accountRef),
|
||||
zap.String("status", string(status)))
|
||||
a.logger.Debug("Account status updated", mzap.ObjRef("account_ref", accountRef), zap.String("status", string(status)))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,15 +5,15 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tech/sendico/ledger/storage"
|
||||
"github.com/tech/sendico/ledger/storage/model"
|
||||
"github.com/tech/sendico/pkg/db/repository"
|
||||
"github.com/tech/sendico/pkg/db/repository/builder"
|
||||
rd "github.com/tech/sendico/pkg/db/repository/decoder"
|
||||
"github.com/tech/sendico/pkg/db/storable"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
pkm "github.com/tech/sendico/pkg/model"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.uber.org/zap"
|
||||
@@ -24,20 +24,20 @@ func TestAccountsStore_Create(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
var insertedAccount *model.Account
|
||||
var insertedAccount *pkm.LedgerAccount
|
||||
stub := &repositoryStub{
|
||||
InsertFunc: func(ctx context.Context, object storable.Storable, _ builder.Query) error {
|
||||
insertedAccount = object.(*model.Account)
|
||||
insertedAccount = object.(*pkm.LedgerAccount)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
store := &accountsStore{logger: logger, repo: stub}
|
||||
account := &model.Account{
|
||||
account := &pkm.LedgerAccount{
|
||||
AccountCode: "1000",
|
||||
Currency: "USD",
|
||||
AccountType: model.AccountTypeAsset,
|
||||
Status: model.AccountStatusActive,
|
||||
AccountType: pkm.LedgerAccountTypeAsset,
|
||||
Status: pkm.LedgerAccountStatusActive,
|
||||
AllowNegative: false,
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func TestAccountsStore_Create(t *testing.T) {
|
||||
}
|
||||
|
||||
store := &accountsStore{logger: logger, repo: stub}
|
||||
account := &model.Account{
|
||||
account := &pkm.LedgerAccount{
|
||||
AccountCode: "1000",
|
||||
Currency: "USD",
|
||||
}
|
||||
@@ -91,7 +91,7 @@ func TestAccountsStore_Create(t *testing.T) {
|
||||
}
|
||||
|
||||
store := &accountsStore{logger: logger, repo: stub}
|
||||
account := &model.Account{AccountCode: "1000", Currency: "USD"}
|
||||
account := &pkm.LedgerAccount{AccountCode: "1000", Currency: "USD"}
|
||||
|
||||
err := store.Create(ctx, account)
|
||||
|
||||
@@ -108,7 +108,7 @@ func TestAccountsStore_Get(t *testing.T) {
|
||||
accountRef := primitive.NewObjectID()
|
||||
stub := &repositoryStub{
|
||||
GetFunc: func(ctx context.Context, id primitive.ObjectID, result storable.Storable) error {
|
||||
account := result.(*model.Account)
|
||||
account := result.(*pkm.LedgerAccount)
|
||||
account.SetID(accountRef)
|
||||
account.AccountCode = "1000"
|
||||
account.Currency = "USD"
|
||||
@@ -178,7 +178,7 @@ func TestAccountsStore_GetByAccountCode(t *testing.T) {
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
stub := &repositoryStub{
|
||||
FindOneByFilterFunc: func(ctx context.Context, _ builder.Query, result storable.Storable) error {
|
||||
account := result.(*model.Account)
|
||||
account := result.(*pkm.LedgerAccount)
|
||||
account.AccountCode = "1000"
|
||||
account.Currency = "USD"
|
||||
return nil
|
||||
@@ -243,6 +243,89 @@ func TestAccountsStore_GetByAccountCode(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccountsStore_GetByRole(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
orgRef := primitive.NewObjectID()
|
||||
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
stub := &repositoryStub{
|
||||
FindOneByFilterFunc: func(ctx context.Context, _ builder.Query, result storable.Storable) error {
|
||||
account := result.(*pkm.LedgerAccount)
|
||||
account.Currency = "USD"
|
||||
account.Role = pkm.AccountRoleOperating
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
store := &accountsStore{logger: logger, repo: stub}
|
||||
result, err := store.GetByRole(ctx, orgRef, "USD", pkm.AccountRoleOperating)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, pkm.AccountRoleOperating, result.Role)
|
||||
assert.Equal(t, "USD", result.Currency)
|
||||
})
|
||||
|
||||
t.Run("ZeroOrganizationID", func(t *testing.T) {
|
||||
store := &accountsStore{logger: logger, repo: &repositoryStub{}}
|
||||
result, err := store.GetByRole(ctx, primitive.NilObjectID, "USD", pkm.AccountRoleOperating)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, result)
|
||||
assert.True(t, errors.Is(err, merrors.ErrInvalidArg))
|
||||
})
|
||||
|
||||
t.Run("EmptyCurrency", func(t *testing.T) {
|
||||
store := &accountsStore{logger: logger, repo: &repositoryStub{}}
|
||||
result, err := store.GetByRole(ctx, orgRef, "", pkm.AccountRoleOperating)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, result)
|
||||
assert.True(t, errors.Is(err, merrors.ErrInvalidArg))
|
||||
})
|
||||
|
||||
t.Run("EmptyRole", func(t *testing.T) {
|
||||
store := &accountsStore{logger: logger, repo: &repositoryStub{}}
|
||||
result, err := store.GetByRole(ctx, orgRef, "USD", "")
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, result)
|
||||
assert.True(t, errors.Is(err, merrors.ErrInvalidArg))
|
||||
})
|
||||
|
||||
t.Run("NotFound", func(t *testing.T) {
|
||||
stub := &repositoryStub{
|
||||
FindOneByFilterFunc: func(ctx context.Context, _ builder.Query, result storable.Storable) error {
|
||||
return merrors.ErrNoData
|
||||
},
|
||||
}
|
||||
|
||||
store := &accountsStore{logger: logger, repo: stub}
|
||||
result, err := store.GetByRole(ctx, orgRef, "USD", pkm.AccountRoleOperating)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, result)
|
||||
assert.True(t, errors.Is(err, storage.ErrAccountNotFound))
|
||||
})
|
||||
|
||||
t.Run("FindError", func(t *testing.T) {
|
||||
expectedErr := errors.New("database error")
|
||||
stub := &repositoryStub{
|
||||
FindOneByFilterFunc: func(ctx context.Context, _ builder.Query, result storable.Storable) error {
|
||||
return expectedErr
|
||||
},
|
||||
}
|
||||
|
||||
store := &accountsStore{logger: logger, repo: stub}
|
||||
result, err := store.GetByRole(ctx, orgRef, "USD", pkm.AccountRoleOperating)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, result)
|
||||
assert.Equal(t, expectedErr, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccountsStore_GetDefaultSettlement(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
@@ -251,10 +334,10 @@ func TestAccountsStore_GetDefaultSettlement(t *testing.T) {
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
stub := &repositoryStub{
|
||||
FindOneByFilterFunc: func(ctx context.Context, _ builder.Query, result storable.Storable) error {
|
||||
account := result.(*model.Account)
|
||||
account := result.(*pkm.LedgerAccount)
|
||||
account.SetID(primitive.NewObjectID())
|
||||
account.Currency = "USD"
|
||||
account.IsSettlement = true
|
||||
account.Role = pkm.AccountRoleSettlement
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -264,7 +347,7 @@ func TestAccountsStore_GetDefaultSettlement(t *testing.T) {
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
assert.True(t, result.IsSettlement)
|
||||
assert.Equal(t, pkm.AccountRoleSettlement, result.Role)
|
||||
assert.Equal(t, "USD", result.Currency)
|
||||
})
|
||||
|
||||
@@ -318,6 +401,83 @@ func TestAccountsStore_GetDefaultSettlement(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccountsStore_GetSystemAccount(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
stub := &repositoryStub{
|
||||
FindOneByFilterFunc: func(ctx context.Context, _ builder.Query, result storable.Storable) error {
|
||||
account := result.(*pkm.LedgerAccount)
|
||||
account.Currency = "USD"
|
||||
purpose := pkm.SystemAccountPurposeExternalSource
|
||||
account.SystemPurpose = &purpose
|
||||
account.Scope = pkm.LedgerAccountScopeSystem
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
store := &accountsStore{logger: logger, repo: stub}
|
||||
result, err := store.GetSystemAccount(ctx, pkm.SystemAccountPurposeExternalSource, "USD")
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
require.Equal(t, pkm.LedgerAccountScopeSystem, result.Scope)
|
||||
require.NotNil(t, result.SystemPurpose)
|
||||
require.Equal(t, pkm.SystemAccountPurposeExternalSource, *result.SystemPurpose)
|
||||
require.Equal(t, "USD", result.Currency)
|
||||
})
|
||||
|
||||
t.Run("EmptyPurpose", func(t *testing.T) {
|
||||
store := &accountsStore{logger: logger, repo: &repositoryStub{}}
|
||||
result, err := store.GetSystemAccount(ctx, "", "USD")
|
||||
|
||||
require.Error(t, err)
|
||||
require.Nil(t, result)
|
||||
require.True(t, errors.Is(err, merrors.ErrInvalidArg))
|
||||
})
|
||||
|
||||
t.Run("EmptyCurrency", func(t *testing.T) {
|
||||
store := &accountsStore{logger: logger, repo: &repositoryStub{}}
|
||||
result, err := store.GetSystemAccount(ctx, pkm.SystemAccountPurposeExternalSink, "")
|
||||
|
||||
require.Error(t, err)
|
||||
require.Nil(t, result)
|
||||
require.True(t, errors.Is(err, merrors.ErrInvalidArg))
|
||||
})
|
||||
|
||||
t.Run("NotFound", func(t *testing.T) {
|
||||
stub := &repositoryStub{
|
||||
FindOneByFilterFunc: func(ctx context.Context, _ builder.Query, result storable.Storable) error {
|
||||
return merrors.ErrNoData
|
||||
},
|
||||
}
|
||||
|
||||
store := &accountsStore{logger: logger, repo: stub}
|
||||
result, err := store.GetSystemAccount(ctx, pkm.SystemAccountPurposeExternalSource, "USD")
|
||||
|
||||
require.Error(t, err)
|
||||
require.Nil(t, result)
|
||||
require.True(t, errors.Is(err, storage.ErrAccountNotFound))
|
||||
})
|
||||
|
||||
t.Run("FindError", func(t *testing.T) {
|
||||
expectedErr := errors.New("database error")
|
||||
stub := &repositoryStub{
|
||||
FindOneByFilterFunc: func(ctx context.Context, _ builder.Query, result storable.Storable) error {
|
||||
return expectedErr
|
||||
},
|
||||
}
|
||||
|
||||
store := &accountsStore{logger: logger, repo: stub}
|
||||
result, err := store.GetSystemAccount(ctx, pkm.SystemAccountPurposeExternalSource, "USD")
|
||||
|
||||
require.Error(t, err)
|
||||
require.Nil(t, result)
|
||||
require.Equal(t, expectedErr, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccountsStore_ListByOrganization(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
@@ -335,7 +495,7 @@ func TestAccountsStore_ListByOrganization(t *testing.T) {
|
||||
}
|
||||
|
||||
store := &accountsStore{logger: logger, repo: stub}
|
||||
results, err := store.ListByOrganization(ctx, orgRef, 10, 0)
|
||||
results, err := store.ListByOrganization(ctx, orgRef, nil, 10, 0)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.True(t, calledWithQuery, "FindManyByFilter should have been called")
|
||||
@@ -346,7 +506,7 @@ func TestAccountsStore_ListByOrganization(t *testing.T) {
|
||||
stub := &repositoryStub{}
|
||||
store := &accountsStore{logger: logger, repo: stub}
|
||||
|
||||
results, err := store.ListByOrganization(ctx, primitive.NilObjectID, 10, 0)
|
||||
results, err := store.ListByOrganization(ctx, primitive.NilObjectID, nil, 10, 0)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, results)
|
||||
@@ -361,7 +521,7 @@ func TestAccountsStore_ListByOrganization(t *testing.T) {
|
||||
}
|
||||
|
||||
store := &accountsStore{logger: logger, repo: stub}
|
||||
results, err := store.ListByOrganization(ctx, orgRef, 10, 0)
|
||||
results, err := store.ListByOrganization(ctx, orgRef, nil, 10, 0)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, results, 0)
|
||||
@@ -376,7 +536,7 @@ func TestAccountsStore_ListByOrganization(t *testing.T) {
|
||||
}
|
||||
|
||||
store := &accountsStore{logger: logger, repo: stub}
|
||||
results, err := store.ListByOrganization(ctx, orgRef, 10, 0)
|
||||
results, err := store.ListByOrganization(ctx, orgRef, nil, 10, 0)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, results)
|
||||
@@ -391,29 +551,29 @@ func TestAccountsStore_UpdateStatus(t *testing.T) {
|
||||
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
var patchedID primitive.ObjectID
|
||||
var patchedStatus model.AccountStatus
|
||||
var patchedStatus pkm.LedgerAccountStatus
|
||||
stub := &repositoryStub{
|
||||
PatchFunc: func(ctx context.Context, id primitive.ObjectID, _ repository.PatchDoc) error {
|
||||
patchedID = id
|
||||
// In real test, we'd inspect patch builder but this is sufficient for stub
|
||||
patchedStatus = model.AccountStatusFrozen
|
||||
patchedStatus = pkm.LedgerAccountStatusFrozen
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
store := &accountsStore{logger: logger, repo: stub}
|
||||
err := store.UpdateStatus(ctx, accountRef, model.AccountStatusFrozen)
|
||||
err := store.UpdateStatus(ctx, accountRef, pkm.LedgerAccountStatusFrozen)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, accountRef, patchedID)
|
||||
assert.Equal(t, model.AccountStatusFrozen, patchedStatus)
|
||||
assert.Equal(t, pkm.LedgerAccountStatusFrozen, patchedStatus)
|
||||
})
|
||||
|
||||
t.Run("ZeroID", func(t *testing.T) {
|
||||
stub := &repositoryStub{}
|
||||
store := &accountsStore{logger: logger, repo: stub}
|
||||
|
||||
err := store.UpdateStatus(ctx, primitive.NilObjectID, model.AccountStatusFrozen)
|
||||
err := store.UpdateStatus(ctx, primitive.NilObjectID, pkm.LedgerAccountStatusFrozen)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.True(t, errors.Is(err, merrors.ErrInvalidArg))
|
||||
@@ -428,7 +588,7 @@ func TestAccountsStore_UpdateStatus(t *testing.T) {
|
||||
}
|
||||
|
||||
store := &accountsStore{logger: logger, repo: stub}
|
||||
err := store.UpdateStatus(ctx, accountRef, model.AccountStatusFrozen)
|
||||
err := store.UpdateStatus(ctx, accountRef, pkm.LedgerAccountStatusFrozen)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, expectedErr, err)
|
||||
|
||||
Reference in New Issue
Block a user