From 4d133f1964ac2c8a3072812528f8610546148888 Mon Sep 17 00:00:00 2001 From: francy51 Date: Fri, 24 Apr 2026 21:45:38 -0400 Subject: [PATCH] Add detailed cash flow statement line items --- MosaicIQ/src-tauri/src/agent/panel_context.rs | 191 +++++- .../src-tauri/src/terminal/sec_edgar/facts.rs | 554 +++++++++++++++++- MosaicIQ/src-tauri/src/terminal/types.rs | 42 +- .../src/components/Panels/CashFlowPanel.tsx | 303 +++++++++- MosaicIQ/src/lib/terminalResearchNote.ts | 22 +- MosaicIQ/src/types/financial.ts | 42 +- 6 files changed, 1114 insertions(+), 40 deletions(-) diff --git a/MosaicIQ/src-tauri/src/agent/panel_context.rs b/MosaicIQ/src-tauri/src/agent/panel_context.rs index 733ccd0..8a1ee52 100644 --- a/MosaicIQ/src-tauri/src/agent/panel_context.rs +++ b/MosaicIQ/src-tauri/src/agent/panel_context.rs @@ -1,4 +1,4 @@ -use serde_json::{json, Value}; +use serde_json::{json, Map, Value}; use rig::completion::Message; @@ -304,21 +304,142 @@ fn compact_statement_period(period: &StatementPeriod) -> Value { } fn compact_cash_flow_period(period: &CashFlowPeriod) -> Value { - json!({ - "label": truncate_text(&period.label), - "fiscalYear": period.fiscal_year, - "fiscalPeriod": period.fiscal_period, - "periodStart": period.period_start, - "periodEnd": period.period_end, - "filedDate": period.filed_date, - "form": period.form, - "operatingCashFlow": period.operating_cash_flow, - "investingCashFlow": period.investing_cash_flow, - "financingCashFlow": period.financing_cash_flow, - "capex": period.capex, - "freeCashFlow": period.free_cash_flow, - "endingCash": period.ending_cash, - }) + let mut value = Map::new(); + value.insert("label".to_string(), json!(truncate_text(&period.label))); + value.insert("fiscalYear".to_string(), json!(&period.fiscal_year)); + value.insert("fiscalPeriod".to_string(), json!(&period.fiscal_period)); + value.insert("periodStart".to_string(), json!(&period.period_start)); + value.insert("periodEnd".to_string(), json!(&period.period_end)); + value.insert("filedDate".to_string(), json!(&period.filed_date)); + value.insert("form".to_string(), json!(&period.form)); + value.insert("netIncome".to_string(), json!(period.net_income)); + value.insert( + "depreciationAndAmortization".to_string(), + json!(period.depreciation_and_amortization), + ); + value.insert( + "stockBasedCompensation".to_string(), + json!(period.stock_based_compensation), + ); + value.insert( + "deferredIncomeTaxes".to_string(), + json!(period.deferred_income_taxes), + ); + value.insert( + "impairmentCharges".to_string(), + json!(period.impairment_charges), + ); + value.insert( + "lossGainOnSaleOfAssets".to_string(), + json!(period.loss_gain_on_sale_of_assets), + ); + value.insert( + "otherNonCashItems".to_string(), + json!(period.other_non_cash_items), + ); + value.insert( + "accountsReceivable".to_string(), + json!(period.accounts_receivable), + ); + value.insert("inventory".to_string(), json!(period.inventory)); + value.insert( + "prepaidExpensesAndOtherCurrentAssets".to_string(), + json!(period.prepaid_expenses_and_other_current_assets), + ); + value.insert( + "accountsPayable".to_string(), + json!(period.accounts_payable), + ); + value.insert( + "accruedExpenses".to_string(), + json!(period.accrued_expenses), + ); + value.insert( + "deferredRevenue".to_string(), + json!(period.deferred_revenue), + ); + value.insert( + "otherWorkingCapitalChanges".to_string(), + json!(period.other_working_capital_changes), + ); + value.insert( + "changeInOperatingAssets".to_string(), + json!(period.change_in_operating_assets), + ); + value.insert( + "changeInOperatingLiabilities".to_string(), + json!(period.change_in_operating_liabilities), + ); + value.insert( + "otherOperatingAdjustments".to_string(), + json!(period.other_operating_adjustments), + ); + value.insert( + "operatingCashFlow".to_string(), + json!(period.operating_cash_flow), + ); + value.insert("capex".to_string(), json!(period.capex)); + value.insert( + "proceedsFromSaleOfPpe".to_string(), + json!(period.proceeds_from_sale_of_ppe), + ); + value.insert( + "purchasesOfInvestments".to_string(), + json!(period.purchases_of_investments), + ); + value.insert( + "salesOfInvestments".to_string(), + json!(period.sales_of_investments), + ); + value.insert("acquisitions".to_string(), json!(period.acquisitions)); + value.insert( + "otherInvestingActivities".to_string(), + json!(period.other_investing_activities), + ); + value.insert( + "investingCashFlow".to_string(), + json!(period.investing_cash_flow), + ); + value.insert("debtIssuance".to_string(), json!(period.debt_issuance)); + value.insert("debtRepayment".to_string(), json!(period.debt_repayment)); + value.insert("equityIssuance".to_string(), json!(period.equity_issuance)); + value.insert( + "stockRepurchases".to_string(), + json!(period.stock_repurchases), + ); + value.insert("dividendsPaid".to_string(), json!(period.dividends_paid)); + value.insert( + "financeLeaseObligations".to_string(), + json!(period.finance_lease_obligations), + ); + value.insert( + "otherFinancingActivities".to_string(), + json!(period.other_financing_activities), + ); + value.insert( + "financingCashFlow".to_string(), + json!(period.financing_cash_flow), + ); + value.insert("freeCashFlow".to_string(), json!(period.free_cash_flow)); + value.insert( + "exchangeRateEffects".to_string(), + json!(period.exchange_rate_effects), + ); + value.insert( + "netIncreaseDecreaseCash".to_string(), + json!(period.net_increase_decrease_cash), + ); + value.insert("beginningCash".to_string(), json!(period.beginning_cash)); + value.insert("endingCash".to_string(), json!(period.ending_cash)); + value.insert( + "cashPaidInterest".to_string(), + json!(period.cash_paid_interest), + ); + value.insert( + "cashPaidIncomeTaxes".to_string(), + json!(period.cash_paid_income_taxes), + ); + Value::Object(value) } fn compact_dividend_event(event: &DividendEvent) -> Value { @@ -794,12 +915,46 @@ mod tests { period_end: "2025-12-31".to_string(), filed_date: "2026-01-31".to_string(), form: "10-K".to_string(), + net_income: Some(30.0), + depreciation_and_amortization: Some(15.0), + stock_based_compensation: Some(5.0), + deferred_income_taxes: Some(2.0), + impairment_charges: None, + loss_gain_on_sale_of_assets: None, + other_non_cash_items: Some(1.0), + accounts_receivable: Some(-4.0), + inventory: Some(-2.0), + prepaid_expenses_and_other_current_assets: Some(-1.0), + accounts_payable: Some(3.0), + accrued_expenses: Some(2.0), + deferred_revenue: Some(1.0), + other_working_capital_changes: None, + change_in_operating_assets: Some(-8.0), + change_in_operating_liabilities: Some(3.0), + other_operating_adjustments: None, operating_cash_flow: Some(100.0), - investing_cash_flow: Some(-50.0), - financing_cash_flow: Some(-25.0), capex: Some(-10.0), + proceeds_from_sale_of_ppe: Some(1.0), + purchases_of_investments: Some(-40.0), + sales_of_investments: Some(20.0), + acquisitions: None, + other_investing_activities: None, + investing_cash_flow: Some(-50.0), + debt_issuance: Some(50.0), + debt_repayment: Some(-30.0), + equity_issuance: Some(10.0), + stock_repurchases: Some(-15.0), + dividends_paid: Some(-20.0), + finance_lease_obligations: Some(-5.0), + other_financing_activities: None, + financing_cash_flow: Some(-25.0), free_cash_flow: Some(90.0), + exchange_rate_effects: None, + net_increase_decrease_cash: Some(5.0), + beginning_cash: Some(15.0), ending_cash: Some(20.0), + cash_paid_interest: Some(4.0), + cash_paid_income_taxes: Some(6.0), } } diff --git a/MosaicIQ/src-tauri/src/terminal/sec_edgar/facts.rs b/MosaicIQ/src-tauri/src/terminal/sec_edgar/facts.rs index 53c841c..0e7de86 100644 --- a/MosaicIQ/src-tauri/src/terminal/sec_edgar/facts.rs +++ b/MosaicIQ/src-tauri/src/terminal/sec_edgar/facts.rs @@ -150,6 +150,375 @@ pub(crate) const CAPEX_CONCEPTS: &[ConceptCandidate] = &[ UnitFamily::Currency, ), ]; +pub(crate) const NET_INCOME_CF_CONCEPTS: &[ConceptCandidate] = &[ + candidate("us-gaap", "NetIncomeLoss", UnitFamily::Currency), + candidate("ifrs-full", "ProfitLoss", UnitFamily::Currency), +]; +pub(crate) const DEPRECIATION_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "DepreciationDepletionAndAmortization", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "DepreciationAndAmortization", + UnitFamily::Currency, + ), + candidate( + "ifrs-full", + "DepreciationAndAmortisation", + UnitFamily::Currency, + ), +]; +pub(crate) const STOCK_BASED_COMPENSATION_CONCEPTS: &[ConceptCandidate] = &[ + candidate("us-gaap", "ShareBasedCompensation", UnitFamily::Currency), + candidate("us-gaap", "StockCompensationExpense", UnitFamily::Currency), +]; +pub(crate) const DEFERRED_INCOME_TAXES_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "DeferredIncomeTaxExpenseBenefit", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "DeferredIncomeTaxesAndTaxCredits", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "IncreaseDecreaseInDeferredIncomeTaxes", + UnitFamily::Currency, + ), +]; +pub(crate) const IMPAIRMENT_CHARGES_CONCEPTS: &[ConceptCandidate] = &[ + candidate("us-gaap", "AssetImpairmentCharges", UnitFamily::Currency), + candidate("us-gaap", "GoodwillImpairmentLosses", UnitFamily::Currency), + candidate( + "us-gaap", + "ImpairmentOfGoodwillAndIndefiniteLivedIntangibleAssets", + UnitFamily::Currency, + ), +]; +pub(crate) const LOSS_GAIN_ON_SALE_OF_ASSETS_CONCEPTS: &[ConceptCandidate] = &[ + candidate("us-gaap", "LossGainOnSaleOfAssets", UnitFamily::Currency), + candidate( + "us-gaap", + "GainLossOnSaleOfPropertyPlantEquipment", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "GainLossOnDispositionOfAssets", + UnitFamily::Currency, + ), +]; +pub(crate) const OTHER_NON_CASH_ITEMS_CONCEPTS: &[ConceptCandidate] = &[ + candidate("us-gaap", "OtherNoncashIncomeExpense", UnitFamily::Currency), + candidate("us-gaap", "OtherNoncashExpense", UnitFamily::Currency), + candidate( + "us-gaap", + "OtherNoncashItemsIncludedInNetIncomeLoss", + UnitFamily::Currency, + ), +]; +pub(crate) const ACCOUNTS_RECEIVABLE_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "IncreaseDecreaseInAccountsReceivable", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "ChangesInAccountReceivables", + UnitFamily::Currency, + ), +]; +pub(crate) const INVENTORY_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "IncreaseDecreaseInInventories", + UnitFamily::Currency, + ), + candidate("us-gaap", "ChangesInInventories", UnitFamily::Currency), +]; +pub(crate) const PREPAIDS_AND_OTHER_CURRENT_ASSETS_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "IncreaseDecreaseInPrepaidDeferredExpenseAndOtherAssets", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "IncreaseDecreaseInPrepaidExpenseAndOtherAssets", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "IncreaseDecreaseInOtherCurrentAssets", + UnitFamily::Currency, + ), +]; +pub(crate) const ACCOUNTS_PAYABLE_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "IncreaseDecreaseInAccountsPayable", + UnitFamily::Currency, + ), + candidate("us-gaap", "ChangesInAccountPayables", UnitFamily::Currency), +]; +pub(crate) const ACCRUED_EXPENSES_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "IncreaseDecreaseInAccruedLiabilities", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "IncreaseDecreaseInAccruedExpenses", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "IncreaseDecreaseInAccruedIncomeTaxesPayable", + UnitFamily::Currency, + ), +]; +pub(crate) const DEFERRED_REVENUE_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "IncreaseDecreaseInContractWithCustomerLiability", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "IncreaseDecreaseInDeferredRevenue", + UnitFamily::Currency, + ), +]; +pub(crate) const OTHER_WORKING_CAPITAL_CHANGES_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "IncreaseDecreaseInOperatingCapital", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "IncreaseDecreaseInOperatingAssetsAndLiabilitiesNet", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "OtherAssetsAndLiabilitiesNet", + UnitFamily::Currency, + ), +]; +pub(crate) const CHANGE_IN_OPERATING_ASSETS_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "IncreaseDecreaseInAccountsReceivable", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "ChangesInAccountReceivables", + UnitFamily::Currency, + ), +]; +pub(crate) const CHANGE_IN_OPERATING_LIABILITIES_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "IncreaseDecreaseInAccountsPayable", + UnitFamily::Currency, + ), + candidate("us-gaap", "ChangesInAccountPayables", UnitFamily::Currency), +]; +pub(crate) const OTHER_OPERATING_ADJUSTMENTS_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "OtherAdjustmentsToReconcileNetIncomeLossToCashProvidedByUsedInOperatingActivities", + UnitFamily::Currency, + ), + candidate("us-gaap", "OtherNoncashIncomeExpense", UnitFamily::Currency), +]; +pub(crate) const PROCEEDS_FROM_SALE_OF_PPE_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "ProceedsFromSaleOfPropertyPlantAndEquipment", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "ProceedsFromSaleOfPropertyPlantAndEquipmentAndIntangibleAssets", + UnitFamily::Currency, + ), +]; +pub(crate) const PURCHASES_OF_INVESTMENTS_CONCEPTS: &[ConceptCandidate] = &[ + candidate("us-gaap", "PaymentsForInvestments", UnitFamily::Currency), + candidate("us-gaap", "PurchaseOfInvestments", UnitFamily::Currency), + candidate( + "us-gaap", + "PaymentsToAcquireInvestments", + UnitFamily::Currency, + ), +]; +pub(crate) const SALES_OF_INVESTMENTS_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "ProceedsFromSaleOfInvestments", + UnitFamily::Currency, + ), + candidate("us-gaap", "MaturitiesOfInvestments", UnitFamily::Currency), +]; +pub(crate) const ACQUISITIONS_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "PaymentsToAcquireBusinessesNetOfCashAcquired", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "PaymentsToAcquireBusinesses", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "PaymentsMadeForAcquisitionOfBusiness", + UnitFamily::Currency, + ), +]; +pub(crate) const OTHER_INVESTING_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "OtherCashPaymentsForInvestingActivities", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "OtherInvestingActivitiesNetCashFlow", + UnitFamily::Currency, + ), +]; +pub(crate) const DEBT_ISSUANCE_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "ProceedsFromIssuanceOfDebt", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "ProceedsFromIssuanceOfLongTermDebt", + UnitFamily::Currency, + ), +]; +pub(crate) const DEBT_REPAYMENT_CONCEPTS: &[ConceptCandidate] = &[ + candidate("us-gaap", "PaymentsOfLongTermDebt", UnitFamily::Currency), + candidate("us-gaap", "RepaymentsOfLongTermDebt", UnitFamily::Currency), + candidate("us-gaap", "RepaymentsOfDebt", UnitFamily::Currency), +]; +pub(crate) const EQUITY_ISSUANCE_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "ProceedsFromIssuanceOfCommonStock", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "ProceedsFromStockOptionsExercised", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "ProceedsFromIssuanceOfSharesUnderIncentiveAndShareBasedCompensationPlansIncludingStockOptions", + UnitFamily::Currency, + ), +]; +pub(crate) const STOCK_REPURCHASES_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "PaymentsForRepurchaseOfCommonStock", + UnitFamily::Currency, + ), + candidate("us-gaap", "RepurchaseOfCommonStock", UnitFamily::Currency), + candidate( + "us-gaap", + "PaymentsForRepurchaseOfTreasuryStock", + UnitFamily::Currency, + ), +]; +pub(crate) const DIVIDENDS_PAID_CONCEPTS: &[ConceptCandidate] = &[ + candidate("us-gaap", "PaymentsOfDividends", UnitFamily::Currency), + candidate( + "us-gaap", + "PaymentsOfDividendsCommonStock", + UnitFamily::Currency, + ), +]; +pub(crate) const FINANCE_LEASE_OBLIGATIONS_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "PaymentsOfFinanceLeaseObligations", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "PaymentsOfCapitalLeaseObligations", + UnitFamily::Currency, + ), +]; +pub(crate) const OTHER_FINANCING_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "OtherCashPaymentsForFinancingActivities", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "OtherFinancingActivitiesNetCashFlow", + UnitFamily::Currency, + ), +]; +pub(crate) const EXCHANGE_RATE_EFFECTS_CONCEPTS: &[ConceptCandidate] = &[candidate( + "us-gaap", + "EffectOfExchangeRateOnCashAndCashEquivalents", + UnitFamily::Currency, +)]; +pub(crate) const NET_INCREASE_DECREASE_CASH_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalentsPeriodIncreaseDecreaseIncludingExchangeRateEffect", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "CashAndCashEquivalentsPeriodIncreaseDecrease", + UnitFamily::Currency, + ), + candidate( + "ifrs-full", + "IncreaseDecreaseInCashAndCashEquivalents", + UnitFamily::Currency, + ), +]; +pub(crate) const BEGINNING_CASH_CONCEPTS: &[ConceptCandidate] = &[ + candidate( + "us-gaap", + "CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalentsAtBeginningOfPeriod", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "CashAndCashEquivalentsAtBeginningOfPeriod", + UnitFamily::Currency, + ), + candidate( + "us-gaap", + "CashAndCashEquivalentsAtCarryingValue", + UnitFamily::Currency, + ), + candidate("ifrs-full", "CashAndCashEquivalents", UnitFamily::Currency), +]; pub(crate) const ENDING_CASH_CONCEPTS: &[ConceptCandidate] = &[ candidate( "us-gaap", @@ -163,6 +532,18 @@ pub(crate) const ENDING_CASH_CONCEPTS: &[ConceptCandidate] = &[ ), candidate("ifrs-full", "CashAndCashEquivalents", UnitFamily::Currency), ]; +pub(crate) const CASH_PAID_INTEREST_CONCEPTS: &[ConceptCandidate] = &[ + candidate("us-gaap", "InterestPaidNet", UnitFamily::Currency), + candidate("us-gaap", "InterestPaid", UnitFamily::Currency), + candidate("us-gaap", "CashPaidForInterest", UnitFamily::Currency), + candidate("ifrs-full", "InterestPaid", UnitFamily::Currency), +]; +pub(crate) const CASH_PAID_INCOME_TAXES_CONCEPTS: &[ConceptCandidate] = &[ + candidate("us-gaap", "IncomeTaxesPaidNet", UnitFamily::Currency), + candidate("us-gaap", "IncomeTaxesPaid", UnitFamily::Currency), + candidate("us-gaap", "CashPaidForIncomeTaxes", UnitFamily::Currency), + candidate("ifrs-full", "IncomeTaxesPaid", UnitFamily::Currency), +]; pub(crate) const DIVIDEND_PER_SHARE_CONCEPTS: &[ConceptCandidate] = &[ candidate( "us-gaap", @@ -309,15 +690,184 @@ pub(crate) fn build_cash_flow_periods( period_end: row.period_end.clone(), filed_date: row.filed_date.clone(), form: row.form.clone(), + net_income: value_for_period(facts, &row, NET_INCOME_CF_CONCEPTS, latest_xbrl), + depreciation_and_amortization: value_for_period( + facts, + &row, + DEPRECIATION_CONCEPTS, + latest_xbrl, + ), + stock_based_compensation: value_for_period( + facts, + &row, + STOCK_BASED_COMPENSATION_CONCEPTS, + latest_xbrl, + ), + deferred_income_taxes: value_for_period( + facts, + &row, + DEFERRED_INCOME_TAXES_CONCEPTS, + latest_xbrl, + ), + impairment_charges: value_for_period( + facts, + &row, + IMPAIRMENT_CHARGES_CONCEPTS, + latest_xbrl, + ), + loss_gain_on_sale_of_assets: value_for_period( + facts, + &row, + LOSS_GAIN_ON_SALE_OF_ASSETS_CONCEPTS, + latest_xbrl, + ), + other_non_cash_items: value_for_period( + facts, + &row, + OTHER_NON_CASH_ITEMS_CONCEPTS, + latest_xbrl, + ), + accounts_receivable: value_for_period( + facts, + &row, + ACCOUNTS_RECEIVABLE_CONCEPTS, + latest_xbrl, + ), + inventory: value_for_period(facts, &row, INVENTORY_CONCEPTS, latest_xbrl), + prepaid_expenses_and_other_current_assets: value_for_period( + facts, + &row, + PREPAIDS_AND_OTHER_CURRENT_ASSETS_CONCEPTS, + latest_xbrl, + ), + accounts_payable: value_for_period( + facts, + &row, + ACCOUNTS_PAYABLE_CONCEPTS, + latest_xbrl, + ), + accrued_expenses: value_for_period( + facts, + &row, + ACCRUED_EXPENSES_CONCEPTS, + latest_xbrl, + ), + deferred_revenue: value_for_period( + facts, + &row, + DEFERRED_REVENUE_CONCEPTS, + latest_xbrl, + ), + other_working_capital_changes: value_for_period( + facts, + &row, + OTHER_WORKING_CAPITAL_CHANGES_CONCEPTS, + latest_xbrl, + ), + change_in_operating_assets: value_for_period( + facts, + &row, + CHANGE_IN_OPERATING_ASSETS_CONCEPTS, + latest_xbrl, + ), + change_in_operating_liabilities: value_for_period( + facts, + &row, + CHANGE_IN_OPERATING_LIABILITIES_CONCEPTS, + latest_xbrl, + ), + other_operating_adjustments: value_for_period( + facts, + &row, + OTHER_OPERATING_ADJUSTMENTS_CONCEPTS, + latest_xbrl, + ), operating_cash_flow, - investing_cash_flow: value_for_period(facts, &row, CFI_CONCEPTS, latest_xbrl), - financing_cash_flow: value_for_period(facts, &row, CFF_CONCEPTS, latest_xbrl), capex, + proceeds_from_sale_of_ppe: value_for_period( + facts, + &row, + PROCEEDS_FROM_SALE_OF_PPE_CONCEPTS, + latest_xbrl, + ), + purchases_of_investments: value_for_period( + facts, + &row, + PURCHASES_OF_INVESTMENTS_CONCEPTS, + latest_xbrl, + ), + sales_of_investments: value_for_period( + facts, + &row, + SALES_OF_INVESTMENTS_CONCEPTS, + latest_xbrl, + ), + acquisitions: value_for_period(facts, &row, ACQUISITIONS_CONCEPTS, latest_xbrl), + other_investing_activities: value_for_period( + facts, + &row, + OTHER_INVESTING_CONCEPTS, + latest_xbrl, + ), + investing_cash_flow: value_for_period(facts, &row, CFI_CONCEPTS, latest_xbrl), + debt_issuance: value_for_period(facts, &row, DEBT_ISSUANCE_CONCEPTS, latest_xbrl), + debt_repayment: value_for_period(facts, &row, DEBT_REPAYMENT_CONCEPTS, latest_xbrl), + equity_issuance: value_for_period( + facts, + &row, + EQUITY_ISSUANCE_CONCEPTS, + latest_xbrl, + ), + stock_repurchases: value_for_period( + facts, + &row, + STOCK_REPURCHASES_CONCEPTS, + latest_xbrl, + ), + dividends_paid: value_for_period(facts, &row, DIVIDENDS_PAID_CONCEPTS, latest_xbrl), + finance_lease_obligations: value_for_period( + facts, + &row, + FINANCE_LEASE_OBLIGATIONS_CONCEPTS, + latest_xbrl, + ), + other_financing_activities: value_for_period( + facts, + &row, + OTHER_FINANCING_CONCEPTS, + latest_xbrl, + ), + financing_cash_flow: value_for_period(facts, &row, CFF_CONCEPTS, latest_xbrl), free_cash_flow: match (operating_cash_flow, capex) { (Some(cfo), Some(capex)) => Some(cfo - capex.abs()), _ => None, }, + exchange_rate_effects: value_for_period( + facts, + &row, + EXCHANGE_RATE_EFFECTS_CONCEPTS, + latest_xbrl, + ), + net_increase_decrease_cash: value_for_period( + facts, + &row, + NET_INCREASE_DECREASE_CASH_CONCEPTS, + latest_xbrl, + ), + beginning_cash: value_for_period(facts, &row, BEGINNING_CASH_CONCEPTS, latest_xbrl), ending_cash: value_for_period(facts, &row, ENDING_CASH_CONCEPTS, latest_xbrl), + cash_paid_interest: value_for_period( + facts, + &row, + CASH_PAID_INTEREST_CONCEPTS, + latest_xbrl, + ), + cash_paid_income_taxes: value_for_period( + facts, + &row, + CASH_PAID_INCOME_TAXES_CONCEPTS, + latest_xbrl, + ), } }) .collect() diff --git a/MosaicIQ/src-tauri/src/terminal/types.rs b/MosaicIQ/src-tauri/src/terminal/types.rs index f38dc6e..5ef08f1 100644 --- a/MosaicIQ/src-tauri/src/terminal/types.rs +++ b/MosaicIQ/src-tauri/src/terminal/types.rs @@ -297,12 +297,50 @@ pub struct CashFlowPeriod { pub period_end: String, pub filed_date: String, pub form: String, + // Operating Activities + pub net_income: Option, + pub depreciation_and_amortization: Option, + pub stock_based_compensation: Option, + pub deferred_income_taxes: Option, + pub impairment_charges: Option, + pub loss_gain_on_sale_of_assets: Option, + pub other_non_cash_items: Option, + pub accounts_receivable: Option, + pub inventory: Option, + pub prepaid_expenses_and_other_current_assets: Option, + pub accounts_payable: Option, + pub accrued_expenses: Option, + pub deferred_revenue: Option, + pub other_working_capital_changes: Option, + pub change_in_operating_assets: Option, + pub change_in_operating_liabilities: Option, + pub other_operating_adjustments: Option, pub operating_cash_flow: Option, - pub investing_cash_flow: Option, - pub financing_cash_flow: Option, + // Investing Activities pub capex: Option, + pub proceeds_from_sale_of_ppe: Option, + pub purchases_of_investments: Option, + pub sales_of_investments: Option, + pub acquisitions: Option, + pub other_investing_activities: Option, + pub investing_cash_flow: Option, + // Financing Activities + pub debt_issuance: Option, + pub debt_repayment: Option, + pub equity_issuance: Option, + pub stock_repurchases: Option, + pub dividends_paid: Option, + pub finance_lease_obligations: Option, + pub other_financing_activities: Option, + pub financing_cash_flow: Option, + // Summary pub free_cash_flow: Option, + pub exchange_rate_effects: Option, + pub net_increase_decrease_cash: Option, + pub beginning_cash: Option, pub ending_cash: Option, + pub cash_paid_interest: Option, + pub cash_paid_income_taxes: Option, } #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] diff --git a/MosaicIQ/src/components/Panels/CashFlowPanel.tsx b/MosaicIQ/src/components/Panels/CashFlowPanel.tsx index 1a423ff..59e9812 100644 --- a/MosaicIQ/src/components/Panels/CashFlowPanel.tsx +++ b/MosaicIQ/src/components/Panels/CashFlowPanel.tsx @@ -1,11 +1,29 @@ import React from 'react'; import { CashFlowPanelData } from '../../types/financial'; -import { StatementTableMinimal, formatMoney } from '../ui/StatementTableMinimal'; +import { formatMoney } from '../ui/StatementTableMinimal'; interface CashFlowPanelProps { data: CashFlowPanelData; } +type CashFlowRow = CashFlowPanelData['periods'][number]; + +interface SectionHeader { + key: string; + label: string; + isSectionHeader: true; +} + +interface MetricRow { + key: string; + label: string; + indent?: boolean; + isSectionHeader?: false; + render: (period: CashFlowRow) => React.ReactNode; +} + +type CashFlowMetric = SectionHeader | MetricRow; + const SourceAttribution: React.FC<{ status: CashFlowPanelData['sourceStatus'] }> = ({ status }) => { return (
@@ -17,10 +35,226 @@ const SourceAttribution: React.FC<{ status: CashFlowPanelData['sourceStatus'] }> ); }; +const metrics: CashFlowMetric[] = [ + { key: 'section-operating', label: 'Operating Activities', isSectionHeader: true }, + { + key: 'netIncome', + label: 'Net Income / Net Earnings', + render: (period: CashFlowRow) => formatMoney(period.netIncome), + }, + { + key: 'depreciationAndAmortization', + label: 'Depreciation & Amortization', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.depreciationAndAmortization), + }, + { + key: 'stockBasedCompensation', + label: 'Stock-Based Compensation', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.stockBasedCompensation), + }, + { + key: 'deferredIncomeTaxes', + label: 'Deferred Income Taxes', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.deferredIncomeTaxes), + }, + { + key: 'impairmentCharges', + label: 'Impairment Charges', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.impairmentCharges), + }, + { + key: 'lossGainOnSaleOfAssets', + label: 'Loss / Gain on Sale of Assets', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.lossGainOnSaleOfAssets), + }, + { + key: 'otherNonCashItems', + label: 'Other Non-Cash Items', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.otherNonCashItems), + }, + { + key: 'accountsReceivable', + label: 'Accounts Receivable', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.accountsReceivable), + }, + { + key: 'inventory', + label: 'Inventory', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.inventory), + }, + { + key: 'prepaidExpensesAndOtherCurrentAssets', + label: 'Prepaid Expenses and Other Current Assets', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.prepaidExpensesAndOtherCurrentAssets), + }, + { + key: 'accountsPayable', + label: 'Accounts Payable', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.accountsPayable), + }, + { + key: 'accruedExpenses', + label: 'Accrued Expenses', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.accruedExpenses), + }, + { + key: 'deferredRevenue', + label: 'Deferred Revenue', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.deferredRevenue), + }, + { + key: 'otherWorkingCapitalChanges', + label: 'Other Working Capital Changes', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.otherWorkingCapitalChanges), + }, + { + key: 'operatingCashFlow', + label: 'Net Cash Provided by Operating Activities', + render: (period: CashFlowRow) => formatMoney(period.operatingCashFlow), + }, + { key: 'section-investing', label: 'Investing Activities', isSectionHeader: true }, + { + key: 'capex', + label: 'Capital Expenditures / Purchases of Property and Equipment', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.capex), + }, + { + key: 'proceedsFromSaleOfPpe', + label: 'Proceeds from Sale of Property and Equipment', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.proceedsFromSaleOfPpe), + }, + { + key: 'acquisitions', + label: 'Acquisitions, Net of Cash Acquired', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.acquisitions), + }, + { + key: 'purchasesOfInvestments', + label: 'Purchases of Investments', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.purchasesOfInvestments), + }, + { + key: 'salesOfInvestments', + label: 'Proceeds from Sale or Maturity of Investments', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.salesOfInvestments), + }, + { + key: 'otherInvestingActivities', + label: 'Other Investing Activities', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.otherInvestingActivities), + }, + { + key: 'investingCashFlow', + label: 'Net Cash Used in Investing Activities', + render: (period: CashFlowRow) => formatMoney(period.investingCashFlow), + }, + { key: 'section-financing', label: 'Financing Activities', isSectionHeader: true }, + { + key: 'debtIssuance', + label: 'Proceeds from Debt Issuance', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.debtIssuance), + }, + { + key: 'debtRepayment', + label: 'Repayments of Debt', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.debtRepayment), + }, + { + key: 'equityIssuance', + label: 'Proceeds from Equity Issuance', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.equityIssuance), + }, + { + key: 'stockRepurchases', + label: 'Repurchases of Common Stock', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.stockRepurchases), + }, + { + key: 'dividendsPaid', + label: 'Dividends Paid', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.dividendsPaid), + }, + { + key: 'financeLeaseObligations', + label: 'Payment of Finance Lease Obligations', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.financeLeaseObligations), + }, + { + key: 'otherFinancingActivities', + label: 'Other Financing Activities', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.otherFinancingActivities), + }, + { + key: 'financingCashFlow', + label: 'Net Cash Provided by / Used in Financing Activities', + render: (period: CashFlowRow) => formatMoney(period.financingCashFlow), + }, + { key: 'section-summary', label: 'Supplemental / Other Cash Flow', isSectionHeader: true }, + { + key: 'exchangeRateEffects', + label: 'Effect of Exchange Rate Changes on Cash', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.exchangeRateEffects), + }, + { + key: 'netIncreaseDecreaseCash', + label: 'Net Increase / Decrease in Cash and Cash Equivalents', + render: (period: CashFlowRow) => formatMoney(period.netIncreaseDecreaseCash), + }, + { + key: 'beginningCash', + label: 'Cash and Cash Equivalents at Beginning of Period', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.beginningCash), + }, + { + key: 'endingCash', + label: 'Cash and Cash Equivalents at End of Period', + render: (period: CashFlowRow) => formatMoney(period.endingCash), + }, + { + key: 'cashPaidInterest', + label: 'Cash Paid for Interest', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.cashPaidInterest), + }, + { + key: 'cashPaidIncomeTaxes', + label: 'Cash Paid for Income Taxes', + indent: true, + render: (period: CashFlowRow) => formatMoney(period.cashPaidIncomeTaxes), + }, +]; + export const CashFlowPanel: React.FC = ({ data }) => { return (
- {/* Header - Minimal */}
@@ -40,22 +274,61 @@ export const CashFlowPanel: React.FC = ({ data }) => {
- {/* Table - Minimal styling */}
- formatMoney(period.operatingCashFlow) }, - { key: 'cfi', label: 'Investing Cash Flow', render: (period) => formatMoney(period.investingCashFlow) }, - { key: 'cff', label: 'Financing Cash Flow', render: (period) => formatMoney(period.financingCashFlow) }, - { key: 'capex', label: 'Capex', render: (period) => formatMoney(period.capex) }, - { key: 'fcf', label: 'Free Cash Flow', render: (period) => formatMoney(period.freeCashFlow) }, - { key: 'endingCash', label: 'Ending Cash', render: (period) => formatMoney(period.endingCash) }, - ]} - /> +
+ + + + + {data.periods.map((period) => ( + + ))} + + + + {metrics.map((metric) => { + if (metric.isSectionHeader) { + return ( + + + + ); + } + + const labelClass = metric.indent + ? 'pl-6 font-normal text-term-text-muted' + : 'font-medium text-term-text'; + + return ( + + + {data.periods.map((period) => ( + + ))} + + ); + })} + +
+ Item + + {period.label} +
+ {metric.label} +
+ {metric.label} + + {metric.render(period)} +
+
- {/* Footer - Minimal attribution */} diff --git a/MosaicIQ/src/lib/terminalResearchNote.ts b/MosaicIQ/src/lib/terminalResearchNote.ts index d08be33..4c5dc5f 100644 --- a/MosaicIQ/src/lib/terminalResearchNote.ts +++ b/MosaicIQ/src/lib/terminalResearchNote.ts @@ -118,15 +118,35 @@ const summarizeCashFlow = (panel: CashFlowPanelData, sourceCommand?: string) => const rows = latest ? [ `Latest period: ${latest.label}.`, + latest.netIncome != null ? `Net income: ${latest.netIncome.toLocaleString()}.` : null, + latest.depreciationAndAmortization != null + ? `Depreciation & amortization: ${latest.depreciationAndAmortization.toLocaleString()}.` + : null, latest.operatingCashFlow != null ? `Operating cash flow: ${latest.operatingCashFlow.toLocaleString()}.` : null, + latest.capex != null ? `Capital expenditures: ${latest.capex.toLocaleString()}.` : null, latest.freeCashFlow != null ? `Free cash flow: ${latest.freeCashFlow.toLocaleString()}.` : null, + latest.investingCashFlow != null + ? `Investing cash flow: ${latest.investingCashFlow.toLocaleString()}.` + : null, + latest.financingCashFlow != null + ? `Financing cash flow: ${latest.financingCashFlow.toLocaleString()}.` + : null, + latest.dividendsPaid != null + ? `Dividends paid: ${latest.dividendsPaid.toLocaleString()}.` + : null, + latest.stockRepurchases != null + ? `Stock repurchases: ${latest.stockRepurchases.toLocaleString()}.` + : null, + latest.endingCash != null + ? `Ending cash: ${latest.endingCash.toLocaleString()}.` + : null, ].filter(Boolean) as string[] : ['No cash flow rows loaded.']; - return summarizeStatementPanel('cash flow summary', panel.symbol, panel.latestFiling, rows, sourceCommand); + return summarizeStatementPanel('cash flow statement', panel.symbol, panel.latestFiling, rows, sourceCommand); }; const summarizeDividends = (panel: DividendsPanelData, sourceCommand?: string) => diff --git a/MosaicIQ/src/types/financial.ts b/MosaicIQ/src/types/financial.ts index 9c6b011..53853c7 100644 --- a/MosaicIQ/src/types/financial.ts +++ b/MosaicIQ/src/types/financial.ts @@ -175,12 +175,50 @@ export interface CashFlowPeriod { periodEnd: string; filedDate: string; form: string; + // Operating Activities + netIncome?: number; + depreciationAndAmortization?: number; + stockBasedCompensation?: number; + deferredIncomeTaxes?: number; + impairmentCharges?: number; + lossGainOnSaleOfAssets?: number; + otherNonCashItems?: number; + accountsReceivable?: number; + inventory?: number; + prepaidExpensesAndOtherCurrentAssets?: number; + accountsPayable?: number; + accruedExpenses?: number; + deferredRevenue?: number; + otherWorkingCapitalChanges?: number; + changeInOperatingAssets?: number; + changeInOperatingLiabilities?: number; + otherOperatingAdjustments?: number; operatingCashFlow?: number; - investingCashFlow?: number; - financingCashFlow?: number; + // Investing Activities capex?: number; + proceedsFromSaleOfPpe?: number; + purchasesOfInvestments?: number; + salesOfInvestments?: number; + acquisitions?: number; + otherInvestingActivities?: number; + investingCashFlow?: number; + // Financing Activities + debtIssuance?: number; + debtRepayment?: number; + equityIssuance?: number; + stockRepurchases?: number; + dividendsPaid?: number; + financeLeaseObligations?: number; + otherFinancingActivities?: number; + financingCashFlow?: number; + // Summary freeCashFlow?: number; + exchangeRateEffects?: number; + netIncreaseDecreaseCash?: number; + beginningCash?: number; endingCash?: number; + cashPaidInterest?: number; + cashPaidIncomeTaxes?: number; } export interface DividendEvent {