refactored payment orchestration
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/model/account_role"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
)
|
||||
|
||||
@@ -40,7 +41,7 @@ type Account struct {
|
||||
// Posting policy & lifecycle
|
||||
AllowNegative bool `bson:"allowNegative" json:"allowNegative"`
|
||||
Status model.LedgerAccountStatus `bson:"status" json:"status"`
|
||||
Role model.AccountRole `bson:"role,omitempty" json:"role,omitempty"`
|
||||
Role account_role.AccountRole `bson:"role,omitempty" json:"role,omitempty"`
|
||||
|
||||
// Legal ownership history
|
||||
Ownerships []Ownership `bson:"ownerships,omitempty" json:"ownerships,omitempty"`
|
||||
@@ -78,17 +79,17 @@ func (a *Account) Validate() error {
|
||||
|
||||
if role := strings.TrimSpace(string(a.Role)); role != "" {
|
||||
switch a.Role {
|
||||
case model.AccountRoleOperating,
|
||||
model.AccountRoleHold,
|
||||
model.AccountRoleTransit,
|
||||
model.AccountRoleSettlement,
|
||||
model.AccountRoleClearing,
|
||||
model.AccountRolePending,
|
||||
model.AccountRoleReserve,
|
||||
model.AccountRoleLiquidity,
|
||||
model.AccountRoleFee,
|
||||
model.AccountRoleChargeback,
|
||||
model.AccountRoleAdjustment:
|
||||
case account_role.AccountRoleOperating,
|
||||
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:
|
||||
default:
|
||||
veAdd(&verr, "role", "invalid", "unknown account role")
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/tech/sendico/pkg/api/routers/gsresponse"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
pmodel "github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/model/account_role"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
describablev1 "github.com/tech/sendico/pkg/proto/common/describable/v1"
|
||||
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||
@@ -25,7 +26,7 @@ type createAccountParams struct {
|
||||
currency string
|
||||
modelType pmodel.LedgerAccountType
|
||||
modelStatus pmodel.LedgerAccountStatus
|
||||
modelRole pmodel.AccountRole
|
||||
modelRole account_role.AccountRole
|
||||
}
|
||||
|
||||
// validateCreateAccountInput validates and normalizes all fields from the request.
|
||||
@@ -93,7 +94,7 @@ func (s *Service) createAccountResponder(_ context.Context, req *ledgerv1.Create
|
||||
}
|
||||
|
||||
// Non-settlement accounts require a settlement account to exist first.
|
||||
if p.modelRole != pmodel.AccountRoleSettlement {
|
||||
if p.modelRole != account_role.AccountRoleSettlement {
|
||||
if _, err := s.ensureSettlementAccount(ctx, p.orgRef, p.currency); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -104,7 +105,7 @@ func (s *Service) createAccountResponder(_ context.Context, req *ledgerv1.Create
|
||||
}
|
||||
|
||||
// resolveTopologyAccount ensures ledger topology is initialized and returns the system account for the given role.
|
||||
func (s *Service) resolveTopologyAccount(ctx context.Context, orgRef bson.ObjectID, currency string, role pmodel.AccountRole) (*ledgerv1.CreateAccountResponse, error) {
|
||||
func (s *Service) resolveTopologyAccount(ctx context.Context, orgRef bson.ObjectID, currency string, role account_role.AccountRole) (*ledgerv1.CreateAccountResponse, error) {
|
||||
if err := s.ensureLedgerTopology(ctx, orgRef, currency); err != nil {
|
||||
recordAccountOperation("create", "error")
|
||||
return nil, err
|
||||
@@ -244,58 +245,58 @@ func modelAccountTypeToProto(t pmodel.LedgerAccountType) ledgerv1.AccountType {
|
||||
}
|
||||
}
|
||||
|
||||
func protoAccountRoleToModel(r ledgerv1.AccountRole) (pmodel.AccountRole, error) {
|
||||
func protoAccountRoleToModel(r ledgerv1.AccountRole) (account_role.AccountRole, error) {
|
||||
switch r {
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_OPERATING, ledgerv1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED:
|
||||
return pmodel.AccountRoleOperating, nil
|
||||
return account_role.AccountRoleOperating, nil
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_HOLD:
|
||||
return pmodel.AccountRoleHold, nil
|
||||
return account_role.AccountRoleHold, nil
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_TRANSIT:
|
||||
return pmodel.AccountRoleTransit, nil
|
||||
return account_role.AccountRoleTransit, nil
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_SETTLEMENT:
|
||||
return pmodel.AccountRoleSettlement, nil
|
||||
return account_role.AccountRoleSettlement, nil
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_CLEARING:
|
||||
return pmodel.AccountRoleClearing, nil
|
||||
return account_role.AccountRoleClearing, nil
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_PENDING:
|
||||
return pmodel.AccountRolePending, nil
|
||||
return account_role.AccountRolePending, nil
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_RESERVE:
|
||||
return pmodel.AccountRoleReserve, nil
|
||||
return account_role.AccountRoleReserve, nil
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_LIQUIDITY:
|
||||
return pmodel.AccountRoleLiquidity, nil
|
||||
return account_role.AccountRoleLiquidity, nil
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_FEE:
|
||||
return pmodel.AccountRoleFee, nil
|
||||
return account_role.AccountRoleFee, nil
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_CHARGEBACK:
|
||||
return pmodel.AccountRoleChargeback, nil
|
||||
return account_role.AccountRoleChargeback, nil
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_ADJUSTMENT:
|
||||
return pmodel.AccountRoleAdjustment, nil
|
||||
return account_role.AccountRoleAdjustment, nil
|
||||
default:
|
||||
return "", merrors.InvalidArgument("invalid account role")
|
||||
}
|
||||
}
|
||||
|
||||
func modelAccountRoleToProto(r pmodel.AccountRole) ledgerv1.AccountRole {
|
||||
func modelAccountRoleToProto(r account_role.AccountRole) ledgerv1.AccountRole {
|
||||
switch r {
|
||||
case pmodel.AccountRoleOperating, "":
|
||||
case account_role.AccountRoleOperating, "":
|
||||
return ledgerv1.AccountRole_ACCOUNT_ROLE_OPERATING
|
||||
case pmodel.AccountRoleHold:
|
||||
case account_role.AccountRoleHold:
|
||||
return ledgerv1.AccountRole_ACCOUNT_ROLE_HOLD
|
||||
case pmodel.AccountRoleTransit:
|
||||
case account_role.AccountRoleTransit:
|
||||
return ledgerv1.AccountRole_ACCOUNT_ROLE_TRANSIT
|
||||
case pmodel.AccountRoleSettlement:
|
||||
case account_role.AccountRoleSettlement:
|
||||
return ledgerv1.AccountRole_ACCOUNT_ROLE_SETTLEMENT
|
||||
case pmodel.AccountRoleClearing:
|
||||
case account_role.AccountRoleClearing:
|
||||
return ledgerv1.AccountRole_ACCOUNT_ROLE_CLEARING
|
||||
case pmodel.AccountRolePending:
|
||||
case account_role.AccountRolePending:
|
||||
return ledgerv1.AccountRole_ACCOUNT_ROLE_PENDING
|
||||
case pmodel.AccountRoleReserve:
|
||||
case account_role.AccountRoleReserve:
|
||||
return ledgerv1.AccountRole_ACCOUNT_ROLE_RESERVE
|
||||
case pmodel.AccountRoleLiquidity:
|
||||
case account_role.AccountRoleLiquidity:
|
||||
return ledgerv1.AccountRole_ACCOUNT_ROLE_LIQUIDITY
|
||||
case pmodel.AccountRoleFee:
|
||||
case account_role.AccountRoleFee:
|
||||
return ledgerv1.AccountRole_ACCOUNT_ROLE_FEE
|
||||
case pmodel.AccountRoleChargeback:
|
||||
case account_role.AccountRoleChargeback:
|
||||
return ledgerv1.AccountRole_ACCOUNT_ROLE_CHARGEBACK
|
||||
case pmodel.AccountRoleAdjustment:
|
||||
case account_role.AccountRoleAdjustment:
|
||||
return ledgerv1.AccountRole_ACCOUNT_ROLE_ADJUSTMENT
|
||||
default:
|
||||
return ledgerv1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED
|
||||
@@ -414,7 +415,7 @@ func describableToProto(desc pmodel.Describable) *describablev1.Describable {
|
||||
}
|
||||
|
||||
func (s *Service) ensureSettlementAccount(ctx context.Context, orgRef bson.ObjectID, currency string) (*pmodel.LedgerAccount, error) {
|
||||
return s.ensureRoleAccount(ctx, orgRef, currency, pmodel.AccountRoleSettlement)
|
||||
return s.ensureRoleAccount(ctx, orgRef, currency, account_role.AccountRoleSettlement)
|
||||
}
|
||||
|
||||
func generateAccountCode(accountType pmodel.LedgerAccountType, currency string, id bson.ObjectID) string {
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/tech/sendico/ledger/storage"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
pmodel "github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/model/account_role"
|
||||
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||
)
|
||||
|
||||
@@ -21,14 +22,14 @@ type accountStoreStub struct {
|
||||
created []*pmodel.LedgerAccount
|
||||
existing *pmodel.LedgerAccount
|
||||
existingErr error
|
||||
existingByRole map[pmodel.AccountRole]*pmodel.LedgerAccount
|
||||
existingByRole map[account_role.AccountRole]*pmodel.LedgerAccount
|
||||
defaultSettlement *pmodel.LedgerAccount
|
||||
defaultErr error
|
||||
createErrs []error
|
||||
}
|
||||
|
||||
func (s *accountStoreStub) Create(_ context.Context, account *pmodel.LedgerAccount) error {
|
||||
if account.Role == pmodel.AccountRoleSettlement {
|
||||
if account.Role == account_role.AccountRoleSettlement {
|
||||
if s.createErrSettlement != nil {
|
||||
return s.createErrSettlement
|
||||
}
|
||||
@@ -66,7 +67,7 @@ func (s *accountStoreStub) Get(context.Context, bson.ObjectID) (*pmodel.LedgerAc
|
||||
return nil, storage.ErrAccountNotFound
|
||||
}
|
||||
|
||||
func (s *accountStoreStub) GetByRole(_ context.Context, orgRef bson.ObjectID, currency string, role pmodel.AccountRole) (*pmodel.LedgerAccount, error) {
|
||||
func (s *accountStoreStub) GetByRole(_ context.Context, orgRef bson.ObjectID, currency string, role account_role.AccountRole) (*pmodel.LedgerAccount, error) {
|
||||
if s.existingByRole != nil {
|
||||
if acc, ok := s.existingByRole[role]; ok {
|
||||
return acc, nil
|
||||
@@ -190,14 +191,14 @@ func TestCreateAccountResponder_AutoCreatesSettlementAccount(t *testing.T) {
|
||||
var settlement *pmodel.LedgerAccount
|
||||
var operating *pmodel.LedgerAccount
|
||||
|
||||
roles := make(map[pmodel.AccountRole]bool)
|
||||
roles := make(map[account_role.AccountRole]bool)
|
||||
for _, acc := range accountStore.created {
|
||||
roles[acc.Role] = true
|
||||
|
||||
if acc.Role == pmodel.AccountRoleSettlement {
|
||||
if acc.Role == account_role.AccountRoleSettlement {
|
||||
settlement = acc
|
||||
}
|
||||
if acc.Role == pmodel.AccountRoleOperating {
|
||||
if acc.Role == account_role.AccountRoleOperating {
|
||||
operating = acc
|
||||
}
|
||||
|
||||
@@ -230,7 +231,7 @@ func TestCreateAccountResponder_AutoCreatesSettlementAccount(t *testing.T) {
|
||||
require.Equal(t, pmodel.LedgerAccountTypeAsset, settlement.AccountType)
|
||||
require.Equal(t, "USD", settlement.Currency)
|
||||
require.False(t, settlement.AllowNegative)
|
||||
require.Equal(t, pmodel.AccountRoleSettlement, settlement.Role)
|
||||
require.Equal(t, account_role.AccountRoleSettlement, settlement.Role)
|
||||
require.Equal(t, "true", settlement.Metadata["system"])
|
||||
}
|
||||
|
||||
@@ -265,7 +266,7 @@ func TestCreateAccountResponder_RetriesOnConflict(t *testing.T) {
|
||||
|
||||
var createdFee *pmodel.LedgerAccount
|
||||
for _, acc := range accountStore.created {
|
||||
if acc.Role == pmodel.AccountRoleFee {
|
||||
if acc.Role == account_role.AccountRoleFee {
|
||||
createdFee = acc
|
||||
break
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ func (c *connectorAdapter) SubmitOperation(ctx context.Context, req *connectorv1
|
||||
if err != nil {
|
||||
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(mapErrorCode(err), err.Error(), op, accountID)}}, nil
|
||||
}
|
||||
return &connectorv1.SubmitOperationResponse{Receipt: ledgerReceipt(resp.GetJournalEntryRef(), connectorv1.OperationStatus_CONFIRMED)}, nil
|
||||
return &connectorv1.SubmitOperationResponse{Receipt: ledgerReceipt(resp.GetJournalEntryRef(), connectorv1.OperationStatus_OPERATION_SUCCESS)}, nil
|
||||
case connectorv1.OperationType_DEBIT:
|
||||
accountID := operationAccountID(op.GetFrom())
|
||||
if accountID == "" && op.GetFromRole() == accountrolev1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED {
|
||||
@@ -280,7 +280,7 @@ func (c *connectorAdapter) SubmitOperation(ctx context.Context, req *connectorv1
|
||||
if err != nil {
|
||||
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(mapErrorCode(err), err.Error(), op, accountID)}}, nil
|
||||
}
|
||||
return &connectorv1.SubmitOperationResponse{Receipt: ledgerReceipt(resp.GetJournalEntryRef(), connectorv1.OperationStatus_CONFIRMED)}, nil
|
||||
return &connectorv1.SubmitOperationResponse{Receipt: ledgerReceipt(resp.GetJournalEntryRef(), connectorv1.OperationStatus_OPERATION_SUCCESS)}, nil
|
||||
case connectorv1.OperationType_TRANSFER:
|
||||
fromID := operationAccountID(op.GetFrom())
|
||||
toID := operationAccountID(op.GetTo())
|
||||
@@ -306,7 +306,7 @@ func (c *connectorAdapter) SubmitOperation(ctx context.Context, req *connectorv1
|
||||
if err != nil {
|
||||
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(mapErrorCode(err), err.Error(), op, "")}}, nil
|
||||
}
|
||||
return &connectorv1.SubmitOperationResponse{Receipt: ledgerReceipt(resp.GetJournalEntryRef(), connectorv1.OperationStatus_CONFIRMED)}, nil
|
||||
return &connectorv1.SubmitOperationResponse{Receipt: ledgerReceipt(resp.GetJournalEntryRef(), connectorv1.OperationStatus_OPERATION_SUCCESS)}, nil
|
||||
case connectorv1.OperationType_FX:
|
||||
fromID := operationAccountID(op.GetFrom())
|
||||
toID := operationAccountID(op.GetTo())
|
||||
@@ -333,7 +333,7 @@ func (c *connectorAdapter) SubmitOperation(ctx context.Context, req *connectorv1
|
||||
if err != nil {
|
||||
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(mapErrorCode(err), err.Error(), op, "")}}, nil
|
||||
}
|
||||
return &connectorv1.SubmitOperationResponse{Receipt: ledgerReceipt(resp.GetJournalEntryRef(), connectorv1.OperationStatus_CONFIRMED)}, nil
|
||||
return &connectorv1.SubmitOperationResponse{Receipt: ledgerReceipt(resp.GetJournalEntryRef(), connectorv1.OperationStatus_OPERATION_SUCCESS)}, nil
|
||||
default:
|
||||
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_UNSUPPORTED_OPERATION, "submit_operation: unsupported operation type", op, "")}}, nil
|
||||
}
|
||||
@@ -535,7 +535,7 @@ func ledgerEntryToOperation(entry *ledgerv1.JournalEntryResponse) *connectorv1.O
|
||||
op := &connectorv1.Operation{
|
||||
OperationId: strings.TrimSpace(entry.GetEntryRef()),
|
||||
Type: ledgerEntryType(entry.GetEntryType()),
|
||||
Status: connectorv1.OperationStatus_CONFIRMED,
|
||||
Status: connectorv1.OperationStatus_OPERATION_SUCCESS,
|
||||
CreatedAt: entry.GetEventTime(),
|
||||
UpdatedAt: entry.GetEventTime(),
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/tech/sendico/pkg/db/transaction"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
pmodel "github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/model/account_role"
|
||||
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
||||
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
@@ -86,7 +87,7 @@ func (s *memoryAccountsStore) GetByAccountCode(context.Context, bson.ObjectID, s
|
||||
return nil, merrors.NotImplemented("get by code")
|
||||
}
|
||||
|
||||
func (s *memoryAccountsStore) GetByRole(context.Context, bson.ObjectID, string, pmodel.AccountRole) (*pmodel.LedgerAccount, error) {
|
||||
func (s *memoryAccountsStore) GetByRole(context.Context, bson.ObjectID, string, account_role.AccountRole) (*pmodel.LedgerAccount, error) {
|
||||
return nil, merrors.NotImplemented("get by role")
|
||||
}
|
||||
|
||||
@@ -242,7 +243,7 @@ func newTestService() (*Service, *memoryRepository) {
|
||||
return svc, repo
|
||||
}
|
||||
|
||||
func newOrgAccount(orgRef bson.ObjectID, currency string, role pmodel.AccountRole) *pmodel.LedgerAccount {
|
||||
func newOrgAccount(orgRef bson.ObjectID, currency string, role account_role.AccountRole) *pmodel.LedgerAccount {
|
||||
account := &pmodel.LedgerAccount{
|
||||
AccountCode: "test:" + strings.ToLower(currency) + ":" + bson.NewObjectID().Hex(),
|
||||
Currency: currency,
|
||||
@@ -290,7 +291,7 @@ func TestExternalCreditAndDebit(t *testing.T) {
|
||||
require.NoError(t, svc.ensureSystemAccounts(ctx))
|
||||
|
||||
orgRef := bson.NewObjectID()
|
||||
pending := newOrgAccount(orgRef, "USD", pmodel.AccountRolePending)
|
||||
pending := newOrgAccount(orgRef, "USD", account_role.AccountRolePending)
|
||||
require.NoError(t, repo.accounts.Create(ctx, pending))
|
||||
|
||||
creditResp, err := svc.PostExternalCreditWithCharges(ctx, &ledgerv1.PostCreditRequest{
|
||||
@@ -334,7 +335,7 @@ func TestExternalCreditCurrencyMismatch(t *testing.T) {
|
||||
require.NoError(t, svc.ensureSystemAccounts(ctx))
|
||||
|
||||
orgRef := bson.NewObjectID()
|
||||
pending := newOrgAccount(orgRef, "USD", pmodel.AccountRolePending)
|
||||
pending := newOrgAccount(orgRef, "USD", account_role.AccountRolePending)
|
||||
require.NoError(t, repo.accounts.Create(ctx, pending))
|
||||
|
||||
source, err := repo.accounts.GetSystemAccount(ctx, pmodel.SystemAccountPurposeExternalSource, "USD")
|
||||
@@ -396,8 +397,8 @@ func TestExternalFlowInvariant(t *testing.T) {
|
||||
require.NoError(t, svc.ensureSystemAccounts(ctx))
|
||||
|
||||
orgRef := bson.NewObjectID()
|
||||
pending := newOrgAccount(orgRef, "USD", pmodel.AccountRolePending)
|
||||
transit := newOrgAccount(orgRef, "USD", pmodel.AccountRoleTransit)
|
||||
pending := newOrgAccount(orgRef, "USD", account_role.AccountRolePending)
|
||||
transit := newOrgAccount(orgRef, "USD", account_role.AccountRoleTransit)
|
||||
require.NoError(t, repo.accounts.Create(ctx, pending))
|
||||
require.NoError(t, repo.accounts.Create(ctx, transit))
|
||||
|
||||
@@ -442,8 +443,8 @@ func TestExternalInvariantRandomSequence(t *testing.T) {
|
||||
require.NoError(t, svc.ensureSystemAccounts(ctx))
|
||||
|
||||
orgRef := bson.NewObjectID()
|
||||
pending := newOrgAccount(orgRef, "USD", pmodel.AccountRolePending)
|
||||
transit := newOrgAccount(orgRef, "USD", pmodel.AccountRoleTransit)
|
||||
pending := newOrgAccount(orgRef, "USD", account_role.AccountRolePending)
|
||||
transit := newOrgAccount(orgRef, "USD", account_role.AccountRoleTransit)
|
||||
require.NoError(t, repo.accounts.Create(ctx, pending))
|
||||
require.NoError(t, repo.accounts.Create(ctx, transit))
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/tech/sendico/pkg/api/routers/gsresponse"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
pmodel "github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/model/account_role"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
@@ -28,7 +29,7 @@ func (s *Service) postCreditResponder(_ context.Context, req *ledgerv1.PostCredi
|
||||
if req.OrganizationRef == "" {
|
||||
return nil, merrors.InvalidArgument("organization_ref is required")
|
||||
}
|
||||
roleModel := pmodel.AccountRole("")
|
||||
roleModel := account_role.AccountRole("")
|
||||
if req.Role != ledgerv1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED {
|
||||
var err error
|
||||
roleModel, err = protoAccountRoleToModel(req.Role)
|
||||
@@ -36,7 +37,7 @@ func (s *Service) postCreditResponder(_ context.Context, req *ledgerv1.PostCredi
|
||||
return nil, err
|
||||
}
|
||||
} else if strings.TrimSpace(req.LedgerAccountRef) == "" {
|
||||
roleModel = pmodel.AccountRoleOperating
|
||||
roleModel = account_role.AccountRoleOperating
|
||||
}
|
||||
if strings.TrimSpace(req.LedgerAccountRef) == "" && roleModel == "" {
|
||||
return nil, merrors.InvalidArgument("ledger_account_ref or role is required")
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/tech/sendico/pkg/api/routers/gsresponse"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
pmodel "github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/model/account_role"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
@@ -26,7 +27,7 @@ func (s *Service) postDebitResponder(_ context.Context, req *ledgerv1.PostDebitR
|
||||
if req.OrganizationRef == "" {
|
||||
return nil, merrors.InvalidArgument("organization_ref is required")
|
||||
}
|
||||
roleModel := pmodel.AccountRole("")
|
||||
roleModel := account_role.AccountRole("")
|
||||
if req.Role != ledgerv1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED {
|
||||
var err error
|
||||
roleModel, err = protoAccountRoleToModel(req.Role)
|
||||
@@ -34,7 +35,7 @@ func (s *Service) postDebitResponder(_ context.Context, req *ledgerv1.PostDebitR
|
||||
return nil, err
|
||||
}
|
||||
} else if strings.TrimSpace(req.LedgerAccountRef) == "" {
|
||||
roleModel = pmodel.AccountRoleOperating
|
||||
roleModel = account_role.AccountRoleOperating
|
||||
}
|
||||
if strings.TrimSpace(req.LedgerAccountRef) == "" && roleModel == "" {
|
||||
return nil, merrors.InvalidArgument("ledger_account_ref or role is required")
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/tech/sendico/pkg/api/routers/gsresponse"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
pmodel "github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/model/account_role"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
@@ -25,7 +26,7 @@ func (s *Service) postExternalCreditResponder(_ context.Context, req *ledgerv1.P
|
||||
if req.OrganizationRef == "" {
|
||||
return nil, merrors.InvalidArgument("organization_ref is required")
|
||||
}
|
||||
roleModel := pmodel.AccountRole("")
|
||||
roleModel := account_role.AccountRole("")
|
||||
if req.Role != ledgerv1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED {
|
||||
var err error
|
||||
roleModel, err = protoAccountRoleToModel(req.Role)
|
||||
@@ -258,7 +259,7 @@ func (s *Service) postExternalDebitResponder(_ context.Context, req *ledgerv1.Po
|
||||
if req.OrganizationRef == "" {
|
||||
return nil, merrors.InvalidArgument("organization_ref is required")
|
||||
}
|
||||
roleModel := pmodel.AccountRole("")
|
||||
roleModel := account_role.AccountRole("")
|
||||
if req.Role != ledgerv1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED {
|
||||
var err error
|
||||
roleModel, err = protoAccountRoleToModel(req.Role)
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/tech/sendico/ledger/storage/model"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
pmodel "github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/model/account_role"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
"go.uber.org/zap"
|
||||
@@ -33,7 +34,7 @@ type outboxJournalPayload struct {
|
||||
Lines []outboxLinePayload `json:"lines"`
|
||||
}
|
||||
|
||||
func validateAccountRole(account *pmodel.LedgerAccount, expected pmodel.AccountRole, label string) error {
|
||||
func validateAccountRole(account *pmodel.LedgerAccount, expected account_role.AccountRole, label string) error {
|
||||
if expected == "" {
|
||||
return nil
|
||||
}
|
||||
@@ -47,7 +48,7 @@ func validateAccountRole(account *pmodel.LedgerAccount, expected pmodel.AccountR
|
||||
// If accountRefStr is non-empty, it fetches by ID and optionally asserts the role.
|
||||
// If accountRefStr is empty and role is set, it resolves via GetByRole(orgRef, currency, role).
|
||||
// Returns the account and its ObjectID, or an error.
|
||||
func (s *Service) resolveAccount(ctx context.Context, accountRefStr string, role pmodel.AccountRole, orgRef bson.ObjectID, currency, label string) (*pmodel.LedgerAccount, bson.ObjectID, error) {
|
||||
func (s *Service) resolveAccount(ctx context.Context, accountRefStr string, role account_role.AccountRole, orgRef bson.ObjectID, currency, label string) (*pmodel.LedgerAccount, bson.ObjectID, error) {
|
||||
if accountRefStr != "" {
|
||||
ref, err := parseObjectID(accountRefStr)
|
||||
if err != nil {
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/tech/sendico/ledger/storage/model"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
pmodel "github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/model/account_role"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
@@ -52,7 +53,7 @@ func (s *stubAccountsStore) Get(ctx context.Context, accountRef bson.ObjectID) (
|
||||
func (s *stubAccountsStore) GetByAccountCode(context.Context, bson.ObjectID, string, string) (*pmodel.LedgerAccount, error) {
|
||||
return nil, merrors.NotImplemented("get by code")
|
||||
}
|
||||
func (s *stubAccountsStore) GetByRole(context.Context, bson.ObjectID, string, pmodel.AccountRole) (*pmodel.LedgerAccount, error) {
|
||||
func (s *stubAccountsStore) GetByRole(context.Context, bson.ObjectID, string, account_role.AccountRole) (*pmodel.LedgerAccount, error) {
|
||||
return nil, merrors.NotImplemented("get by role")
|
||||
}
|
||||
func (s *stubAccountsStore) GetSystemAccount(context.Context, pmodel.SystemAccountPurpose, string) (*pmodel.LedgerAccount, error) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/tech/sendico/pkg/api/routers/gsresponse"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
pmodel "github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/model/account_role"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
@@ -27,7 +28,7 @@ func (s *Service) transferResponder(_ context.Context, req *ledgerv1.TransferReq
|
||||
if req.OrganizationRef == "" {
|
||||
return nil, merrors.InvalidArgument("organization_ref is required")
|
||||
}
|
||||
fromRoleModel := pmodel.AccountRole("")
|
||||
fromRoleModel := account_role.AccountRole("")
|
||||
if req.FromRole != ledgerv1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED {
|
||||
var err error
|
||||
fromRoleModel, err = protoAccountRoleToModel(req.FromRole)
|
||||
@@ -35,9 +36,9 @@ func (s *Service) transferResponder(_ context.Context, req *ledgerv1.TransferReq
|
||||
return nil, err
|
||||
}
|
||||
} else if strings.TrimSpace(req.FromLedgerAccountRef) == "" {
|
||||
fromRoleModel = pmodel.AccountRoleOperating
|
||||
fromRoleModel = account_role.AccountRoleOperating
|
||||
}
|
||||
toRoleModel := pmodel.AccountRole("")
|
||||
toRoleModel := account_role.AccountRole("")
|
||||
if req.ToRole != ledgerv1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED {
|
||||
var err error
|
||||
toRoleModel, err = protoAccountRoleToModel(req.ToRole)
|
||||
@@ -45,7 +46,7 @@ func (s *Service) transferResponder(_ context.Context, req *ledgerv1.TransferReq
|
||||
return nil, err
|
||||
}
|
||||
} else if strings.TrimSpace(req.ToLedgerAccountRef) == "" {
|
||||
toRoleModel = pmodel.AccountRoleOperating
|
||||
toRoleModel = account_role.AccountRoleOperating
|
||||
}
|
||||
if strings.TrimSpace(req.FromLedgerAccountRef) == "" && fromRoleModel == "" {
|
||||
return nil, merrors.InvalidArgument("from_ledger_account_ref or from_role is required")
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/tech/sendico/ledger/storage"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
pmodel "github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/model/account_role"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
@@ -40,7 +41,7 @@ func (s *systemAccountsStoreStub) GetByAccountCode(context.Context, bson.ObjectI
|
||||
return nil, merrors.NotImplemented("get by code")
|
||||
}
|
||||
|
||||
func (s *systemAccountsStoreStub) GetByRole(context.Context, bson.ObjectID, string, pmodel.AccountRole) (*pmodel.LedgerAccount, error) {
|
||||
func (s *systemAccountsStoreStub) GetByRole(context.Context, bson.ObjectID, string, account_role.AccountRole) (*pmodel.LedgerAccount, error) {
|
||||
return nil, merrors.NotImplemented("get by role")
|
||||
}
|
||||
|
||||
@@ -98,7 +99,7 @@ func TestEnsureSystemAccounts_CreatesAndCaches(t *testing.T) {
|
||||
require.Equal(t, pmodel.LedgerAccountScopeSystem, acc.Scope)
|
||||
require.True(t, acc.AllowNegative)
|
||||
require.Nil(t, acc.OrganizationRef)
|
||||
require.Equal(t, pmodel.AccountRole(""), acc.Role)
|
||||
require.Equal(t, account_role.AccountRole(""), acc.Role)
|
||||
require.NotNil(t, acc.SystemPurpose)
|
||||
require.Equal(t, pmodel.LedgerAccountTypeAsset, acc.AccountType)
|
||||
require.Equal(t, pmodel.LedgerAccountStatusActive, acc.Status)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/tech/sendico/ledger/storage"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
pmodel "github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/model/account_role"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
"go.uber.org/zap"
|
||||
@@ -15,15 +16,15 @@ import (
|
||||
|
||||
const LedgerTopologyVersion = 1
|
||||
|
||||
var RequiredRolesV1 = []pmodel.AccountRole{
|
||||
pmodel.AccountRoleOperating,
|
||||
pmodel.AccountRoleHold,
|
||||
pmodel.AccountRolePending,
|
||||
pmodel.AccountRoleTransit,
|
||||
pmodel.AccountRoleSettlement,
|
||||
var RequiredRolesV1 = []account_role.AccountRole{
|
||||
account_role.AccountRoleOperating,
|
||||
account_role.AccountRoleHold,
|
||||
account_role.AccountRolePending,
|
||||
account_role.AccountRoleTransit,
|
||||
account_role.AccountRoleSettlement,
|
||||
}
|
||||
|
||||
func isRequiredTopologyRole(role pmodel.AccountRole) bool {
|
||||
func isRequiredTopologyRole(role account_role.AccountRole) bool {
|
||||
for _, required := range RequiredRolesV1 {
|
||||
if role == required {
|
||||
return true
|
||||
@@ -52,7 +53,7 @@ func (s *Service) ensureLedgerTopology(ctx context.Context, orgRef bson.ObjectID
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) ensureRoleAccount(ctx context.Context, orgRef bson.ObjectID, currency string, role pmodel.AccountRole) (*pmodel.LedgerAccount, error) {
|
||||
func (s *Service) ensureRoleAccount(ctx context.Context, orgRef bson.ObjectID, currency string, role account_role.AccountRole) (*pmodel.LedgerAccount, error) {
|
||||
if s.storage == nil || s.storage.Accounts() == nil {
|
||||
return nil, errStorageNotInitialized
|
||||
}
|
||||
@@ -104,7 +105,7 @@ func (s *Service) ensureRoleAccount(ctx context.Context, orgRef bson.ObjectID, c
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func newSystemAccount(orgRef bson.ObjectID, currency string, role pmodel.AccountRole) *pmodel.LedgerAccount {
|
||||
func newSystemAccount(orgRef bson.ObjectID, currency string, role account_role.AccountRole) *pmodel.LedgerAccount {
|
||||
ref := bson.NewObjectID()
|
||||
account := &pmodel.LedgerAccount{
|
||||
AccountCode: generateAccountCode(pmodel.LedgerAccountTypeAsset, currency, ref),
|
||||
|
||||
Reference in New Issue
Block a user