Treasury bot + ledger fix

This commit is contained in:
Stephan D
2026-03-04 20:01:37 +01:00
parent 75555520f3
commit b6f05f52dc
22 changed files with 2844 additions and 18 deletions

View File

@@ -3,11 +3,11 @@ package store
import (
"context"
"errors"
"fmt"
"strings"
"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"
@@ -26,10 +26,27 @@ type accountsStore struct {
}
const (
orgCurrencyRoleNonOperatingIndex = "org_currency_role_non_operating_unique"
orgCurrencyRoleNonOperatingPrefix = "org_currency_role_non_operating_unique"
orgCurrencyRoleSystemOperatingName = "org_currency_role_system_operating_unique"
)
var nonOperatingUniqueRoles = []account_role.AccountRole{
account_role.AccountRoleHold,
account_role.AccountRoleTransit,
account_role.AccountRoleSettlement,
account_role.AccountRoleClearing,
account_role.AccountRolePending,
account_role.AccountRoleReserve,
account_role.AccountRoleLiquidity,
account_role.AccountRoleFee,
account_role.AccountRoleChargeback,
account_role.AccountRoleAdjustment,
}
func nonOperatingRoleIndexName(role account_role.AccountRole) string {
return fmt.Sprintf("%s_%s", orgCurrencyRoleNonOperatingPrefix, role)
}
func NewAccounts(logger mlogger.Logger, db *mongo.Database) (storage.AccountsStore, error) {
repo := repository.CreateMongoRepository(db, mservice.LedgerAccounts)
@@ -48,21 +65,25 @@ func NewAccounts(logger mlogger.Logger, db *mongo.Database) (storage.AccountsSto
}
// Keep role uniqueness for non-operating organization accounts.
roleIndex := &ri.Definition{
Keys: []ri.Key{
{Field: "organizationRef", Sort: ri.Asc},
{Field: "currency", Sort: ri.Asc},
{Field: "role", Sort: ri.Asc},
},
Unique: true,
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
// Some Mongo-compatible backends reject partial filters that use negation ($ne/$not).
// Build one equality-based partial index per non-operating role for compatibility.
for _, role := range nonOperatingUniqueRoles {
roleIndex := &ri.Definition{
Keys: []ri.Key{
{Field: "organizationRef", Sort: ri.Asc},
{Field: "currency", Sort: ri.Asc},
{Field: "role", Sort: ri.Asc},
},
Unique: true,
Name: nonOperatingRoleIndexName(role),
PartialFilter: repository.Query().
Filter(repository.Field("scope"), pkm.LedgerAccountScopeOrganization).
Filter(repository.Field("role"), role),
}
if err := repo.CreateIndex(roleIndex); err != nil {
logger.Error("Failed to ensure accounts role index", zap.String("role", string(role)), zap.Error(err))
return nil, err
}
}
// Ensure only one system-tagged operating role per organization/currency.