inst/doc/french-investment-tax-impact.R

## ----setup, include=FALSE-----------------------------------------------------
knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE)
library(cre.dcf)
library(dplyr)
library(tibble)
library(scales)
library(yaml)

## -----------------------------------------------------------------------------
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"
)

## -----------------------------------------------------------------------------
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)

## -----------------------------------------------------------------------------
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"
)

## -----------------------------------------------------------------------------
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"
)

## -----------------------------------------------------------------------------
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"
)

Try the cre.dcf package in your browser

Any scripts or data that you put into this service are public.

cre.dcf documentation built on April 10, 2026, 5:08 p.m.