Author: Package cre.dcf

# French investment illustration: stylized SPV tax impact

#### Package cre.dcf

## 1 Purpose

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()`:

- straight-line depreciation only,
- simple interest deductibility,
- simple loss carryforwards,
- no investor-level taxes,
- no withholding taxes,
- no treaty logic,
- no attempt to encode the full French interest-limitation regime.

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.

## 2 Start from a French operating case

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

Before-tax baseline for the stylized French core-office case
scenario | irr_project | irr_equity | min_dscr | max_ltv_forward | ops_share | tv_share
--- | --- | --- | --- | --- | --- | ---
debt_bullet | 0.046 | 0.062 | 5.257 | 0.449 | 0.37 | 0.63

At this stage, the core DCF is still entirely before tax. The tax layer comes next and consumes the consolidated cash-flow table.

## 3 Translate the French-like SPV into tax inputs

The current tax engine needs two objects:

- a tax basis extracted from the pre-tax case,
- a tax specification describing the stylized SPV rules.

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

French-like tax assumptions used in this vignette
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

    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)

## 4 Read the yearly tax table

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

Stylized French SPV tax table
year | noi | tax_depreciation | deductible_interest | taxable_income_pre_losses | loss_cf_open | loss_cf_used | cash_is | after_tax_equity_cf
--- | --- | --- | --- | --- | --- | --- | --- | ---
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -30816000
1 | 2376000 | 1760000 | 451968 | 164032 | 0 | 0 | 41008 | 1883024
2 | 2399760 | 1760000 | 451968 | 187792 | 0 | 0 | 46948 | 1900844
3 | 2423758 | 1760000 | 451968 | 211790 | 0 | 0 | 52947 | 1918842
4 | 2447995 | 1760000 | 451968 | 236027 | 0 | 0 | 59007 | 1937020
5 | 2472475 | 1760000 | 451968 | 260507 | 0 | 0 | 65127 | 1955380
6 | 2497200 | 1760000 | 451968 | 285232 | 0 | 0 | 71308 | 1973924
7 | 2541279 | 1761592 | 451968 | 327719 | 0 | 0 | 81930 | 1991459
8 | 2566692 | 1763200 | 451968 | 351524 | 0 | 0 | 87881 | 2010761
9 | 2592359 | 1764825 | 451968 | 375566 | 0 | 0 | 93892 | 2030257
10 | 2618283 | 1766465 | 451968 | 19818340 | 0 | 0 | 4954585 | 26518385

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.

## 5 Compare pre-tax and after-tax equity cash flows

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

Bridge from pre-tax to after-tax equity cash flows
year | pre_tax_equity_cf | cash_is | after_tax_equity_cf
--- | --- | --- | ---
0 | -30816000 | 0 | -30816000
1 | 1924032 | 41008 | 1883024
2 | 1947792 | 46948 | 1900844
3 | 1971790 | 52947 | 1918842
4 | 1996027 | 59007 | 1937020
5 | 2020507 | 65127 | 1955380
6 | 2045232 | 71308 | 1973924
7 | 2073388 | 81930 | 1991459
8 | 2098642 | 87881 | 2010761
9 | 2124148 | 93892 | 2030257
10 | 31472970 | 4954585 | 26518385

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

Stylized before-tax versus after-tax SPV reading
pre_tax_equity_irr | after_tax_spv_equity_irr | total_cash_is | total_tax_depreciation | exit_year_cash_is
--- | --- | --- | --- | ---
0.062 | 0.047 | 5554632 | 17616083 | 4954585

In this example:

- the pre-tax DCF remains the economic backbone of the case,
- the stylized French tax layer reduces equity cash flows through annual `cash_is`,
- depreciation materially shapes the taxable base,
- and the exit year remains a critical point because the final tax charge can be large.

## 6 What this vignette captures and what it does not

This French illustration is useful because it shows a coherent workflow:

1. build the investment case,
2. extract a tax basis,
3. define a country-like SPV parameterization,
4. read the yearly tax consequences.

But it still has clear limits.

Captured:

- a French office investment setting,
- a normal-CIT-rate scenario,
- non-depreciable land,
- depreciable building and fit-out buckets,
- simple loss carryforwards,
- a visible bridge from pre-tax to after-tax equity cash flows.

Not captured:

- the full French interest-limitation regime,
- the exact French loss-carryforward formula,
- shareholder taxation,
- transfer-tax mechanics beyond the acquisition-cost block already embedded in the DCF,
- exit-structuring choices,
- treaty and cross-border effects.

## 7 Summary

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.

- The before-tax DCF remains the common economic layer.
- `tax_run_spv()` adds a stylized SPV tax reading on top of it.
- The result is useful for scenario analysis and methodology, but should not be read as legal advice or as a complete French tax model.