ledger accounts improvement
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/tech/sendico/pkg/ledgerconv"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/payments/rail"
|
||||
describablev1 "github.com/tech/sendico/pkg/proto/common/describable/v1"
|
||||
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
||||
@@ -38,6 +39,9 @@ type Client interface {
|
||||
TransferInternal(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error)
|
||||
ApplyFXWithCharges(ctx context.Context, req *ledgerv1.FXRequest) (*ledgerv1.PostResponse, error)
|
||||
|
||||
BlockAccount(ctx context.Context, req *ledgerv1.BlockAccountRequest) (*ledgerv1.BlockAccountResponse, error)
|
||||
UnblockAccount(ctx context.Context, req *ledgerv1.UnblockAccountRequest) (*ledgerv1.UnblockAccountResponse, error)
|
||||
|
||||
GetBalance(ctx context.Context, req *ledgerv1.GetBalanceRequest) (*ledgerv1.BalanceResponse, error)
|
||||
GetJournalEntry(ctx context.Context, req *ledgerv1.GetEntryRequest) (*ledgerv1.JournalEntryResponse, error)
|
||||
GetStatement(ctx context.Context, req *ledgerv1.GetStatementRequest) (*ledgerv1.StatementResponse, error)
|
||||
@@ -50,6 +54,7 @@ type grpcConnectorClient interface {
|
||||
GetAccount(ctx context.Context, in *connectorv1.GetAccountRequest, opts ...grpc.CallOption) (*connectorv1.GetAccountResponse, error)
|
||||
ListAccounts(ctx context.Context, in *connectorv1.ListAccountsRequest, opts ...grpc.CallOption) (*connectorv1.ListAccountsResponse, error)
|
||||
GetBalance(ctx context.Context, in *connectorv1.GetBalanceRequest, opts ...grpc.CallOption) (*connectorv1.GetBalanceResponse, error)
|
||||
UpdateAccountState(ctx context.Context, in *connectorv1.UpdateAccountStateRequest, opts ...grpc.CallOption) (*connectorv1.UpdateAccountStateResponse, error)
|
||||
SubmitOperation(ctx context.Context, in *connectorv1.SubmitOperationRequest, opts ...grpc.CallOption) (*connectorv1.SubmitOperationResponse, error)
|
||||
GetOperation(ctx context.Context, in *connectorv1.GetOperationRequest, opts ...grpc.CallOption) (*connectorv1.GetOperationResponse, error)
|
||||
ListOperations(ctx context.Context, in *connectorv1.ListOperationsRequest, opts ...grpc.CallOption) (*connectorv1.ListOperationsResponse, error)
|
||||
@@ -141,10 +146,17 @@ func (c *ledgerClient) CreateTransaction(ctx context.Context, tx rail.LedgerTx)
|
||||
|
||||
description := strings.TrimSpace(tx.Description)
|
||||
metadata := ledgerTxMetadata(tx.Metadata, tx)
|
||||
extraParams := map[string]interface{}{}
|
||||
if op := strings.TrimSpace(tx.Operation); op != "" {
|
||||
extraParams["operation"] = op
|
||||
}
|
||||
if len(extraParams) == 0 {
|
||||
extraParams = nil
|
||||
}
|
||||
|
||||
switch {
|
||||
case isLedgerRail(tx.FromRail) && !isLedgerRail(tx.ToRail):
|
||||
resp, err := c.PostDebitWithCharges(ctx, &ledgerv1.PostDebitRequest{
|
||||
resp, err := c.submitLedgerOperationWithExtras(ctx, connectorv1.OperationType_DEBIT, accountRef, "", money, &ledgerv1.PostDebitRequest{
|
||||
IdempotencyKey: strings.TrimSpace(tx.IdempotencyKey),
|
||||
OrganizationRef: orgRef,
|
||||
LedgerAccountRef: accountRef,
|
||||
@@ -153,13 +165,13 @@ func (c *ledgerClient) CreateTransaction(ctx context.Context, tx rail.LedgerTx)
|
||||
Charges: tx.Charges,
|
||||
Metadata: metadata,
|
||||
ContraLedgerAccountRef: strings.TrimSpace(tx.ContraLedgerAccountRef),
|
||||
})
|
||||
}, extraParams)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(resp.GetJournalEntryRef()), nil
|
||||
case isLedgerRail(tx.ToRail) && !isLedgerRail(tx.FromRail):
|
||||
resp, err := c.PostCreditWithCharges(ctx, &ledgerv1.PostCreditRequest{
|
||||
resp, err := c.submitLedgerOperationWithExtras(ctx, connectorv1.OperationType_CREDIT, "", accountRef, money, &ledgerv1.PostCreditRequest{
|
||||
IdempotencyKey: strings.TrimSpace(tx.IdempotencyKey),
|
||||
OrganizationRef: orgRef,
|
||||
LedgerAccountRef: accountRef,
|
||||
@@ -168,7 +180,7 @@ func (c *ledgerClient) CreateTransaction(ctx context.Context, tx rail.LedgerTx)
|
||||
Charges: tx.Charges,
|
||||
Metadata: metadata,
|
||||
ContraLedgerAccountRef: strings.TrimSpace(tx.ContraLedgerAccountRef),
|
||||
})
|
||||
}, extraParams)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -196,7 +208,9 @@ func (c *ledgerClient) CreateAccount(ctx context.Context, req *ledgerv1.CreateAc
|
||||
"account_type": req.GetAccountType().String(),
|
||||
"status": req.GetStatus().String(),
|
||||
"allow_negative": req.GetAllowNegative(),
|
||||
"is_settlement": req.GetIsSettlement(),
|
||||
}
|
||||
if role := req.GetRole(); role != ledgerv1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED {
|
||||
params["role"] = role.String()
|
||||
}
|
||||
label := ""
|
||||
if desc := req.GetDescribable(); desc != nil {
|
||||
@@ -232,7 +246,10 @@ func (c *ledgerClient) ListAccounts(ctx context.Context, req *ledgerv1.ListAccou
|
||||
if req == nil || strings.TrimSpace(req.GetOrganizationRef()) == "" {
|
||||
return nil, merrors.InvalidArgument("ledger: organization_ref is required")
|
||||
}
|
||||
resp, err := c.client.ListAccounts(ctx, &connectorv1.ListAccountsRequest{OrganizationRef: strings.TrimSpace(req.GetOrganizationRef())})
|
||||
resp, err := c.client.ListAccounts(ctx, &connectorv1.ListAccountsRequest{
|
||||
OrganizationRef: strings.TrimSpace(req.GetOrganizationRef()),
|
||||
OwnerRefFilter: req.GetOwnerRefFilter(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -294,6 +311,48 @@ func (c *ledgerClient) ApplyFXWithCharges(ctx context.Context, req *ledgerv1.FXR
|
||||
return &ledgerv1.PostResponse{JournalEntryRef: resp.GetReceipt().GetOperationId(), EntryType: ledgerv1.EntryType_ENTRY_FX}, nil
|
||||
}
|
||||
|
||||
func (c *ledgerClient) BlockAccount(ctx context.Context, req *ledgerv1.BlockAccountRequest) (*ledgerv1.BlockAccountResponse, error) {
|
||||
ctx, cancel := c.callContext(ctx)
|
||||
defer cancel()
|
||||
if req == nil || strings.TrimSpace(req.GetLedgerAccountRef()) == "" {
|
||||
return nil, merrors.InvalidArgument("ledger: ledger_account_ref is required")
|
||||
}
|
||||
sourceRole := model.ToProto(accountRoleFromLedgerProto(req.GetRole()))
|
||||
resp, err := c.client.UpdateAccountState(ctx, &connectorv1.UpdateAccountStateRequest{
|
||||
AccountRef: &connectorv1.AccountRef{ConnectorId: ledgerConnectorID, AccountId: strings.TrimSpace(req.GetLedgerAccountRef())},
|
||||
TargetState: connectorv1.AccountState_ACCOUNT_SUSPENDED,
|
||||
SourceRole: sourceRole,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.GetError() != nil {
|
||||
return nil, connectorError(resp.GetError())
|
||||
}
|
||||
return &ledgerv1.BlockAccountResponse{Account: ledgerAccountFromConnector(resp.GetAccount())}, nil
|
||||
}
|
||||
|
||||
func (c *ledgerClient) UnblockAccount(ctx context.Context, req *ledgerv1.UnblockAccountRequest) (*ledgerv1.UnblockAccountResponse, error) {
|
||||
ctx, cancel := c.callContext(ctx)
|
||||
defer cancel()
|
||||
if req == nil || strings.TrimSpace(req.GetLedgerAccountRef()) == "" {
|
||||
return nil, merrors.InvalidArgument("ledger: ledger_account_ref is required")
|
||||
}
|
||||
sourceRole := model.ToProto(accountRoleFromLedgerProto(req.GetRole()))
|
||||
resp, err := c.client.UpdateAccountState(ctx, &connectorv1.UpdateAccountStateRequest{
|
||||
AccountRef: &connectorv1.AccountRef{ConnectorId: ledgerConnectorID, AccountId: strings.TrimSpace(req.GetLedgerAccountRef())},
|
||||
TargetState: connectorv1.AccountState_ACCOUNT_ACTIVE,
|
||||
SourceRole: sourceRole,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.GetError() != nil {
|
||||
return nil, connectorError(resp.GetError())
|
||||
}
|
||||
return &ledgerv1.UnblockAccountResponse{Account: ledgerAccountFromConnector(resp.GetAccount())}, nil
|
||||
}
|
||||
|
||||
func (c *ledgerClient) GetBalance(ctx context.Context, req *ledgerv1.GetBalanceRequest) (*ledgerv1.BalanceResponse, error) {
|
||||
ctx, cancel := c.callContext(ctx)
|
||||
defer cancel()
|
||||
@@ -353,6 +412,10 @@ func (c *ledgerClient) GetStatement(ctx context.Context, req *ledgerv1.GetStatem
|
||||
}
|
||||
|
||||
func (c *ledgerClient) submitLedgerOperation(ctx context.Context, opType connectorv1.OperationType, fromRef, toRef string, money *moneyv1.Money, req interface{}) (*ledgerv1.PostResponse, error) {
|
||||
return c.submitLedgerOperationWithExtras(ctx, opType, fromRef, toRef, money, req, nil)
|
||||
}
|
||||
|
||||
func (c *ledgerClient) submitLedgerOperationWithExtras(ctx context.Context, opType connectorv1.OperationType, fromRef, toRef string, money *moneyv1.Money, req interface{}, extraParams map[string]interface{}) (*ledgerv1.PostResponse, error) {
|
||||
ctx, cancel := c.callContext(ctx)
|
||||
defer cancel()
|
||||
if money == nil {
|
||||
@@ -367,6 +430,8 @@ func (c *ledgerClient) submitLedgerOperation(ctx context.Context, opType connect
|
||||
charges []*ledgerv1.PostingLine
|
||||
eventTime *timestamppb.Timestamp
|
||||
contraRef string
|
||||
fromRole model.AccountRole
|
||||
toRole model.AccountRole
|
||||
)
|
||||
|
||||
switch r := req.(type) {
|
||||
@@ -378,6 +443,7 @@ func (c *ledgerClient) submitLedgerOperation(ctx context.Context, opType connect
|
||||
charges = r.GetCharges()
|
||||
eventTime = r.GetEventTime()
|
||||
contraRef = r.GetContraLedgerAccountRef()
|
||||
toRole = accountRoleFromLedgerProto(r.GetRole())
|
||||
case *ledgerv1.PostDebitRequest:
|
||||
idempotencyKey = r.GetIdempotencyKey()
|
||||
orgRef = r.GetOrganizationRef()
|
||||
@@ -386,6 +452,7 @@ func (c *ledgerClient) submitLedgerOperation(ctx context.Context, opType connect
|
||||
charges = r.GetCharges()
|
||||
eventTime = r.GetEventTime()
|
||||
contraRef = r.GetContraLedgerAccountRef()
|
||||
fromRole = accountRoleFromLedgerProto(r.GetRole())
|
||||
case *ledgerv1.TransferRequest:
|
||||
idempotencyKey = r.GetIdempotencyKey()
|
||||
orgRef = r.GetOrganizationRef()
|
||||
@@ -393,12 +460,19 @@ func (c *ledgerClient) submitLedgerOperation(ctx context.Context, opType connect
|
||||
metadata = r.GetMetadata()
|
||||
charges = r.GetCharges()
|
||||
eventTime = r.GetEventTime()
|
||||
fromRole = accountRoleFromLedgerProto(r.GetFromRole())
|
||||
toRole = accountRoleFromLedgerProto(r.GetToRole())
|
||||
}
|
||||
|
||||
params := ledgerOperationParams(orgRef, description, metadata, charges, eventTime)
|
||||
if contraRef != "" {
|
||||
params["contra_ledger_account_ref"] = strings.TrimSpace(contraRef)
|
||||
}
|
||||
if len(extraParams) > 0 {
|
||||
for key, value := range extraParams {
|
||||
params[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
op := &connectorv1.Operation{
|
||||
Type: opType,
|
||||
@@ -412,6 +486,12 @@ func (c *ledgerClient) submitLedgerOperation(ctx context.Context, opType connect
|
||||
if toRef != "" {
|
||||
op.To = accountParty(toRef)
|
||||
}
|
||||
if fromRole != "" {
|
||||
op.FromRole = model.ToProto(fromRole)
|
||||
}
|
||||
if toRole != "" {
|
||||
op.ToRole = model.ToProto(toRole)
|
||||
}
|
||||
|
||||
resp, err := c.client.SubmitOperation(ctx, &connectorv1.SubmitOperationRequest{Operation: op})
|
||||
if err != nil {
|
||||
@@ -423,6 +503,35 @@ func (c *ledgerClient) submitLedgerOperation(ctx context.Context, opType connect
|
||||
return &ledgerv1.PostResponse{JournalEntryRef: resp.GetReceipt().GetOperationId(), EntryType: entryTypeFromOperation(opType)}, nil
|
||||
}
|
||||
|
||||
func accountRoleFromLedgerProto(role ledgerv1.AccountRole) model.AccountRole {
|
||||
switch role {
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_OPERATING:
|
||||
return model.AccountRoleOperating
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_HOLD:
|
||||
return model.AccountRoleHold
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_TRANSIT:
|
||||
return model.AccountRoleTransit
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_SETTLEMENT:
|
||||
return model.AccountRoleSettlement
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_CLEARING:
|
||||
return model.AccountRoleClearing
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_PENDING:
|
||||
return model.AccountRolePending
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_RESERVE:
|
||||
return model.AccountRoleReserve
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_LIQUIDITY:
|
||||
return model.AccountRoleLiquidity
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_FEE:
|
||||
return model.AccountRoleFee
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_CHARGEBACK:
|
||||
return model.AccountRoleChargeback
|
||||
case ledgerv1.AccountRole_ACCOUNT_ROLE_ADJUSTMENT:
|
||||
return model.AccountRoleAdjustment
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func ledgerOperationParams(orgRef, description string, metadata map[string]string, charges []*ledgerv1.PostingLine, eventTime *timestamppb.Timestamp) map[string]interface{} {
|
||||
params := map[string]interface{}{
|
||||
"organization_ref": strings.TrimSpace(orgRef),
|
||||
@@ -482,9 +591,23 @@ func ledgerAccountFromConnector(account *connectorv1.Account) *ledgerv1.LedgerAc
|
||||
if v, ok := details["allow_negative"].(bool); ok {
|
||||
allowNegative = v
|
||||
}
|
||||
isSettlement := false
|
||||
if v, ok := details["is_settlement"].(bool); ok {
|
||||
isSettlement = v
|
||||
role := ledgerv1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED
|
||||
if v := strings.TrimSpace(fmt.Sprint(details["role"])); v != "" {
|
||||
if parsed, ok := ledgerconv.ParseAccountRole(v); ok {
|
||||
role = parsed
|
||||
}
|
||||
}
|
||||
if role == ledgerv1.AccountRole_ACCOUNT_ROLE_UNSPECIFIED {
|
||||
switch v := details["is_settlement"].(type) {
|
||||
case bool:
|
||||
if v {
|
||||
role = ledgerv1.AccountRole_ACCOUNT_ROLE_SETTLEMENT
|
||||
}
|
||||
case string:
|
||||
if strings.EqualFold(strings.TrimSpace(v), "true") {
|
||||
role = ledgerv1.AccountRole_ACCOUNT_ROLE_SETTLEMENT
|
||||
}
|
||||
}
|
||||
}
|
||||
accountCode := strings.TrimSpace(fmt.Sprint(details["account_code"]))
|
||||
accountID := ""
|
||||
@@ -515,7 +638,7 @@ func ledgerAccountFromConnector(account *connectorv1.Account) *ledgerv1.LedgerAc
|
||||
Currency: strings.TrimSpace(account.GetAsset()),
|
||||
Status: status,
|
||||
AllowNegative: allowNegative,
|
||||
IsSettlement: isSettlement,
|
||||
Role: role,
|
||||
CreatedAt: account.GetCreatedAt(),
|
||||
UpdatedAt: account.GetUpdatedAt(),
|
||||
Describable: describable,
|
||||
|
||||
@@ -32,6 +32,10 @@ func (s *stubConnector) GetBalance(context.Context, *connectorv1.GetBalanceReque
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *stubConnector) UpdateAccountState(context.Context, *connectorv1.UpdateAccountStateRequest, ...grpc.CallOption) (*connectorv1.UpdateAccountStateResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *stubConnector) SubmitOperation(ctx context.Context, req *connectorv1.SubmitOperationRequest, _ ...grpc.CallOption) (*connectorv1.SubmitOperationResponse, error) {
|
||||
if s.submitFn != nil {
|
||||
return s.submitFn(ctx, req)
|
||||
|
||||
@@ -21,6 +21,8 @@ type Fake struct {
|
||||
PostDebitWithChargesFn func(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error)
|
||||
TransferInternalFn func(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error)
|
||||
ApplyFXWithChargesFn func(ctx context.Context, req *ledgerv1.FXRequest) (*ledgerv1.PostResponse, error)
|
||||
BlockAccountFn func(ctx context.Context, req *ledgerv1.BlockAccountRequest) (*ledgerv1.BlockAccountResponse, error)
|
||||
UnblockAccountFn func(ctx context.Context, req *ledgerv1.UnblockAccountRequest) (*ledgerv1.UnblockAccountResponse, error)
|
||||
GetBalanceFn func(ctx context.Context, req *ledgerv1.GetBalanceRequest) (*ledgerv1.BalanceResponse, error)
|
||||
GetJournalEntryFn func(ctx context.Context, req *ledgerv1.GetEntryRequest) (*ledgerv1.JournalEntryResponse, error)
|
||||
GetStatementFn func(ctx context.Context, req *ledgerv1.GetStatementRequest) (*ledgerv1.StatementResponse, error)
|
||||
@@ -97,6 +99,20 @@ func (f *Fake) ApplyFXWithCharges(ctx context.Context, req *ledgerv1.FXRequest)
|
||||
return &ledgerv1.PostResponse{}, nil
|
||||
}
|
||||
|
||||
func (f *Fake) BlockAccount(ctx context.Context, req *ledgerv1.BlockAccountRequest) (*ledgerv1.BlockAccountResponse, error) {
|
||||
if f.BlockAccountFn != nil {
|
||||
return f.BlockAccountFn(ctx, req)
|
||||
}
|
||||
return &ledgerv1.BlockAccountResponse{}, nil
|
||||
}
|
||||
|
||||
func (f *Fake) UnblockAccount(ctx context.Context, req *ledgerv1.UnblockAccountRequest) (*ledgerv1.UnblockAccountResponse, error) {
|
||||
if f.UnblockAccountFn != nil {
|
||||
return f.UnblockAccountFn(ctx, req)
|
||||
}
|
||||
return &ledgerv1.UnblockAccountResponse{}, nil
|
||||
}
|
||||
|
||||
func (f *Fake) GetBalance(ctx context.Context, req *ledgerv1.GetBalanceRequest) (*ledgerv1.BalanceResponse, error) {
|
||||
if f.GetBalanceFn != nil {
|
||||
return f.GetBalanceFn(ctx, req)
|
||||
|
||||
Reference in New Issue
Block a user