fixed ledger account name propagation when creating ledger account

This commit is contained in:
Stephan D
2026-03-04 18:52:43 +01:00
parent 706a57e860
commit d666c4ce51
4 changed files with 137 additions and 23 deletions

View File

@@ -7,6 +7,7 @@ import (
"github.com/tech/sendico/ledger/storage"
"github.com/tech/sendico/pkg/db/repository"
"github.com/tech/sendico/pkg/db/repository/builder"
ri "github.com/tech/sendico/pkg/db/repository/index"
"github.com/tech/sendico/pkg/merrors"
"github.com/tech/sendico/pkg/mlogger"
@@ -24,6 +25,11 @@ type accountsStore struct {
repo repository.Repository
}
const (
orgCurrencyRoleNonOperatingIndex = "org_currency_role_non_operating_unique"
orgCurrencyRoleSystemOperatingName = "org_currency_role_system_operating_unique"
)
func NewAccounts(logger mlogger.Logger, db *mongo.Database) (storage.AccountsStore, error) {
repo := repository.CreateMongoRepository(db, mservice.LedgerAccounts)
@@ -41,7 +47,7 @@ func NewAccounts(logger mlogger.Logger, db *mongo.Database) (storage.AccountsSto
return nil, err
}
// Create compound index on organizationRef + currency + role (unique)
// Keep role uniqueness for non-operating organization accounts.
roleIndex := &ri.Definition{
Keys: []ri.Key{
{Field: "organizationRef", Sort: ri.Asc},
@@ -49,16 +55,36 @@ func NewAccounts(logger mlogger.Logger, db *mongo.Database) (storage.AccountsSto
{Field: "role", Sort: ri.Asc},
},
Unique: true,
PartialFilter: repository.Filter(
"scope",
pkm.LedgerAccountScopeOrganization,
),
Name: orgCurrencyRoleNonOperatingIndex,
PartialFilter: repository.Query().
Filter(repository.Field("scope"), pkm.LedgerAccountScopeOrganization).
Comparison(repository.Field("role"), builder.Ne, account_role.AccountRoleOperating),
}
if err := repo.CreateIndex(roleIndex); err != nil {
logger.Error("Failed to ensure accounts role index", zap.Error(err))
return nil, err
}
// Ensure only one system-tagged operating role per organization/currency.
systemOperatingRoleIndex := &ri.Definition{
Keys: []ri.Key{
{Field: "organizationRef", Sort: ri.Asc},
{Field: "currency", Sort: ri.Asc},
{Field: "role", Sort: ri.Asc},
{Field: "metadata.system", Sort: ri.Asc},
},
Unique: true,
Name: orgCurrencyRoleSystemOperatingName,
PartialFilter: repository.Query().
Filter(repository.Field("scope"), pkm.LedgerAccountScopeOrganization).
Filter(repository.Field("role"), account_role.AccountRoleOperating).
Filter(repository.Field("metadata.system"), "true"),
}
if err := repo.CreateIndex(systemOperatingRoleIndex); err != nil {
logger.Error("Failed to ensure system operating 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{
@@ -182,14 +208,34 @@ func (a *accountsStore) GetByRole(ctx context.Context, orgRef bson.ObjectID, cur
return nil, merrors.InvalidArgument("accountsStore: empty role")
}
result := &pkm.LedgerAccount{}
limit := int64(1)
// Prefer topology/system-tagged account when present.
systemQuery := repository.Query().
Filter(repository.Field("organizationRef"), orgRef).
Filter(repository.Field("currency"), currency).
Filter(repository.Field("role"), role).
Filter(repository.Field("scope"), pkm.LedgerAccountScopeOrganization).
Filter(repository.Field("metadata.system"), "true").
Limit(&limit)
if err := a.repo.FindOneByFilter(ctx, systemQuery, result); err == nil {
a.logger.Debug("System account loaded by role", mzap.ObjRef("accountRef", *result.GetID()),
zap.String("currency", currency), zap.String("role", string(role)))
return result, nil
} else if !errors.Is(err, merrors.ErrNoData) {
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
}
// Fallback to any organization account with the role.
query := repository.Query().
Filter(repository.Field("organizationRef"), orgRef).
Filter(repository.Field("currency"), currency).
Filter(repository.Field("role"), role).
Filter(repository.Field("scope"), pkm.LedgerAccountScopeOrganization).
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),