133 lines
4.8 KiB
Go
133 lines
4.8 KiB
Go
package model
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/tech/sendico/pkg/db/storable"
|
|
"github.com/tech/sendico/pkg/merrors"
|
|
"github.com/tech/sendico/pkg/model/account_role"
|
|
"github.com/tech/sendico/pkg/mservice"
|
|
"go.mongodb.org/mongo-driver/v2/bson"
|
|
)
|
|
|
|
// AccountType defines the category of account (asset, liability, revenue, expense).
|
|
type LedgerAccountType string
|
|
|
|
const (
|
|
LedgerAccountTypeAsset LedgerAccountType = "asset"
|
|
LedgerAccountTypeLiability LedgerAccountType = "liability"
|
|
LedgerAccountTypeRevenue LedgerAccountType = "revenue"
|
|
LedgerAccountTypeExpense LedgerAccountType = "expense"
|
|
)
|
|
|
|
// AccountStatus tracks the operational state of an account.
|
|
type LedgerAccountStatus string
|
|
|
|
const (
|
|
LedgerAccountStatusActive LedgerAccountStatus = "active"
|
|
LedgerAccountStatusFrozen LedgerAccountStatus = "frozen"
|
|
LedgerAccountStatusClosed LedgerAccountStatus = "closed"
|
|
)
|
|
|
|
type LedgerAccountScope string
|
|
|
|
const (
|
|
LedgerAccountScopeOrganization LedgerAccountScope = "organization"
|
|
LedgerAccountScopeSystem LedgerAccountScope = "system"
|
|
)
|
|
|
|
type SystemAccountPurpose string
|
|
|
|
const (
|
|
SystemAccountPurposeExternalSource SystemAccountPurpose = "external_source"
|
|
SystemAccountPurposeExternalSink SystemAccountPurpose = "external_sink"
|
|
)
|
|
|
|
// Account represents a ledger account that holds balances for a specific currency.
|
|
type LedgerAccount struct {
|
|
storable.Base `bson:",inline" json:",inline"`
|
|
Describable `bson:",inline" json:",inline"`
|
|
|
|
// Scope defines whether the account belongs to an organization
|
|
// or is a system-level ledger account used for internal accounting.
|
|
Scope LedgerAccountScope `bson:"scope" json:"scope"`
|
|
|
|
// SystemPurpose specifies the role of a system-scoped account
|
|
// (e.g., external source or sink of funds). Must be set for system accounts
|
|
// and must be nil for organization accounts.
|
|
SystemPurpose *SystemAccountPurpose `bson:"systemPurpose,omitempty" json:"systemPurpose,omitempty"`
|
|
|
|
// OrganizationRef links the account to an organization.
|
|
// Must be set for organization accounts and nil for system accounts.
|
|
OrganizationRef *bson.ObjectID `bson:"organizationRef,omitempty" json:"organizationRef,omitempty"`
|
|
|
|
// Role defines the functional purpose of the account within an organization
|
|
// (e.g., pending, operating, settlement, hold, etc.).
|
|
// Must be set for organization accounts and omitted for system accounts.
|
|
Role account_role.AccountRole `bson:"role,omitempty" json:"role,omitempty"`
|
|
|
|
// AccountCode is a logical classification code of the account
|
|
// (e.g., "asset:cash:usd") used for reporting and grouping.
|
|
AccountCode string `bson:"accountCode" json:"accountCode"`
|
|
|
|
// Currency is the ISO 4217 currency code the account operates in.
|
|
Currency string `bson:"currency" json:"currency"`
|
|
|
|
// AccountType defines the accounting category of the account
|
|
// (asset, liability, revenue, expense).
|
|
AccountType LedgerAccountType `bson:"accountType" json:"accountType"`
|
|
|
|
// Status represents the operational state of the account.
|
|
Status LedgerAccountStatus `bson:"status" json:"status"`
|
|
|
|
// AllowNegative defines whether the account is allowed to have
|
|
// a negative balance (used for system control accounts).
|
|
AllowNegative bool `bson:"allowNegative" json:"allowNegative"`
|
|
|
|
// OwnerRef optionally links the account to a specific owner entity
|
|
// (e.g., user or sub-entity within the organization).
|
|
OwnerRef *bson.ObjectID `bson:"ownerRef,omitempty" json:"ownerRef,omitempty"`
|
|
|
|
// Metadata holds additional arbitrary key-value attributes.
|
|
Metadata map[string]string `bson:"metadata,omitempty" json:"metadata,omitempty"`
|
|
}
|
|
|
|
// Collection implements storable.Storable.
|
|
func (*LedgerAccount) Collection() string {
|
|
return mservice.LedgerAccounts
|
|
}
|
|
|
|
// Validate enforces scope-specific invariants for ledger accounts.
|
|
func (a *LedgerAccount) Validate() error {
|
|
if a == nil {
|
|
return merrors.InvalidArgument("ledger account is required")
|
|
}
|
|
|
|
switch a.Scope {
|
|
case LedgerAccountScopeOrganization:
|
|
if a.OrganizationRef == nil || a.OrganizationRef.IsZero() {
|
|
return merrors.InvalidArgument("organization_ref is required for organization accounts")
|
|
}
|
|
if strings.TrimSpace(string(a.Role)) == "" {
|
|
return merrors.InvalidArgument("role is required for organization accounts")
|
|
}
|
|
if a.SystemPurpose != nil {
|
|
return merrors.InvalidArgument("system_purpose must be nil for organization accounts")
|
|
}
|
|
case LedgerAccountScopeSystem:
|
|
if a.OrganizationRef != nil && !a.OrganizationRef.IsZero() {
|
|
return merrors.InvalidArgument("organization_ref must be nil for system accounts")
|
|
}
|
|
if strings.TrimSpace(string(a.Role)) != "" {
|
|
return merrors.InvalidArgument("role must be empty for system accounts")
|
|
}
|
|
if a.SystemPurpose == nil {
|
|
return merrors.InvalidArgument("system_purpose is required for system accounts")
|
|
}
|
|
default:
|
|
return merrors.InvalidArgument("scope is required")
|
|
}
|
|
|
|
return nil
|
|
}
|