Sign in

    moonbit-accounting

    Generate financial statements from normalized accounting entries.

    accounting
    financial-report
    ledger
    moonbit
    Download zip
    Author
    Version
    0.3.0
    License
    Apache-2.0
    Last updated
    2 months ago
    Downloads
    23

    No dependencies

    #moonbit-accounting

    Deterministic financial reporting for MoonBit applications. It accepts normalized entries and explicit mappings, then produces stable reports.

    ///|
    test "readme example" {
    let mappings = [AccountMapping::new("sales", "Revenue", IncomeStatement)]
    let entries = [Entry::new("sales", "2026-Q2", 0, 1200)]
    let report = generate(IncomeStatement, "2026-Q2", entries, mappings)
    assert_eq(report.total, 1200)
    assert_eq(report.line("Revenue"), Some(1200))
    }

    Amounts are integer minor units. Reports can be rendered as CSV, JSON, Markdown, or HTML. Validation, comparison periods, dimensions, budgets, consolidation, ratios, trends, anomaly detection, and account catalogues are available from the same dependency-free package, together with CSV import, journal validation, trial balances, reconciliation, and report packs.

    moon check --target all --deny-warn moon test --target all moon run cmd/main

    See README.md for architecture, CLI, benchmark, CI, and license details.

    Amount

    type Amount = Int

    A signed amount in the smallest currency unit (for example, cents).

    AccountClass

    pub enum AccountClass {
    Asset
    Liability
    Equity
    Revenue
    Expense
    } derive(Eq,
    Debug
    )

    A compact standard chart helps applications bootstrap mappings.

    AccountDefinition

    pub struct AccountDefinition {
    code : String
    name : String
    class : AccountClass
    normal_debit : Bool
    } derive(Eq,
    Debug
    )

    AccountDefinition::new

    fn AccountDefinition::new(code : String, name : String, class : AccountClass, normal_debit : Bool) -> AccountDefinition

    AccountMapping

    pub struct AccountMapping {
    account : String
    line : String
    kind : StatementKind
    factor : Int
    } derive(Eq,
    Debug
    )

    AccountMapping::new

    fn AccountMapping::new(account : String, line : String, kind : StatementKind, factor? : Int) -> AccountMapping

    AgingBucket

    pub struct AgingBucket {
    label : String
    amount : Int
    entries : Int
    } derive(Eq,
    Debug
    )

    Receivable aging uses quarter periods and integer minor units.

    Anomaly

    pub struct Anomaly {
    kind : AnomalyKind
    account : String
    period : String
    amount : Int
    detail : String
    } derive(Eq,
    Debug
    )

    AnomalyKind

    pub(all) enum AnomalyKind {
    LargeDebit
    LargeCredit
    UnmappedAccount
    InvalidPeriod
    DuplicateEntry
    } derive(Eq,
    Debug
    )

    BatchValidation

    pub enum BatchValidation {
    BalancedBatch
    UnbalancedBatch(Int)
    EmptyBatch
    } derive(Eq,
    Debug
    )

    BenchmarkResult

    pub struct BenchmarkResult {
    entries : Int
    mappings : Int
    lines : Int
    total : Int
    elapsed_ms : Int
    } derive(Eq,
    Debug
    )

    Deterministic fixture generation keeps benchmark comparisons meaningful.

    BudgetLine

    pub struct BudgetLine {
    line : String
    amount : Int
    } derive(Eq,
    Debug
    )

    BudgetLine::new

    fn BudgetLine::new(line : String, amount : Int) -> BudgetLine

    BudgetVariance

    pub struct BudgetVariance {
    line : String
    budget : Int
    actual : Int
    variance : Int
    favorable : Bool
    } derive(Eq,
    Debug
    )

    CashFlowKind

    pub(all) enum CashFlowKind {
    Operating
    Investing
    Financing
    Unclassified
    } derive(Eq,
    Debug
    )

    CashFlowLine

    pub struct CashFlowLine {
    kind : CashFlowKind
    amount : Int
    entries : Int
    } derive(Eq,
    Debug
    )

    CashFlowRule

    pub struct CashFlowRule {
    account : String
    kind : CashFlowKind
    factor : Int
    } derive(Eq,
    Debug
    )

    CashFlowRule::new

    fn CashFlowRule::new(account : String, kind : CashFlowKind, factor? : Int) -> CashFlowRule

    CashRecord

    pub struct CashRecord {
    reference : String
    amount : Int
    } derive(Eq,
    Debug
    )

    Reconciliation compares normalized balances with an external statement.

    CashRecord::new

    fn CashRecord::new(reference : String, amount : Int) -> CashRecord

    CsvImportResult

    pub struct CsvImportResult {
    entries : Array[Entry]
    errors : Array[String]
    } derive(Eq,
    Debug
    )

    A small dependency-free importer for normalized CSV exchange files.

    Currency

    pub struct Currency {
    code : String
    name : String
    minor_units : Int
    } derive(Eq,
    Debug
    )

    ISO-like currency metadata used by callers that need explicit scale.

    Currency::new

    fn Currency::new(code : String, name : String, minor_units : Int) -> Currency

    DatasetIssue

    pub enum DatasetIssue {
    InvalidEntry(Int, EntryValidation)
    UnmappedEntry(Int, String)
    DuplicateEntry(Int, Int)
    } derive(Eq,
    Debug
    )

    Dataset validation combines structural and mapping checks for importers.

    DimensionLine

    pub struct DimensionLine {
    dimension : String
    amount : Int
    entries : Int
    } derive(Eq,
    Debug
    )

    DimensionTotal

    pub struct DimensionTotal {
    dimension : String
    amount : Int
    entries : Int
    } derive(Eq,
    Debug
    )

    Deterministic scalar metrics for dashboards and reports.

    EliminationRule

    pub struct EliminationRule {
    debit_account : String
    credit_account : String
    } derive(Eq,
    Debug
    )

    A rule identifies reciprocal intercompany balances to remove.

    EliminationRule::new

    fn EliminationRule::new(debit_account : String, credit_account : String) -> EliminationRule

    Entity

    pub struct Entity {
    name : String
    entries : Array[Entry]
    } derive(Eq,
    Debug
    )

    Entity::new

    fn Entity::new(name : String, entries : Array[Entry]) -> Entity

    Entry

    pub struct Entry {
    account : String
    period : String
    debit : Int
    credit : Int
    cash : Bool
    dimension : String
    } derive(Eq,
    Debug
    )

    Entry::new

    fn Entry::new(account : String, period : String, debit : Int, credit : Int, cash? : Bool) -> Entry

    Entry::new_with_dimension

    fn Entry::new_with_dimension(account : String, period : String, debit : Int, credit : Int, dimension : String, cash? : Bool) -> Entry

    EntryValidation

    pub enum EntryValidation {
    ValidEntry
    EmptyAccount
    InvalidEntryPeriod(String)
    NegativeAmount
    UnbalancedEntry
    } derive(Eq,
    Debug
    )

    Validation errors are explicit so importers can reject bad source data.

    ExchangeRate

    pub struct ExchangeRate {
    from : String
    to : String
    numerator : Int
    denominator : Int
    } derive(Eq,
    Debug
    )

    Explicit exchange rates keep conversion policy outside report generation.

    ExchangeRate::new

    fn ExchangeRate::new(from : String, to : String, numerator : Int, denominator : Int) -> ExchangeRate

    ForecastPoint

    pub struct ForecastPoint {
    period : Int
    amount : Int
    source_average : Int
    } derive(Eq,
    Debug
    )

    Forecasts are intentionally transparent run-rate projections.

    HealthScore

    pub struct HealthScore {
    liquidity : Int
    profitability : Int
    leverage : Int
    overall : Int
    } derive(Eq,
    Debug
    )

    JournalEntry

    pub struct JournalEntry {
    id : String
    period : String
    description : String
    lines : Array[JournalLine]
    } derive(Eq,
    Debug
    )

    JournalEntry::add_line

    fn JournalEntry::add_line(self : JournalEntry, line : JournalLine) -> Unit

    JournalEntry::new

    fn JournalEntry::new(id : String, period : String, description : String) -> JournalEntry

    JournalLine

    pub struct JournalLine {
    account : String
    debit : Int
    credit : Int
    } derive(Eq,
    Debug
    )

    Journal entries preserve source-document boundaries before normalization.

    JournalLine::new

    fn JournalLine::new(account : String, debit : Int, credit : Int) -> JournalLine

    JournalValidation

    pub enum JournalValidation {
    BalancedJournal
    EmptyJournal
    InvalidJournalPeriod
    UnbalancedJournal(Int)
    InvalidJournalLine(String)
    } derive(Eq,
    Debug
    )

    LineItem

    pub struct LineItem {
    line : String
    amount : Int
    entries : Int
    } derive(Eq,
    Debug
    )

    MappingValidation

    pub enum MappingValidation {
    ValidMappings
    DuplicateAccount(String)
    } derive(Eq,
    Debug
    )

    PeriodResult

    pub enum PeriodResult {
    ValidPeriod(String)
    InvalidPeriod(String)
    } derive(Eq,
    Debug
    )

    RatioSet

    pub struct RatioSet {
    current_ratio : Int?
    quick_ratio : Int?
    gross_margin : Int?
    operating_margin : Int?
    debt_ratio : Int?
    return_on_assets : Int?
    } derive(Eq,
    Debug
    )

    Financial analysis primitives operate on integer minor units.

    ReconciliationResult

    pub struct ReconciliationResult {
    matched : Int
    outstanding_ledger : Int
    outstanding_statement : Int
    ledger_total : Int
    statement_total : Int
    difference : Int
    } derive(Eq,
    Debug
    )

    Report

    pub struct Report {
    kind : StatementKind
    period : String
    lines : Array[LineItem]
    total : Int
    } derive(Eq,
    Debug
    )

    Report::line

    fn Report::line(self : Report, name : String) -> Int?

    Report::to_csv

    fn Report::to_csv(self : Report) -> String

    Report::to_html

    fn Report::to_html(self : Report) -> String

    Report::to_json

    fn Report::to_json(self : Report) -> String

    Report::to_markdown

    fn Report::to_markdown(self : Report) -> String

    ReportComparison

    pub struct ReportComparison {
    current : Report
    prior : Report
    total_variance : Int
    } derive(Eq,
    Debug
    )

    ReportPack

    pub struct ReportPack {
    period : String
    reports : Array[Report]
    } derive(Eq,
    Debug
    )

    A report pack groups the statements normally shown together.

    ReportPack::to_markdown

    fn ReportPack::to_markdown(self : ReportPack) -> String

    StatementKind

    pub(all) enum StatementKind {
    BalanceSheet
    IncomeStatement
    CashFlowStatement
    Management
    } derive(Eq,
    Debug
    )

    TaxRule

    pub struct TaxRule {
    account : String
    treatment : TaxTreatment
    } derive(Eq,
    Debug
    )

    TaxRule::new

    fn TaxRule::new(account : String, treatment : TaxTreatment) -> TaxRule

    TaxSummary

    pub struct TaxSummary {
    period : String
    revenue : Int
    deductions : Int
    base : Int
    rate : Int
    tax : Int
    } derive(Eq,
    Debug
    )

    TaxTreatment

    pub(all) enum TaxTreatment {
    TaxableRevenue
    DeductibleExpense
    Exempt
    } derive(Eq,
    Debug
    )

    Tax rules express how normalized accounts contribute to a tax base.

    TrendPoint

    pub struct TrendPoint {
    period : String
    amount : Int
    change : Int
    direction : String
    } derive(Eq,
    Debug
    )

    TrialBalance

    pub struct TrialBalance {
    lines : Array[TrialBalanceLine]
    total_debit : Int
    total_credit : Int
    balanced : Bool
    } derive(Eq,
    Debug
    )

    TrialBalanceLine

    pub struct TrialBalanceLine {
    account : String
    debit : Int
    credit : Int
    balance : Int
    } derive(Eq,
    Debug
    )

    Variance

    pub struct Variance {
    line : String
    current : Int
    prior : Int
    absolute : Int
    percent : Int?
    } derive(Eq,
    Debug
    )

    account_balance

    fn account_balance(entries : Array[Entry], account : String, period? : String) -> Int

    age_receivables

    fn age_receivables(entries : Array[Entry], account : String, as_of : String) -> Array[AgingBucket]

    analyze_ratios

    fn analyze_ratios(assets : Int, current_assets : Int, inventory : Int, liabilities : Int, revenue : Int, cost : Int, operating_expense : Int, net_income : Int) -> RatioSet

    benchmark_fixture

    fn benchmark_fixture(size : Int) -> (Array[Entry], Array[AccountMapping])

    benchmark_summary

    fn benchmark_summary(result : BenchmarkResult) -> String

    budget_variance

    fn budget_variance(budget : Array[BudgetLine], actual : Array[BudgetLine]) -> Array[BudgetVariance]

    build_report_pack

    fn build_report_pack(period : String, entries : Array[Entry], mappings : Array[AccountMapping]) -> ReportPack

    burn_rate

    fn burn_rate(expenses : Array[Int], periods : Int) -> Int?

    cash_flow

    fn cash_flow(period : String, entries : Array[Entry], rules : Array[CashFlowRule]) -> Array[CashFlowLine]

    compare_period

    fn compare_period(left : String, right : String) -> Int

    compare_reports

    fn compare_reports(kind : StatementKind, current_period : String, prior_period : String, entries : Array[Entry], mappings : Array[AccountMapping]) -> ReportComparison

    compound_change

    fn compound_change(first : Int, last : Int, periods : Int) -> Int?

    consolidate

    fn consolidate(period : String, entities : Array[Entity], mappings : Array[AccountMapping]) -> Report

    consolidate_with_eliminations

    fn consolidate_with_eliminations(period : String, entities : Array[Entity], mappings : Array[AccountMapping], rules : Array[EliminationRule]) -> Report

    convert_amount

    fn convert_amount(amount : Int, from : Currency?, to : Currency?, rate : ExchangeRate) -> Int?

    convert_entries

    fn convert_entries(entries : Array[Entry], from : Currency?, to : Currency?, rate : ExchangeRate) -> Array[Entry]

    convert_entry

    fn convert_entry(entry : Entry, from : Currency?, to : Currency?, rate : ExchangeRate) -> Entry?

    convert_minor

    fn convert_minor(amount : Int, from : Currency, to : Currency, rate_numerator : Int, rate_denominator : Int) -> Int?

    currency

    fn currency(code : String) -> Currency?

    currency_codes

    fn currency_codes() -> Array[String]

    detect_invalid_periods

    fn detect_invalid_periods(entries : Array[Entry]) -> Array[Anomaly]

    detect_large_entries

    fn detect_large_entries(entries : Array[Entry], threshold : Int) -> Array[Anomaly]

    detect_unmapped

    fn detect_unmapped(entries : Array[Entry], mappings : Array[AccountMapping]) -> Array[Anomaly]

    entries_for_period

    fn entries_for_period(entries : Array[Entry], period : String) -> Array[Entry]

    escape_html

    fn escape_html(value : String) -> String

    Escape text for HTML contexts without relying on a runtime or DOM.

    find_trial_line

    fn find_trial_line(balance : TrialBalance, account : String) -> TrialBalanceLine?

    flatten_journal

    fn flatten_journal(journal : JournalEntry) -> Array[Entry]

    forecast_linear

    fn forecast_linear(history : Array[Int], periods : Int) -> Array[ForecastPoint]

    forecast_run_rate

    fn forecast_run_rate(history : Array[Int], periods : Int) -> Array[ForecastPoint]

    forecast_total

    fn forecast_total(points : Array[ForecastPoint]) -> Int

    generate

    fn generate(kind : StatementKind, period : String, entries : Array[Entry], mappings : Array[AccountMapping], comparison? : String) -> Report

    generate_by_dimension

    fn generate_by_dimension(kind : StatementKind, period : String, entries : Array[Entry], mappings : Array[AccountMapping]) -> Array[DimensionLine]

    generate_many

    fn generate_many(kind : StatementKind, periods : Array[String], entries : Array[Entry], mappings : Array[AccountMapping]) -> Array[Report]

    Build a stable report for each requested period.

    group_by_dimension

    fn group_by_dimension(entries : Array[Entry], period : String) -> Array[DimensionTotal]

    growth_percent

    fn growth_percent(previous : Int, current : Int) -> Int?

    health_score

    fn health_score(ratios : RatioSet) -> HealthScore

    import_entries_csv

    fn import_entries_csv(csv : String) -> CsvImportResult

    journal_totals

    fn journal_totals(journal : JournalEntry) -> (Int, Int)

    latest_report

    fn latest_report(reports : Array[Report]) -> Report?

    lowest_report

    fn lowest_report(reports : Array[Report]) -> Report?

    moving_average

    fn moving_average(values : Array[Int], window : Int) -> Array[Int]

    net_margin

    fn net_margin(revenue : Int, expenses : Int) -> Int?

    period_delta

    fn period_delta(reports : Array[Report]) -> Array[Int]

    period_distance

    fn period_distance(older : String, newer : String) -> Int

    period_totals

    fn period_totals(reports : Array[Report]) -> Array[(String, Int)]

    reconcile_cash

    fn reconcile_cash(ledger : Array[CashRecord], statement : Array[CashRecord]) -> ReconciliationResult

    reconciliation_is_balanced

    fn reconciliation_is_balanced(result : ReconciliationResult) -> Bool

    render_variances

    fn render_variances(variances : Array[Variance]) -> String

    report_document

    fn report_document(report : Report, title : String) -> String

    report_variances

    fn report_variances(current : Report, prior : Report) -> Array[Variance]

    run_benchmark

    fn run_benchmark(size : Int) -> BenchmarkResult

    safe_ratio

    fn safe_ratio(numerator : Int, denominator : Int) -> Int?

    standard_account

    fn standard_account(code : String) -> AccountDefinition?

    standard_chart

    fn standard_chart() -> Array[AccountDefinition]

    tax_amount

    fn tax_amount(base : Int, rate : Int) -> Int

    tax_summary

    fn tax_summary(entries : Array[Entry], period : String, rules : Array[TaxRule], rate~ : Int) -> TaxSummary

    trend_points

    fn trend_points(values : Array[(String, Int)]) -> Array[TrendPoint]

    trial_balance

    fn trial_balance(entries : Array[Entry], period? : String) -> TrialBalance

    valid_entries

    fn valid_entries(entries : Array[Entry], mappings : Array[AccountMapping]) -> Array[Entry]

    validate_batch

    fn validate_batch(entries : Array[Entry]) -> BatchValidation

    validate_dataset

    fn validate_dataset(entries : Array[Entry], mappings : Array[AccountMapping]) -> Array[DatasetIssue]

    validate_entry

    fn validate_entry(entry : Entry) -> EntryValidation

    validate_journal

    fn validate_journal(journal : JournalEntry) -> JournalValidation

    validate_mappings

    fn validate_mappings(mappings : Array[AccountMapping]) -> MappingValidation

    validate_period

    fn validate_period(period : String) -> PeriodResult

    variance

    fn variance(line : String, current : Int, prior : Int) -> Variance

    weighted_average

    fn weighted_average(values : Array[(Int, Int)]) -> Int?

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io