knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE) library(cre.dcf) library(dplyr) library(tibble) library(scales) library(yaml)
This vignette illustrates how tax_run_spv() can be used on a French office investment case.
It is intentionally a stylized French-like SPV illustration, not a legal or tax opinion. As of April 1, 2026, the normal French corporate income-tax rate is 25%, following the official French tax administration's published corporate-tax guidance consulted for this vignette.
The rest of the tax assumptions below are simulated package inputs chosen to remain within the current scope of tax_run_spv():
The point of the vignette is therefore methodological: show how a French case can be parameterized in the current generic engine without pretending to replicate the full French tax code.
We use the package's preset_core.yml, which already describes a stylized Paris 8e office investment.
cfg_path <- system.file("extdata", "preset_core.yml", package = "cre.dcf") cfg <- yaml::read_yaml(cfg_path) case <- run_case(cfg) bullet_summary <- case$comparison$summary |> filter(scenario == "debt_bullet") |> transmute( scenario, irr_project, irr_equity, min_dscr, max_ltv_forward, ops_share, tv_share ) knitr::kable( bullet_summary, digits = 3, caption = "Before-tax baseline for the stylized French core-office case" )
At this stage, the core DCF is still entirely before tax. The tax layer comes next and consumes the consolidated cash-flow table.
The current tax engine needs two objects:
tax_basis <- tax_basis_spv(case) tax_assumptions <- tibble::tribble( ~item, ~value, ~comment, "Corporate income tax", "25%", "Official French normal CIT rate as of April 1, 2026", "Land share", "20%", "Stylized non-depreciable portion", "Building share", "65%", "Stylized depreciable structure", "Fit-out share", "15%", "Stylized depreciable tenant-improvement bucket", "Building life", "30 years", "Stylized straight-line life", "Fit-out life", "10 years", "Stylized straight-line life", "Interest deductibility", "full", "Current engine scope, not full French law", "Loss carryforward", "on", "Stylized carryforward allowed", "Offset cap", "50%", "Stylized cap used to keep the rule conservative" ) knitr::kable( tax_assumptions, caption = "French-like tax assumptions used in this vignette" )
tax_spec <- tax_spec_spv( corp_tax_rate = 0.25, depreciation_spec = depreciation_spec( acquisition_split = tibble::tribble( ~bucket, ~share, ~life_years, ~method, ~depreciable, "land", 0.20, NA, "none", FALSE, "building", 0.65, 30, "straight_line", TRUE, "fitout", 0.15, 10, "straight_line", TRUE ), capex_bucket = "fitout", start_rule = "full_year" ), interest_rule = interest_rule(mode = "full"), loss_rule = loss_rule( carryforward = TRUE, carryforward_years = Inf, offset_cap_pct = 0.50 ) ) tax_res <- tax_run_spv(tax_basis, tax_spec)
The yearly table below is the main output of tax_run_spv().
tax_view <- tax_res$tax_table |> select( year, noi, tax_depreciation, deductible_interest, taxable_income_pre_losses, loss_cf_open, loss_cf_used, cash_is, after_tax_equity_cf ) knitr::kable( tax_view, digits = 0, caption = "Stylized French SPV tax table" )
This is the main reading grid:
tax_depreciation transforms acquisition basis and capex into fiscal charges,deductible_interest removes the debt cost that is deductible under the simplified rule,taxable_income_pre_losses is the taxable base before using prior losses,loss_cf_open and loss_cf_used show how prior tax losses are mobilized,cash_is is the annual corporate income tax paid by the SPV,after_tax_equity_cf is the equity cash flow net of that annual tax.The generic engine does not yet build a complete French investor-level valuation. It does, however, let us measure the tax drag at the SPV level.
equity_bridge <- tax_res$tax_table |> select(year, pre_tax_equity_cf, cash_is, after_tax_equity_cf) knitr::kable( equity_bridge, digits = 0, caption = "Bridge from pre-tax to after-tax equity cash flows" )
We can also compare the before-tax leveraged IRR with a stylized after-tax SPV equity IRR computed on after_tax_equity_cf.
pre_tax_equity_irr <- case$leveraged$irr_equity after_tax_equity_irr <- irr_safe(tax_res$tax_table$after_tax_equity_cf) tax_highlights <- tibble( pre_tax_equity_irr = pre_tax_equity_irr, after_tax_spv_equity_irr = after_tax_equity_irr, total_cash_is = tax_res$summary$total_cash_is, total_tax_depreciation = tax_res$summary$total_tax_depreciation, exit_year_cash_is = tax_res$tax_table$cash_is[ tax_res$tax_table$year == max(tax_res$tax_table$year) ] ) knitr::kable( tax_highlights, digits = 3, caption = "Stylized before-tax versus after-tax SPV reading" )
In this example:
cash_is,This French illustration is useful because it shows a coherent workflow:
But it still has clear limits.
Captured:
Not captured:
This vignette shows how cre.dcf can already support a realistic French-style teaching case without hard-coding French tax law into the package core.
tax_run_spv() adds a stylized SPV tax reading on top of it.Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.