Learning by Doing Series — Topic 8: Build Cash Flow Indicators
Create operating cash flow, capital expenditures, free cash flow, cash flow to debt, and cash flow coverage indicators.
The main output of this topic is Fact_Cash_Flow.csv.
Create operating cash flow, capital expenditures, free cash flow, cash flow to debt, and cash flow coverage indicators.
El archivo principal de salida de este tópico es Fact_Cash_Flow.csv.
This exercise uses synthetic training data only. Do not run scripts in production, real corporate folders, financial databases, or work environments without formal authorization. Validate backups, sandbox testing, least-privilege permissions, change-control approval, audit requirements, and organizational cybersecurity protocols before automating any real financial process.
Este ejercicio usa data sintética. No ejecutes scripts en producción, carpetas reales, bases financieras corporativas o ambientes de trabajo sin autorización formal. Valida respaldos, pruebas en sandbox, permisos mínimos, control de cambios, auditoría y protocolos de ciberseguridad antes de automatizar cualquier proceso financiero real.
The training has already created dimensions and core financial data. This topic moves one step closer to an executive BI model by creating a clean analytical file for Cash Flow.
El training ya creó dimensiones y data financiera base. Este tópico avanza hacia el modelo ejecutivo de BI creando un archivo analítico limpio para Cash Flow.
from pathlib import Path
import pandas as pd
# ============================================================
# Financial Ratios Analysis in BI
# Topic 8 - Build Cash Flow Indicators
# ============================================================
# ------------------------------------------------------------
# Step 1 - Set project folder
# ------------------------------------------------------------
base_path = Path("financial_ratios_bi_training")
if not base_path.exists():
raise FileNotFoundError(
"The project folder does not exist. Please run Topic 1 first."
)
trial_balance_path = base_path / "Fact_Trial_Balance.csv"
mapping_path = base_path / "Mapping_Financial_Statements.csv"
balance_sheet_path = base_path / "Fact_Balance_Sheet.csv"
dim_company_path = base_path / "Dim_Company.csv"
dim_period_path = base_path / "Dim_Period.csv"
required_files = [
trial_balance_path,
mapping_path,
balance_sheet_path,
dim_company_path,
dim_period_path
]
for file_path in required_files:
if not file_path.exists():
raise FileNotFoundError(
f"Required file not found: {file_path.name}. "
"Please run the previous topics first."
)
print(f"Project folder found: {base_path.resolve()}")
# ------------------------------------------------------------
# Step 2 - Load source files
# ------------------------------------------------------------
trial_balance = pd.read_csv(trial_balance_path)
mapping = pd.read_csv(mapping_path)
balance_sheet = pd.read_csv(balance_sheet_path)
dim_company = pd.read_csv(dim_company_path)
dim_period = pd.read_csv(dim_period_path)
trial_balance["AccountNumber"] = trial_balance["AccountNumber"].astype(str)
mapping["AccountNumber"] = mapping["AccountNumber"].astype(str)
print("Fact_Trial_Balance loaded:", len(trial_balance), "rows")
print("Mapping_Financial_Statements loaded:", len(mapping), "rows")
print("Fact_Balance_Sheet loaded:", len(balance_sheet), "rows")
# ------------------------------------------------------------
# Step 3 - Filter Cash Flow Statement accounts
# ------------------------------------------------------------
cash_flow_mapping = mapping[
mapping["FinancialStatement"] == "Cash Flow Statement"
].copy()
cash_flow_source = trial_balance.merge(
cash_flow_mapping[
[
"AccountNumber",
"StatementSection",
"StatementLine",
"LineGroup",
"LineOrder",
"SignMultiplier",
"RatioInput"
]
],
on="AccountNumber",
how="inner"
)
print("Cash flow source rows:", len(cash_flow_source))
# ------------------------------------------------------------
# Step 4 - Calculate cash flow amount
# ------------------------------------------------------------
# In this training model:
# - Operating Cash Flow is shown as positive.
# - Capital Expenditures are shown as negative because they are cash outflows.
# - Dividends Paid are shown as negative because they are cash outflows.
cash_flow_source["RawAmount"] = cash_flow_source.apply(
lambda row: row["Debit"] if row["Debit"] > 0 else row["Credit"],
axis=1
)
cash_flow_source["Amount"] = cash_flow_source["RawAmount"].round(2)
cash_flow_source.loc[
cash_flow_source["RatioInput"] == "Capital Expenditures",
"Amount"
] = cash_flow_source.loc[
cash_flow_source["RatioInput"] == "Capital Expenditures",
"Amount"
] * -1
cash_flow_source.loc[
cash_flow_source["RatioInput"] == "Dividends Paid",
"Amount"
] = cash_flow_source.loc[
cash_flow_source["RatioInput"] == "Dividends Paid",
"Amount"
] * -1
cash_flow_source["Amount"] = cash_flow_source["Amount"].round(2)
# ------------------------------------------------------------
# Step 5 - Aggregate base cash flow lines
# ------------------------------------------------------------
base_lines = (
cash_flow_source
.groupby(
[
"CompanyID",
"PeriodID",
"StatementSection",
"StatementLine",
"LineGroup",
"LineOrder",
"RatioInput"
]
)
.agg(Amount=("Amount", "sum"))
.reset_index()
)
base_lines["Amount"] = base_lines["Amount"].round(2)
base_lines["LineType"] = "Base Line"
# ------------------------------------------------------------
# Step 6 - Prepare debt values from Balance Sheet
# ------------------------------------------------------------
debt_lines = balance_sheet[
balance_sheet["RatioInput"].isin(
[
"Short-Term Debt",
"Long-Term Debt",
"Total Liabilities"
]
)
].copy()
debt_pivot = (
debt_lines
.pivot_table(
index=["CompanyID", "PeriodID"],
columns="RatioInput",
values="Amount",
aggfunc="sum"
)
.reset_index()
)
for col in ["Short-Term Debt", "Long-Term Debt", "Total Liabilities"]:
if col not in debt_pivot.columns:
debt_pivot[col] = 0
debt_pivot["TotalDebt"] = (
debt_pivot["Short-Term Debt"]
+ debt_pivot["Long-Term Debt"]
).round(2)
# ------------------------------------------------------------
# Step 7 - Create calculated cash flow indicators
# ------------------------------------------------------------
calculated_records = []
for (company_id, period_id), group in base_lines.groupby(["CompanyID", "PeriodID"]):
operating_cash_flow = group.loc[
group["RatioInput"] == "Operating Cash Flow",
"Amount"
].sum()
capital_expenditures = group.loc[
group["RatioInput"] == "Capital Expenditures",
"Amount"
].sum()
dividends_paid = group.loc[
group["RatioInput"] == "Dividends Paid",
"Amount"
].sum()
free_cash_flow = operating_cash_flow + capital_expenditures
net_cash_flow_after_dividends = free_cash_flow + dividends_paid
debt_row = debt_pivot[
(debt_pivot["CompanyID"] == company_id)
& (debt_pivot["PeriodID"] == period_id)
]
if debt_row.empty:
total_debt = 0
total_liabilities = 0
else:
total_debt = float(debt_row["TotalDebt"].iloc[0])
total_liabilities = float(debt_row["Total Liabilities"].iloc[0])
cash_flow_to_debt = (
operating_cash_flow / total_debt
if total_debt != 0
else 0
)
cash_flow_coverage = (
operating_cash_flow / total_liabilities
if total_liabilities != 0
else 0
)
calculated_records.extend([
{
"CompanyID": company_id,
"PeriodID": period_id,
"StatementSection": "Cash Flow Indicators",
"StatementLine": "Free Cash Flow",
"LineGroup": "Calculated Cash Flow Lines",
"LineOrder": 1250,
"RatioInput": "Free Cash Flow",
"Amount": round(free_cash_flow, 2),
"LineType": "Calculated Line"
},
{
"CompanyID": company_id,
"PeriodID": period_id,
"StatementSection": "Cash Flow Indicators",
"StatementLine": "Net Cash Flow After Dividends",
"LineGroup": "Calculated Cash Flow Lines",
"LineOrder": 1350,
"RatioInput": "Net Cash Flow After Dividends",
"Amount": round(net_cash_flow_after_dividends, 2),
"LineType": "Calculated Line"
},
{
"CompanyID": company_id,
"PeriodID": period_id,
"StatementSection": "Cash Flow Indicators",
"StatementLine": "Cash Flow to Debt",
"LineGroup": "Calculated Cash Flow Ratios",
"LineOrder": 1400,
"RatioInput": "Cash Flow to Debt",
"Amount": round(cash_flow_to_debt, 4),
"LineType": "Calculated Ratio"
},
{
"CompanyID": company_id,
"PeriodID": period_id,
"StatementSection": "Cash Flow Indicators",
"StatementLine": "Cash Flow Coverage",
"LineGroup": "Calculated Cash Flow Ratios",
"LineOrder": 1410,
"RatioInput": "Cash Flow Coverage",
"Amount": round(cash_flow_coverage, 4),
"LineType": "Calculated Ratio"
}
])
calculated_lines = pd.DataFrame(calculated_records)
# ------------------------------------------------------------
# Step 8 - Combine base and calculated lines
# ------------------------------------------------------------
fact_cash_flow = pd.concat(
[
base_lines,
calculated_lines
],
ignore_index=True
)
fact_cash_flow = fact_cash_flow.sort_values(
[
"CompanyID",
"PeriodID",
"LineOrder",
"StatementLine"
]
).reset_index(drop=True)
# ------------------------------------------------------------
# Step 9 - Add company and period labels
# ------------------------------------------------------------
fact_cash_flow = fact_cash_flow.merge(
dim_company[["CompanyID", "CompanyName", "Industry"]],
on="CompanyID",
how="left"
)
fact_cash_flow = fact_cash_flow.merge(
dim_period[
[
"PeriodID",
"FiscalYear",
"FiscalQuarter",
"YearQuarter",
"PeriodEndDate"
]
],
on="PeriodID",
how="left"
)
fact_cash_flow = fact_cash_flow[
[
"CompanyID",
"CompanyName",
"Industry",
"PeriodID",
"FiscalYear",
"FiscalQuarter",
"YearQuarter",
"PeriodEndDate",
"StatementSection",
"StatementLine",
"LineGroup",
"LineOrder",
"RatioInput",
"LineType",
"Amount"
]
]
# ------------------------------------------------------------
# Step 10 - Export Fact_Cash_Flow
# ------------------------------------------------------------
fact_cash_flow.to_csv(
base_path / "Fact_Cash_Flow.csv",
index=False
)
print("Fact_Cash_Flow.csv created successfully.")
# ------------------------------------------------------------
# Step 11 - Create cash flow validation summary
# ------------------------------------------------------------
validation_records = []
for (company_id, period_id), group in fact_cash_flow.groupby(["CompanyID", "PeriodID"]):
operating_cash_flow = group.loc[
group["RatioInput"] == "Operating Cash Flow",
"Amount"
].sum()
capital_expenditures = group.loc[
group["RatioInput"] == "Capital Expenditures",
"Amount"
].sum()
free_cash_flow = group.loc[
group["RatioInput"] == "Free Cash Flow",
"Amount"
].sum()
dividends_paid = group.loc[
group["RatioInput"] == "Dividends Paid",
"Amount"
].sum()
net_cash_flow_after_dividends = group.loc[
group["RatioInput"] == "Net Cash Flow After Dividends",
"Amount"
].sum()
expected_free_cash_flow = operating_cash_flow + capital_expenditures
free_cash_flow_difference = round(
free_cash_flow - expected_free_cash_flow,
2
)
expected_net_cash_flow = free_cash_flow + dividends_paid
net_cash_flow_difference = round(
net_cash_flow_after_dividends - expected_net_cash_flow,
2
)
validation_records.append({
"CompanyID": company_id,
"PeriodID": period_id,
"OperatingCashFlow": round(operating_cash_flow, 2),
"CapitalExpenditures": round(capital_expenditures, 2),
"FreeCashFlow": round(free_cash_flow, 2),
"ExpectedFreeCashFlow": round(expected_free_cash_flow, 2),
"FreeCashFlowDifference": free_cash_flow_difference,
"DividendsPaid": round(dividends_paid, 2),
"NetCashFlowAfterDividends": round(net_cash_flow_after_dividends, 2),
"ExpectedNetCashFlowAfterDividends": round(expected_net_cash_flow, 2),
"NetCashFlowDifference": net_cash_flow_difference,
"Status": (
"PASSED"
if free_cash_flow_difference == 0
and net_cash_flow_difference == 0
else "FAILED"
)
})
cash_flow_validation = pd.DataFrame(validation_records)
cash_flow_validation.to_csv(
base_path / "Cash_Flow_Validation_Summary.csv",
index=False
)
print("Cash_Flow_Validation_Summary.csv created successfully.")
# ------------------------------------------------------------
# Step 12 - Print validation summary
# ------------------------------------------------------------
print()
print("==============================")
print("VALIDATION SUMMARY")
print("==============================")
print("Cash flow rows:", len(fact_cash_flow))
print("Validation rows:", len(cash_flow_validation))
print()
print("Rows by LineType:")
print(
fact_cash_flow
.groupby("LineType")["StatementLine"]
.count()
.reset_index(name="NumberOfRows")
)
print()
print("Cash Flow Lines:")
print(
fact_cash_flow[
[
"LineOrder",
"StatementLine",
"LineType"
]
]
.drop_duplicates()
.sort_values("LineOrder")
)
print()
print("Cash Flow Validation Summary:")
print(cash_flow_validation.head(20))
print()
print("Failed validation rows:")
failed_rows = cash_flow_validation[cash_flow_validation["Status"] == "FAILED"]
if failed_rows.empty:
print("No failed rows. Cash Flow validation passed.")
else:
print(failed_rows)
print()
print("Files currently in project folder:")
for file in base_path.glob("*.csv"):
print("-", file.name)
print()
print("Topic 8 completed successfully.")
The script or instructions create:
El script o las instrucciones crean:
financial_ratios_bi_training/Fact_Cash_Flow.csv
This topic is not only a technical exercise. It teaches students how to translate accounting data into management information. The output becomes part of the BI layer used later for ratios, dashboards, trend analysis, benchmarking, and executive decision-making.
Este tópico no es solo un ejercicio técnico. Enseña a convertir data contable en información gerencial. El archivo de salida se integra a la capa de BI que luego se usa para ratios, dashboards, tendencias, comparación entre compañías y decisiones ejecutivas.
Before moving forward, open the generated file, review the columns, confirm the number of companies and periods, and make sure the numbers make financial sense. Good BI starts with clean, explainable, and validated data.
This is the corrected and live-tested script for Topic 8: Build Cash Flow Indicators.
from pathlib import Path
import pandas as pd
# ============================================================
# Financial Ratios Analysis in BI
# Topic 8 - Build Cash Flow Indicators
# ============================================================
# ------------------------------------------------------------
# Step 1 - Set project folder
# ------------------------------------------------------------
base_path = Path("financial_ratios_bi_training")
if not base_path.exists():
raise FileNotFoundError(
"The project folder does not exist. Please run Topic 1 first."
)
trial_balance_path = base_path / "Fact_Trial_Balance.csv"
mapping_path = base_path / "Mapping_Financial_Statements.csv"
balance_sheet_path = base_path / "Fact_Balance_Sheet.csv"
dim_company_path = base_path / "Dim_Company.csv"
dim_period_path = base_path / "Dim_Period.csv"
required_files = [
trial_balance_path,
mapping_path,
balance_sheet_path,
dim_company_path,
dim_period_path
]
for file_path in required_files:
if not file_path.exists():
raise FileNotFoundError(
f"Required file not found: {file_path.name}. "
"Please run the previous topics first."
)
print(f"Project folder found: {base_path.resolve()}")
# ------------------------------------------------------------
# Step 2 - Load source files
# ------------------------------------------------------------
trial_balance = pd.read_csv(trial_balance_path)
mapping = pd.read_csv(mapping_path)
balance_sheet = pd.read_csv(balance_sheet_path)
dim_company = pd.read_csv(dim_company_path)
dim_period = pd.read_csv(dim_period_path)
trial_balance["AccountNumber"] = trial_balance["AccountNumber"].astype(str)
mapping["AccountNumber"] = mapping["AccountNumber"].astype(str)
print("Fact_Trial_Balance loaded:", len(trial_balance), "rows")
print("Mapping_Financial_Statements loaded:", len(mapping), "rows")
print("Fact_Balance_Sheet loaded:", len(balance_sheet), "rows")
# ------------------------------------------------------------
# Step 3 - Filter Cash Flow Statement accounts
# ------------------------------------------------------------
cash_flow_mapping = mapping[
mapping["FinancialStatement"] == "Cash Flow Statement"
].copy()
cash_flow_source = trial_balance.merge(
cash_flow_mapping[
[
"AccountNumber",
"StatementSection",
"StatementLine",
"LineGroup",
"LineOrder",
"SignMultiplier",
"RatioInput"
]
],
on="AccountNumber",
how="inner"
)
print("Cash flow source rows:", len(cash_flow_source))
# ------------------------------------------------------------
# Step 4 - Calculate cash flow amount
# ------------------------------------------------------------
# In this training model:
# - Operating Cash Flow is shown as positive.
# - Capital Expenditures are shown as negative because they are cash outflows.
# - Dividends Paid are shown as negative because they are cash outflows.
cash_flow_source["RawAmount"] = cash_flow_source.apply(
lambda row: row["Debit"] if row["Debit"] > 0 else row["Credit"],
axis=1
)
cash_flow_source["Amount"] = cash_flow_source["RawAmount"].round(2)
cash_flow_source.loc[
cash_flow_source["RatioInput"] == "Capital Expenditures",
"Amount"
] = cash_flow_source.loc[
cash_flow_source["RatioInput"] == "Capital Expenditures",
"Amount"
] * -1
cash_flow_source.loc[
cash_flow_source["RatioInput"] == "Dividends Paid",
"Amount"
] = cash_flow_source.loc[
cash_flow_source["RatioInput"] == "Dividends Paid",
"Amount"
] * -1
cash_flow_source["Amount"] = cash_flow_source["Amount"].round(2)
# ------------------------------------------------------------
# Step 5 - Aggregate base cash flow lines
# ------------------------------------------------------------
base_lines = (
cash_flow_source
.groupby(
[
"CompanyID",
"PeriodID",
"StatementSection",
"StatementLine",
"LineGroup",
"LineOrder",
"RatioInput"
]
)
.agg(Amount=("Amount", "sum"))
.reset_index()
)
base_lines["Amount"] = base_lines["Amount"].round(2)
base_lines["LineType"] = "Base Line"
# ------------------------------------------------------------
# Step 6 - Prepare debt values from Balance Sheet
# ------------------------------------------------------------
debt_lines = balance_sheet[
balance_sheet["RatioInput"].isin(
[
"Short-Term Debt",
"Long-Term Debt",
"Total Liabilities"
]
)
].copy()
debt_pivot = (
debt_lines
.pivot_table(
index=["CompanyID", "PeriodID"],
columns="RatioInput",
values="Amount",
aggfunc="sum"
)
.reset_index()
)
for col in ["Short-Term Debt", "Long-Term Debt", "Total Liabilities"]:
if col not in debt_pivot.columns:
debt_pivot[col] = 0
debt_pivot["TotalDebt"] = (
debt_pivot["Short-Term Debt"]
+ debt_pivot["Long-Term Debt"]
).round(2)
# ------------------------------------------------------------
# Step 7 - Create calculated cash flow indicators
# ------------------------------------------------------------
calculated_records = []
for (company_id, period_id), group in base_lines.groupby(["CompanyID", "PeriodID"]):
operating_cash_flow = group.loc[
group["RatioInput"] == "Operating Cash Flow",
"Amount"
].sum()
capital_expenditures = group.loc[
group["RatioInput"] == "Capital Expenditures",
"Amount"
].sum()
dividends_paid = group.loc[
group["RatioInput"] == "Dividends Paid",
"Amount"
].sum()
free_cash_flow = operating_cash_flow + capital_expenditures
net_cash_flow_after_dividends = free_cash_flow + dividends_paid
debt_row = debt_pivot[
(debt_pivot["CompanyID"] == company_id)
& (debt_pivot["PeriodID"] == period_id)
]
if debt_row.empty:
total_debt = 0
total_liabilities = 0
else:
total_debt = float(debt_row["TotalDebt"].iloc[0])
total_liabilities = float(debt_row["Total Liabilities"].iloc[0])
cash_flow_to_debt = (
operating_cash_flow / total_debt
if total_debt != 0
else 0
)
cash_flow_coverage = (
operating_cash_flow / total_liabilities
if total_liabilities != 0
else 0
)
calculated_records.extend([
{
"CompanyID": company_id,
"PeriodID": period_id,
"StatementSection": "Cash Flow Indicators",
"StatementLine": "Free Cash Flow",
"LineGroup": "Calculated Cash Flow Lines",
"LineOrder": 1250,
"RatioInput": "Free Cash Flow",
"Amount": round(free_cash_flow, 2),
"LineType": "Calculated Line"
},
{
"CompanyID": company_id,
"PeriodID": period_id,
"StatementSection": "Cash Flow Indicators",
"StatementLine": "Net Cash Flow After Dividends",
"LineGroup": "Calculated Cash Flow Lines",
"LineOrder": 1350,
"RatioInput": "Net Cash Flow After Dividends",
"Amount": round(net_cash_flow_after_dividends, 2),
"LineType": "Calculated Line"
},
{
"CompanyID": company_id,
"PeriodID": period_id,
"StatementSection": "Cash Flow Indicators",
"StatementLine": "Cash Flow to Debt",
"LineGroup": "Calculated Cash Flow Ratios",
"LineOrder": 1400,
"RatioInput": "Cash Flow to Debt",
"Amount": round(cash_flow_to_debt, 4),
"LineType": "Calculated Ratio"
},
{
"CompanyID": company_id,
"PeriodID": period_id,
"StatementSection": "Cash Flow Indicators",
"StatementLine": "Cash Flow Coverage",
"LineGroup": "Calculated Cash Flow Ratios",
"LineOrder": 1410,
"RatioInput": "Cash Flow Coverage",
"Amount": round(cash_flow_coverage, 4),
"LineType": "Calculated Ratio"
}
])
calculated_lines = pd.DataFrame(calculated_records)
# ------------------------------------------------------------
# Step 8 - Combine base and calculated lines
# ------------------------------------------------------------
fact_cash_flow = pd.concat(
[
base_lines,
calculated_lines
],
ignore_index=True
)
fact_cash_flow = fact_cash_flow.sort_values(
[
"CompanyID",
"PeriodID",
"LineOrder",
"StatementLine"
]
).reset_index(drop=True)
# ------------------------------------------------------------
# Step 9 - Add company and period labels
# ------------------------------------------------------------
fact_cash_flow = fact_cash_flow.merge(
dim_company[["CompanyID", "CompanyName", "Industry"]],
on="CompanyID",
how="left"
)
fact_cash_flow = fact_cash_flow.merge(
dim_period[
[
"PeriodID",
"FiscalYear",
"FiscalQuarter",
"YearQuarter",
"PeriodEndDate"
]
],
on="PeriodID",
how="left"
)
fact_cash_flow = fact_cash_flow[
[
"CompanyID",
"CompanyName",
"Industry",
"PeriodID",
"FiscalYear",
"FiscalQuarter",
"YearQuarter",
"PeriodEndDate",
"StatementSection",
"StatementLine",
"LineGroup",
"LineOrder",
"RatioInput",
"LineType",
"Amount"
]
]
# ------------------------------------------------------------
# Step 10 - Export Fact_Cash_Flow
# ------------------------------------------------------------
fact_cash_flow.to_csv(
base_path / "Fact_Cash_Flow.csv",
index=False
)
print("Fact_Cash_Flow.csv created successfully.")
# ------------------------------------------------------------
# Step 11 - Create cash flow validation summary
# ------------------------------------------------------------
validation_records = []
for (company_id, period_id), group in fact_cash_flow.groupby(["CompanyID", "PeriodID"]):
operating_cash_flow = group.loc[
group["RatioInput"] == "Operating Cash Flow",
"Amount"
].sum()
capital_expenditures = group.loc[
group["RatioInput"] == "Capital Expenditures",
"Amount"
].sum()
free_cash_flow = group.loc[
group["RatioInput"] == "Free Cash Flow",
"Amount"
].sum()
dividends_paid = group.loc[
group["RatioInput"] == "Dividends Paid",
"Amount"
].sum()
net_cash_flow_after_dividends = group.loc[
group["RatioInput"] == "Net Cash Flow After Dividends",
"Amount"
].sum()
expected_free_cash_flow = operating_cash_flow + capital_expenditures
free_cash_flow_difference = round(
free_cash_flow - expected_free_cash_flow,
2
)
expected_net_cash_flow = free_cash_flow + dividends_paid
net_cash_flow_difference = round(
net_cash_flow_after_dividends - expected_net_cash_flow,
2
)
validation_records.append({
"CompanyID": company_id,
"PeriodID": period_id,
"OperatingCashFlow": round(operating_cash_flow, 2),
"CapitalExpenditures": round(capital_expenditures, 2),
"FreeCashFlow": round(free_cash_flow, 2),
"ExpectedFreeCashFlow": round(expected_free_cash_flow, 2),
"FreeCashFlowDifference": free_cash_flow_difference,
"DividendsPaid": round(dividends_paid, 2),
"NetCashFlowAfterDividends": round(net_cash_flow_after_dividends, 2),
"ExpectedNetCashFlowAfterDividends": round(expected_net_cash_flow, 2),
"NetCashFlowDifference": net_cash_flow_difference,
"Status": (
"PASSED"
if free_cash_flow_difference == 0
and net_cash_flow_difference == 0
else "FAILED"
)
})
cash_flow_validation = pd.DataFrame(validation_records)
cash_flow_validation.to_csv(
base_path / "Cash_Flow_Validation_Summary.csv",
index=False
)
print("Cash_Flow_Validation_Summary.csv created successfully.")
# ------------------------------------------------------------
# Step 12 - Print validation summary
# ------------------------------------------------------------
print()
print("==============================")
print("VALIDATION SUMMARY")
print("==============================")
print("Cash flow rows:", len(fact_cash_flow))
print("Validation rows:", len(cash_flow_validation))
print()
print("Rows by LineType:")
print(
fact_cash_flow
.groupby("LineType")["StatementLine"]
.count()
.reset_index(name="NumberOfRows")
)
print()
print("Cash Flow Lines:")
print(
fact_cash_flow[
[
"LineOrder",
"StatementLine",
"LineType"
]
]
.drop_duplicates()
.sort_values("LineOrder")
)
print()
print("Cash Flow Validation Summary:")
print(cash_flow_validation.head(20))
print()
print("Failed validation rows:")
failed_rows = cash_flow_validation[cash_flow_validation["Status"] == "FAILED"]
if failed_rows.empty:
print("No failed rows. Cash Flow validation passed.")
else:
print(failed_rows)
print()
print("Files currently in project folder:")
for file in base_path.glob("*.csv"):
print("-", file.name)
print()
print("Topic 8 completed successfully.")