Author: m.eik michalke
Date: 2023-08-14

# The businessPlanR Package

#### m.eik michalke

#### 2023-08-14

Abstract

Learn how to construct and visualize business plans in R. This package provides some tools that, although sometimes rather simple, can be used to build an interactive model of your business activity.

# Introduction

The `businessPlanR` package is the result of the Cultural Commons Collecting Society’s (C3S) need for a comprehensive business plan in order to apply for admission at the DPMA. Business plans can be a bit like looking into a crystal ball, especially when you’re trying something completely new with your business. The more we got into the interrelated details, the more we wanted a tool that would allow us to model very specific aspects of our business case, using data we had gathered from surveys of our members, and to be able to change any aspect of the whole calculation and see how it affected everything else. We also wanted to see what it would look like if, for example, things went on like this for the next five or ten years, rather than just the three years we were supposed to deliver.

That way we could easily look at which factors had the most impact on our plans and which were less critical. Flexibility was key, so this package doesn’t try to impose too many constraints on a business plan. On the other hand, this probably means that a bit more work is needed to get any results at all.

We hope that the package has remained generic enough for others to find it useful.

# Basic ideas

The package provides some S4 classes, methods and a bunch of functions for repeating tasks. Things have been kept quite abstract to be versatile. For example, when it comes to the flow of money, we basically only distinguish between money coming in (revenue) and money going out (expense). A this level we don’t distinguish between, e. g., costs, investments or interest. This is because these classifications can change between contexts, while the amount of money remains the same. So it’s up to us to treat a particular expense as an investment in our depreciation plan. This will become clearer as you work through the example.

The main workflow is

- to define all the relevant types of revenue and expense transactions for our business case,
- write lists that model which of these cash flows are relevant to which aspect of our plan,
- and construct objects containing all the respective cash flows.

These objects are used for the calculations, the lists define where the results of the calculations are visible, and the types simply ensure that we always know what specific transaction we are dealing with.

# Example case

For this vignette, let’s take an example case we want to model, a small greengrocer’s in Germany called Saftladen. It is planned to open its doors in 2024 and we would like to plan its first three years of business. The example is set in Germany because we don’t know about specific for business plan practices in other countries. Adapt it to your needs.

## Defining transaction types

Our new Saftladen will sell fruit (»Obst«) and vegetables (»Gemüse«). It will also offer T-shirts with its logo (»Merchandise«) and it plans to take out a loan (»Kreditaufnahme«). These are all sources of revenue for our business, so our plan starts by defining them as types of revenue:

    set_types(
        types=list(
            "Obst"=rgb(0.1,0.9,0.2,0.8),
            "Gemüse"=rgb(0.2,0.9,0.4,0.8),
            "Merchandise"=rgb(0.3,0.8,0.3,0.8),
            "Kreditaufnahme"=rgb(0.3,0.7,0.2,0.8)
        ),
        class="revenue",
        name="Saftladen"
    )

»What about those `rgb()` colors?« you might ask. Transaction types are defined as a named list of colors. These colors are used by some of the package’s plotting methods to provide a quick visual overview. If you don’t use them, just set them all to white or whatever. The really important thing here is that each income source must have a unique named entry as a revenue here. This also forces you to think about these sources early on.

This set of revenue types is named `"Saftladen"`. The name allows you to define several of these sets in parallel, e. g. if you’re modeling two businesses at the same time.

Note that this is not assigned to an object. Instead, `set_types()` is actually a wrapper for `options()` and adds this information to the options of the current session, as a named list called `businessPlanR`:

    options("businessPlanR")

    ## $businessPlanR
    ## $businessPlanR$Saftladen
    ## $businessPlanR$Saftladen$types
    ## $businessPlanR$Saftladen$types$revenue
    ## $businessPlanR$Saftladen$types$revenue$Obst
    ## [1] "#1AE633CC"
    ##
    ## $businessPlanR$Saftladen$types$revenue$Gemüse
    ## [1] "#33E666CC"
    ##
    ## $businessPlanR$Saftladen$types$revenue$Merchandise
    ## [1] "#4DCC4DCC"
    ##
    ## $businessPlanR$Saftladen$types$revenue$Kreditaufnahme
    ## [1] "#4DB333CC"

As for our expenses, we’re expecting to pay our employees (»Löhne und Gehälter«), social security contributions (»Sozialabgaben«), purchase goods (»Waren«), infrastructure (»Infrastruktur«), depreciation (»Abschreibung«), loan repayment (»Kredittilgung«), interest (»Zinsen«), and taxes (»Steuern«):

    set_types(
        types=list(
            "Löhne und Gehälter"=rgb(0.7,0.2,0.4,0.8),
            "Sozialabgaben"=rgb(0.7,0.2,0.4,0.8),
            "Waren"=rgb(0.7,0.3,0.5,0.8),
            "Infrastruktur"=rgb(0.75,0.2,0.4,0.8),
            "Abschreibung"=rgb(0.9,0.2,0.4,0.8),
            "Kredittilgung"=rgb(0.9,0.2,0.4,0.8),
            "Zinsen"=rgb(0.9,0.2,0.4,0.8),
            "Steuern"=rgb(0.9,0.2,0.4,0.8)
        ),
        class="expense",
        name="Saftladen"
    )

Consequently, each source of cash outflow must have a uniquely named entry as an expense here.

## One object to hold them all

Before we look at individual transactions, we create an object of class `operations`. You might think of this as the closest thing you have to the tabs in Excel spreadsheets you may have worked with in the past, as it collects all sorts of financial movements in a structured way.

This `operations` object is like your complete business case, and it’s what most of the methods in this package expect as input: You throw them everything you’ve got so they can pick out the relevant bits.

This object also defines the time period your business plan will cover:

    saftladen_2024_2026 <- operations(
        period=c("2024.01", "2026.12")
    )

`businessPlanR` uses months as the smallest time resolution.

## First transactions

With all revenue and expense types already set, we can now define the corresponding financial transactions. The methods we’ll use first are `revenue()` and `expense()`:

    # By default, revenues are repeated every month until
    # the value changes.
    rev_merch_2024_2026 <- revenue(
        type="Merchandise",
        category="Merch",
        name="T-Shirts",
        valid_types="Saftladen",
        "2024.03"=30,
        "2024.08"=40,
        "2025.03"=50,
        "2025.09"=70,
        "2026.02"=90,
        "2026.07"=100,
        "2026.10"=110
    )

    # We plan to produce our T-shirts in January each year.
    # Since these expenses should not be repeated every month,
    # we can set missing="0"; alternatives are "rep" for repeat
    # (default) or "interpol" for automatic interpolation.
    exp_merch_2024_2026 <- expense(
        type="Waren",
        category="Merch",
        name="T-Shirts",
        missing="0",
        valid_types="Saftladen",
        "2024.01"=200,
        "2025.01"=400,
        "2026.01"=600
    )

Let’s look at the financial values of these objects. This can be done using `get_value()`:

    get_value(rev_merch_2024_2026)

    ##          type category     name 2024.03 2024.04 2024.05 2024.06 2024.07 2024.08
    ## 1 Merchandise    Merch T-Shirts      30      30      30      30      30      40
    ##   2024.09 2024.10 2024.11 2024.12 2025.01 2025.02 2025.03 2025.04 2025.05 2025.06
    ## 1      40      40      40      40      40      40      50      50      50      50
    ##   2025.07 2025.08 2025.09 2025.10 2025.11 2025.12 2026.01 2026.02 2026.03 2026.04
    ## 1      50      50      70      70      70      70      70      90      90      90
    ##   2026.05 2026.06 2026.07 2026.08 2026.09 2026.10
    ## 1      90      90     100     100     100     110

    get_value(exp_merch_2024_2026)

    ##    type category     name 2024.01 2024.02 2024.03 2024.04 2024.05 2024.06 2024.07
    ## 1 Waren    Merch T-Shirts     200       0       0       0       0       0       0
    ##   2024.08 2024.09 2024.10 2024.11 2024.12 2025.01 2025.02 2025.03 2025.04 2025.05
    ## 1       0       0       0       0       0     400       0       0       0       0
    ##   2025.06 2025.07 2025.08 2025.09 2025.10 2025.11 2025.12 2026.01
    ## 1       0       0       0       0       0       0       0     600

As you can see, `revenue()` and `expense()` always calculate one entry per month. You can control these calculations directly via the `missing` argument (see the comment in the example). It is also possible to set a value with `per_use`, which will not use the numbers as actual financial values but, the number of times that value has been used. So if our shirts sell for 10 bucks and we start selling three shirts a month, this will give us the same numbers:

    # By default, the sales are repeated each month until
    # the value changes.
    rev_merch_2024_2026 <- revenue(
        type="Merchandise",
        category="Merch",
        name="T-Shirts",
        per_use=10,
        valid_types="Saftladen",
        "2024.03"=3,
        "2024.08"=4,
        "2025.03"=5,
        "2025.09"=7,
        "2026.02"=9,
        "2026.07"=10,
        "2026.10"=11
    )
    get_value(rev_merch_2024_2026)

    ##          type category     name 2024.03 2024.04 2024.05 2024.06 2024.07 2024.08
    ## 1 Merchandise    Merch T-Shirts      30      30      30      30      30      40
    ##   2024.09 2024.10 2024.11 2024.12 2025.01 2025.02 2025.03 2025.04 2025.05 2025.06
    ## 1      40      40      40      40      40      40      50      50      50      50
    ##   2025.07 2025.08 2025.09 2025.10 2025.11 2025.12 2026.01 2026.02 2026.03 2026.04
    ## 1      50      50      70      70      70      70      70      90      90      90
    ##   2026.05 2026.06 2026.07 2026.08 2026.09 2026.10
    ## 1      90      90     100     100     100     110

The `get_value()` method can also show quarterly and yearly totals (`"month"` is just the default):

    get_value(rev_merch_2024_2026, resolution="quarter")

    ##          type category     name 2024.Q1 2024.Q2 2024.Q3 2024.Q4 2025.Q1 2025.Q2
    ## 1 Merchandise    Merch T-Shirts      30      90     110     120     130     150
    ##   2025.Q3 2025.Q4 2026.Q1 2026.Q2 2026.Q3 2026.Q4
    ## 1     170     210     250     270     300     110

    get_value(exp_merch_2024_2026, resolution="year")

    ##    type category     name 2024 2025 2026
    ## 1 Waren    Merch T-Shirts  200  400  600

## Populating the `operations` object

At the moment, our two transactions are not yet part of our business case. We need to add them to our `operations` object:

    update_operations(saftladen_2024_2026) <- rev_merch_2024_2026
    update_operations(saftladen_2024_2026) <- exp_merch_2024_2026

Note that both transactions do not cover the full period we have defined: Missing months are assumed to have a value of zero. Also, we don’t really need individual transaction objects, we can assign the output of both `revenue()` and `expense()` directly to `update_operations()<-` or even `operations(...)`.

With these few functions, you should already be able to write your own wrapper functions to implement many of the financial movements you need to cover. Because you can assign financial values to each month individually, you are free to write your own algorithms for whatever complex cash flows you assume, and combine all the results in an `operations` object.

There are also some additional functions to help you with very common tasks (or at least we’ve come across them so often that we’ve written functions for them):

- `first_last()` creates a list of two elements from January of the first given year to December of the last, both with the same amount specified.
- `growth()` calculates the differences between successive values in a numerical vector.
- `regularly()` creates lists of recurring financial transactions for given years (useful, for example, to define quarterly transactions, which may even have different amounts).
- `regularly_delayed()` extends `regularly()` to cover cases where you know the annual total of transactions, but they don’t start at the beginning of the year.
- `calc_staff()` calculates the number of staff needed to complete a given task.
- `fin_needs()` tries to estimate your capital needs from the cash flow.

## Special transaction cases

There are also some additional object classes that cover additional types of transactions:

- `loan()` defines loans with interest and repayment schedule.
- `depreciation()` defines the depreciation schedule for defined items, i. e. your inventory.
- `transaction_plan()` calculates complete repayment and depreciation schedules from single or multiple `loan` or `depreciation` objects according to the specifics defined with these objects.

You can compare the `transaction_plan` class to `operations`, as both are structured collections of multiple objects. There’s also an `update_plan()<-` method to add or replace objects in existing `transaction_plan` collections, similar to `update_operations()<-`.

## Simple modelling

Our goal, of course, it to have nice tables for our income statement or liquidity plan. Obviously, we can’t just turn the `operations` object into a table for this, but have to decide which of the statements are relevant for a particular type of table. What’s more, these are usually not just single large tables, but are structured into (probably even nested) sections.

So we need to define the structure for the tables we need, including the relevant transactions and their place in a table. This is done with the functions `table_model()` (which can do some validity checking) and `model_node()` (which is used »inside« `table_model()` for nested models. Don’t wonder too much about the term model here, we could have called it template or something.

Let’s sketch a very simple model that subtracts expenses from revenues to get an income statement:

    saftladen_inc_stamt <- table_model(
        # Gross income from various sources
        "Bruttoeinnahmen"=model_node(
            revenue=c(
                "Obst",
                "Gemüse",
                "Merchandise"
            )
        ),
        # Gross revenue, i.e. minus costs of goods
        "Bruttoertrag"=model_node(
            carry="Bruttoeinnahmen",
            expense=c(
                "Waren"
            )
        ),
        # Gross profit, i.e. minus operating and depreciation expenses
        "Betriebsergebnis"=model_node(
            carry="Bruttoertrag",
            expense=c(
                "Löhne und Gehälter",
                "Sozialabgaben",
                "Infrastruktur",
                "Abschreibung"
            )
        ),
        # Operating profit, i.e. minus interest expenses
        "Gewinn vor Steuern"=model_node(
            carry="Betriebsergebnis",
            expense=c(
                "Zinsen"
            )
        ),
        # Profit after taxes
        "Nettogewinn"=model_node(
            carry="Gewinn vor Steuern",
            expense=c(
              "Steuern"
            )
        ),
        valid_types="Saftladen"
    )

As you can see, we’re reusing some of the type names we defined for `revenue` and `expense`. Each type listed is used to calculate the sum for the node (as we call a named list in this context) in which it appears, with revenues added and expenses subtracted.

All entries must be named, here »Bruttoeinnahmen« (gross revenue) and »Bruttoertrag« (gross profit), as these names will be used in the actual table. Each node could contain child nodes (just add named objects with `model_node()`), so more complex hierarchical structures can be designed, but let’s keep it simple for now.

Providing `valid_types="Saftladen"` allows `table_model()` to check that all revenues and expenses have actually been defined, to avoid typos and missing values in our tables. Also note the use of `carry="Bruttoeinnahmen"` in the second node, which references the previous one. The effect of this is that the calculated sum of the referenced node is used as the initial value of the referencing node when calculating subtotals for each table section, before any revenues are added or expenses subtracted.

With an initial model defined, we can now condense our `operations` object into the tables we need:

    condense(
        saftladen_2024_2026,
        model=saftladen_inc_stamt
    )

    ##             Position        Type 2024 2025 2026
    ## 1    Bruttoeinnahmen Merchandise  350  660  930
    ## 2    Bruttoeinnahmen         Sum  350  660  930
    ## 3       Bruttoertrag       Waren -200 -400 -600
    ## 4       Bruttoertrag         Sum  150  260  330
    ## 5   Betriebsergebnis         Sum  150  260  330
    ## 6 Gewinn vor Steuern         Sum  150  260  330
    ## 7        Nettogewinn         Sum  150  260  330

Even from this very primitive example, you can see how `condense()` calculates subtotals (where Type is Sum) for each section (Position) of our model. Many methods in this package support the `resolution` argument, as we’ve already seen with `get_value()`, and so does `condense()`:

    condense(
        saftladen_2024_2026,
        model=saftladen_inc_stamt,
        resolution="quarter"
    )

    ##             Position        Type 2024.Q1 2024.Q2 2024.Q3 2024.Q4 2025.Q1 2025.Q2
    ## 1    Bruttoeinnahmen Merchandise      30      90     110     120     130     150
    ## 2    Bruttoeinnahmen         Sum      30      90     110     120     130     150
    ## 3       Bruttoertrag       Waren    -200       0       0       0    -400       0
    ## 4       Bruttoertrag         Sum    -170      90     110     120    -270     150
    ## 5   Betriebsergebnis         Sum    -170      90     110     120    -270     150
    ## 6 Gewinn vor Steuern         Sum    -170      90     110     120    -270     150
    ## 7        Nettogewinn         Sum    -170      90     110     120    -270     150
    ##   2025.Q3 2025.Q4 2026.Q1 2026.Q2 2026.Q3 2026.Q4
    ## 1     170     210     250     270     300     110
    ## 2     170     210     250     270     300     110
    ## 3       0       0    -600       0       0       0
    ## 4     170     210    -350     270     300     110
    ## 5     170     210    -350     270     300     110
    ## 6     170     210    -350     270     300     110
    ## 7     170     210    -350     270     300     110

This is just a raw data frame. But we can also use the `kable_bpR()` method on `operations` objects along with a table model to get nicely formatted tables in RMarkdown. The methods make heavy use of the `kableExtra` package:

    kable_bpR(
        saftladen_2024_2026,
        model=saftladen_inc_stamt,
        resolution="year"
    )

| 2024 | 2025 | 2026
--- | --- | --- | ---
Bruttoeinnahmen
Merchandise | 350 € | 660 € | 930 €
| 350 € | 660 € | 930 €
Bruttoertrag
Waren | -200 € | -400 € | -600 €
| 150 € | 260 € | 330 €
Betriebsergebnis
| 150 € | 260 € | 330 €
Gewinn vor Steuern
| 150 € | 260 € | 330 €
Nettogewinn
| 150 € | 260 € | 330 €

## Add more data

With only one revenue and expense stream, our plan is still not very meaningful. So let’s add some more transactions:

    # Apples
    update_operations(saftladen_2024_2026) <- revenue(
        type="Obst",
        category="Obst",
        name="Äpfel",
        valid_types="Saftladen",
        "2024.01"=2200,
        "2024.08"=3000,
        "2024.09"=3400,
        "2024.12"=2600,
        "2025.01"=2300,
        "2025.08"=3100,
        "2025.09"=3500,
        "2025.12"=2700,
        "2026.01"=2400,
        "2026.08"=3200,
        "2026.09"=3600,
        "2026.12"=2800
    )
    update_operations(saftladen_2024_2026) <- expense(
        type="Waren",
        category="Obst",
        name="Äpfel",
        valid_types="Saftladen",
        "2024.01"=1650,
        "2024.08"=2250,
        "2024.09"=2550,
        "2024.12"=1950,
        "2025.01"=1725,
        "2025.08"=2325,
        "2025.09"=2625,
        "2025.12"=2025,
        "2026.01"=1800,
        "2026.08"=2400,
        "2026.09"=2700,
        "2026.12"=2100
    )
    # Grapes
    update_operations(saftladen_2024_2026) <- revenue(
        type="Obst",
        category="Obst",
        name="Trauben",
        missing="interpol",
        valid_types="Saftladen",
        "2024.01"=480,
        "2025.01"=590,
        "2026.01"=730,
        "2026.12"=820
    )
    update_operations(saftladen_2024_2026) <- expense(
        type="Waren",
        category="Obst",
        name="Trauben",
        missing="interpol",
        valid_types="Saftladen",
        "2024.01"=340,
        "2025.01"=410,
        "2026.01"=510,
        "2026.12"=580
    )
    # Potatoes
    update_operations(saftladen_2024_2026) <- revenue(
        type="Gemüse",
        category="Gemüse",
        name="Kartoffeln",
        missing="interpol",
        valid_types="Saftladen",
        "2024.01"=2440,
        "2025.01"=2670,
        "2026.01"=2800,
        "2026.12"=2980
    )
    update_operations(saftladen_2024_2026) <- expense(
        type="Waren",
        category="Gemüse",
        name="Kartoffeln",
        missing="interpol",
        valid_types="Saftladen",
        "2024.01"=1950,
        "2025.01"=2130,
        "2026.01"=2240,
        "2026.12"=2400
    )

Let’s look at the updated table:

    kable_bpR(
        saftladen_2024_2026,
        model=saftladen_inc_stamt,
        resolution="year"
    )

| 2024 | 2025 | 2026
--- | --- | --- | ---
Bruttoeinnahmen
Obst | 37.565 € | 40.250 € | 42.900 €
Gemüse | 30.545 € | 32.755 € | 34.680 €
Merchandise | 350 € | 660 € | 930 €
| 68.460 € | 73.665 € | 78.510 €
Bruttoertrag
Waren | -52.455 € | -56.335 € | -60.180 €
| 16.005 € | 17.330 € | 18.330 €
Betriebsergebnis
| 16.005 € | 17.330 € | 18.330 €
Gewinn vor Steuern
| 16.005 € | 17.330 € | 18.330 €
Nettogewinn
| 16.005 € | 17.330 € | 18.330 €

It now summarises all the goods we have defined so far, grouped by `type` and placed according to our model. If you want to examine all the data in more detail, you can use the `detailed` view of the table:

    kable_bpR(
        saftladen_2024_2026,
        model=saftladen_inc_stamt,
        resolution="year",
        detailed=TRUE
    )

|  |  | 2024 | 2025 | 2026
--- | --- | --- | --- | --- | ---
Revenue
Merchandise | Merch | T-Shirts | 350 € | 660 € | 930 €
Obst | Obst | Äpfel | 31.200 € | 32.400 € | 33.600 €
Obst | Obst | Trauben | 6.365 € | 7.850 € | 9.300 €
Gemüse | Gemüse | Kartoffeln | 30.545 € | 32.755 € | 34.680 €
Exepense
Waren | Merch | T-Shirts | -200 € | -400 € | -600 €
Waren | Obst | Äpfel | -23.400 € | -24.300 € | -25.200 €
Waren | Obst | Trauben | -4.465 € | -5.470 € | -6.540 €
Waren | Gemüse | Kartoffeln | -24.390 € | -26.165 € | -27.840 €

## Special transactions

As mentioned earlier, the package supports some common special cases of transaction plans. The idea here is to define a complete investment or loan repayment plan and let the methods of this package select relevant aspects of these plans for different tables, such as the profit and loss statement or the liquidity plan.

### Investments and depreciation schedules

Let’s say our shop needs two new cash registers. We’ll buy one for 450 € in the first month of our plan, and the second one a year and a half later. Let the amortisation period for this type of investment be 72 months:

    dep_cashreg1 <- depreciation(
        type="Abschreibung",
        category="Laden",
        name="Kasse 1",
        amount=450,
        obsolete=72,
        invest_month="2024.01",
        valid_types="Saftladen"
    )
    dep_cashreg2 <- depreciation(
        type="Abschreibung",
        category="Laden",
        name="Kasse 2",
        amount=450,
        obsolete=72,
        invest_month="2025.07",
        valid_types="Saftladen"
    )

The objects now contain the full investment and depreciation plans for both cash registers. We can add both aspects to our operations object separately, but we need to tell `update_operations()` how to handle them:

    update_operations(
        saftladen_2024_2026,
        as_transaction=list(
            c(
                to="expense",
                aspect="investment",
                valid_types="Saftladen",
                type="Infrastruktur"
            ),
            c(
                to="expense",
                aspect="depreciation",
                valid_types="Saftladen",
                type="Abschreibung"
            )
        )
    ) <- dep_cashreg1
    update_operations(
        saftladen_2024_2026,
        as_transaction=list(
            c(
                to="expense",
                aspect="investment",
                valid_types="Saftladen",
                type="Infrastruktur"
            ),
            c(
                to="expense",
                aspect="depreciation",
                valid_types="Saftladen",
                type="Abschreibung"
            )
        )
    ) <- dep_cashreg2

    kable_bpR(
        saftladen_2024_2026,
        model=saftladen_inc_stamt,
        resolution="year"
    )

| 2024 | 2025 | 2026
--- | --- | --- | ---
Bruttoeinnahmen
Obst | 37.565 € | 40.250 € | 42.900 €
Gemüse | 30.545 € | 32.755 € | 34.680 €
Merchandise | 350 € | 660 € | 930 €
| 68.460 € | 73.665 € | 78.510 €
Bruttoertrag
Waren | -52.455 € | -56.335 € | -60.180 €
| 16.005 € | 17.330 € | 18.330 €
Betriebsergebnis
Infrastruktur | -450 € | -450 € | 0 €
Abschreibung | -75 € | -112 € | -150 €
| 15.480 € | 16.768 € | 18.180 €
Gewinn vor Steuern
| 15.480 € | 16.768 € | 18.180 €
Nettogewinn
| 15.480 € | 16.768 € | 18.180 €

We can also combine both objects into one transaction plan to get one comprehensive table:

    dep_plan <- transaction_plan(plan_type="depreciation")
    update_plan(dep_plan) <- dep_cashreg1
    update_plan(dep_plan) <- dep_cashreg2

    kable_bpR(
        dep_plan,
        dep_names=c(
            investment="Investition",
            depreciation="Abschreibung",
            value="Wert",
            sum="Summe"
        ),
        resolution="year",
        years=2024:2029
    )

|  |  | 2024 | 2025 | 2026 | 2027 | 2028 | 2029
--- | --- | --- | --- | --- | --- | --- | --- | ---
Laden | Kasse 1 | Investition | 450,00 € | 0,00 € | 0,00 € | 0,00 € | 0,00 € | 0,00 €
Laden | Kasse 1 | Abschreibung | -75,00 € | -75,00 € | -75,00 € | -75,00 € | -75,00 € | -75,00 €
Laden | Kasse 1 | Wert | 375,00 € | 300,00 € | 225,00 € | 150,00 € | 75,00 € | 0,00 €
Laden | Kasse 2 | Investition | 0,00 € | 450,00 € | 0,00 € | 0,00 € | 0,00 € | 0,00 €
Laden | Kasse 2 | Abschreibung | 0,00 € | -37,50 € | -75,00 € | -75,00 € | -75,00 € | -75,00 €
Laden | Kasse 2 | Wert | 0,00 € | 412,50 € | 337,50 € | 262,50 € | 187,50 € | 112,50 €
| Summe | Investition | 450,00 € | 450,00 € | 0,00 € | 0,00 € | 0,00 € | 0,00 €
| Summe | Abschreibung | -75,00 € | -112,50 € | -150,00 € | -150,00 € | -150,00 € | -150,00 €
| Summe | Wert | 375,00 € | 712,50 € | 562,50 € | 412,50 € | 262,50 € | 112,50 €

### Loans and interest

Similar to investments and depreciation, loans are easy to calculate. The profit and loss statement only looks at the interest, but we’ll also add the principal rates which we’ll need later for our liquidity plan:

    loan_2024 <- loan(
        type="Kreditaufnahme",
        category="Bank",
        name="Anschubfinanzierung",
        amount=5000,
        period=60,
        interest=0.075,
        first_month="2024.01",
        schedule=c("amortization"),
        valid_types="Saftladen"
    )
    update_operations(
        saftladen_2024_2026,
        as_transaction=list(
            c(
                to="expense",
                aspect="principal",
                valid_types="Saftladen",
                type="Kredittilgung"
            ),
            c(
                to="expense",
                aspect="interest",
                valid_types="Saftladen",
                type="Zinsen"
            ),
            c(
                to="revenue",
                aspect="balance_start",
                valid_types="Saftladen",
                type="Kreditaufnahme"
            )
        )
    ) <- loan_2024

    kable_bpR(
        saftladen_2024_2026,
        model=saftladen_inc_stamt,
        resolution="year"
    )

| 2024 | 2025 | 2026
--- | --- | --- | ---
Bruttoeinnahmen
Obst | 37.565 € | 40.250 € | 42.900 €
Gemüse | 30.545 € | 32.755 € | 34.680 €
Merchandise | 350 € | 660 € | 930 €
| 68.460 € | 73.665 € | 78.510 €
Bruttoertrag
Waren | -52.455 € | -56.335 € | -60.180 €
| 16.005 € | 17.330 € | 18.330 €
Betriebsergebnis
Infrastruktur | -450 € | -450 € | 0 €
Abschreibung | -75 € | -112 € | -150 €
| 15.480 € | 16.768 € | 18.180 €
Gewinn vor Steuern
Zinsen | -341 € | -266 € | -191 €
| 15.139 € | 16.502 € | 17.989 €
Nettogewinn
| 15.139 € | 16.502 € | 17.989 €

Similar to the investment and depreciation plans, you could also combine several loans into one transaction plan to include them as a table. But in our example there is only one loan:

    loan_plan <- transaction_plan(plan_type="loan")
    update_plan(loan_plan) <- loan_2024

    kable_bpR(
        loan_plan,
        loan_names=c(
            balance_start="Restschuld Beginn",
            interest="Zinsen",
            principal="Tilgung",
            total="Zahlung",
            cumsum="Kumuliert",
            balance_remain="Restschuld Ende",
            sum="Summe"
        ),
        resolution="year"
    )

|  |  | 2024 | 2025 | 2026 | 2027 | 2028
--- | --- | --- | --- | --- | --- | --- | ---
Bank | Anschubfinanzierung | Restschuld Beginn | 5.000,00 € | 4.000,00 € | 3.000,00 € | 2.000,00 € | 1.000,00 €
Bank | Anschubfinanzierung | Zinsen | 340,51 € | 265,54 € | 190,56 € | 115,58 € | 40,61 €
Bank | Anschubfinanzierung | Tilgung | 999,96 € | 999,96 € | 999,96 € | 999,96 € | 999,96 €
Bank | Anschubfinanzierung | Zahlung | 1.340,47 € | 1.265,50 € | 1.190,52 € | 1.115,54 € | 1.040,57 €
Bank | Anschubfinanzierung | Kumuliert | 1.340,47 € | 2.605,97 € | 3.796,49 € | 4.912,03 € | 5.952,60 €
Bank | Anschubfinanzierung | Restschuld Ende | 4.000,00 € | 3.000,00 € | 2.000,00 € | 1.000,00 € | 0,00 €
| Summe | Restschuld Beginn | 5.000,00 € | 4.000,00 € | 3.000,00 € | 2.000,00 € | 1.000,00 €
| Summe | Zinsen | 340,51 € | 265,54 € | 190,56 € | 115,58 € | 40,61 €
| Summe | Tilgung | 999,96 € | 999,96 € | 999,96 € | 999,96 € | 999,96 €
| Summe | Zahlung | 1.340,47 € | 1.265,50 € | 1.190,52 € | 1.115,54 € | 1.040,57 €
| Summe | Kumuliert | 1.340,47 € | 2.605,97 € | 3.796,49 € | 4.912,03 € | 5.952,60 €
| Summe | Restschuld Ende | 4.000,00 € | 3.000,00 € | 2.000,00 € | 1.000,00 € | 0,00 €

# Cash flow (liquidity) plans

The steps to create a cash flow plan are simple: Define another model to include all the relevant types of revenue and expense, and then call `kable_bpR(cashflow=TRUE)` using that model:

    saftladen_cashflow <- table_model(
        "Einzahlungen"=model_node(
            revenue=c(
                "Obst",
                "Gemüse",
                "Merchandise",
                "Kreditaufnahme"
            )
        ),
        "Auszahlungen"=model_node(
            expense=c(
                "Löhne und Gehälter",
                "Sozialabgaben",
                "Infrastruktur",
                "Waren",
                "Zinsen",
                "Kredittilgung",
                "Steuern"
            )
        )
    )

    kable_bpR(
        saftladen_2024_2026,
        model=saftladen_cashflow,
        resolution="year",
        cashflow=TRUE
    )

| 2024 | 2025 | 2026
--- | --- | --- | ---
Begin
| 0 € | 19.215 € | 34.829 €
Einzahlungen
Obst | 37.565 € | 40.250 € | 42.900 €
Gemüse | 30.545 € | 32.755 € | 34.680 €
Merchandise | 350 € | 660 € | 930 €
Kreditaufnahme | 5.000 € | 0 € | 0 €
| 73.460 € | 73.665 € | 78.510 €
Auszahlungen
Infrastruktur | -450 € | -450 € | 0 €
Waren | -52.455 € | -56.335 € | -60.180 €
Zinsen | -341 € | -266 € | -191 €
Kredittilgung | -1.000 € | -1.000 € | -1.000 €
| -54.245 € | -58.050 € | -61.371 €
End
| 19.215 € | 34.829 € | 51.969 €

# Dynamic modelling

So far we have used fixed numbers in our objects. Sure, that’s just an example and it doesn’t really matter if those numbers make sense. But if this were your actual business plan, these numbers would represent of how you think your business will develop. Nobody expects your calculations to predict the future to three decimal places, they should be as plausible as possible. Otherwise, your plan may not convince readers to invest in your idea.

Readers might ask themselves: Why do you think you will sell this amount of potatoes in a particular month? How did you arrive at these figures? And you will often find that when things change (like laws affecting your business or interest rates) or you learn new information that causes you to adjust your assumptions, you will need to update some numbers, which in turn will change your calculations right through to the end result. We have written this package to deal with all this intuitively.

Here is how we did it, and it helped us a lot: We used RMarkdown to write our business plan. In that document, we explained how we expected different aspects of our undertaking to interact, and what initial values we knew, or why we thought it was plausible to propose a certain value. Since these initial assumptions could change at any time, all actual numbers should be defined as a vector or list at the beginning of the document and referenced in the text, so that by changing the values in that object, you not only update your calculations, but also the numbers that appear in the documentation. Finally, we translated these expectations into simple functions to calculate the actual numbers that would then be used as revenues and expenses.

This separates the numbers we assume (vector/list) from the interactions between the basic aspects of our business we assume (functions), i. e. both can be adjusted separately.

## Modelling sales

Let’s go back to our example and try to do this for our apple sales. You have talked to local farmers and studied both the neighbourhood and consumer analyses. You have come up with the following figures:

    assumed <- list(
        total_customers=1860, # number of potential customers?
        pct_year_1st=25,      # how many of them will buy at our store?
        pct_year_last=35,     # how will the number progress?
        pct_demand=30,        # how much will they buy from us?
        kg_per_customer=c(    # how many Kg were cosumed in recent years?
            apples=23
        ),
        exp_per_kg=c(         # how much will it cost to buy goods?
            apples=2.3
        ),
        rev_per_kg=c(         # at what price will we sell?
            apples=3.49
        )
    )

In our business plan, we can now use this list of numbers to explain our expectations. The package has a function called `nice_numbers()` to format numbers, round them and add a prefix or suffix. So `nice_numbers(assumed[["total_customers"]])` prints as 1.860, `nice_numbers(assumed[["pct_year_1st"]], suffix="%")` as 25 %, and `nice_numbers(assumed[["exp_per_kg"]][["apples"]], suffix="€", digits=2)` as 2,30 €:

> According to the demographic analysis, there are a total of 1.860 potential customers for the Saftladen. We assume that we will reach 25 % of this market in the first year, increasing to 35 % in the third year as a result of our marketing efforts. We also assume that our average customer will buy 30 % of their annual apple requirements in our store. From consumer research in recent years, we know that the total annual consumption of apples per person is around 23 Kg on average. We have an agreement with local farmers to buy apples at 2,30 € per Kg, and we calculate a selling price of 3,49 € per Kg.

Great! So how many customers can we expect if these assumptions hold?

    n_customers <- function(data, years){
        customer_pct <- seq(
            from=data[["pct_year_1st"]],
            to=data[["pct_year_last"]],
            length.out=length(years)
        ) / 100
        result <- round(data[["total_customers"]] * customer_pct)
        names(result) <- years
        return(result)
    }

    n_customers(data=assumed, years=2024:2026)

    ## 2024 2025 2026
    ##  465  558  651

How many Kg of apples will we sell?

    kg_sale <- function(what, data, years){
        avg_amount_total <- data[["kg_per_customer"]][[what]]
        result <- round(
            avg_amount_total *
            (data[["pct_demand"]] / 100) *
            n_customers(data=data, years=years)
        )
        return(result)
    }

    kg_sale(what="apples", data=assumed, years=2024:2026)

    ## 2024 2025 2026
    ## 3208 3850 4492

Apple season usually is from August to November, so let’s not spread this equally across the year:

    spread_sales <- function(what, distribution, data, years){
        yearly_sales <- kg_sale(what=what, data=data, years=years)
        distrib <- round(as.vector(sapply(
            yearly_sales,
            function(kg){
                kg * distribution
            }
        )))
        names(distrib) <- paste0(rep(years, each=length(distribution)), names(distribution))
        return(distrib)
    }

    sales_apples_kg <- spread_sales(
        what="apples",
        distribution=c(
            ".01"=0.20,
            ".08"=0.27,
            ".09"=0.30,
            ".12"=0.23
        ),
        data=assumed,
        years=2024:2026
    )

    # multiply by prices
    (sales_apples_expense <- round(sales_apples_kg * assumed[["exp_per_kg"]][["apples"]]))

    ## 2024.01 2024.08 2024.09 2024.12 2025.01 2025.08 2025.09 2025.12 2026.01 2026.08
    ##    1477    1992    2213    1697    1771    2392    2656    2038    2065    2790
    ## 2026.09 2026.12
    ##    3100    2376

    (sales_apples_revenue <- round(sales_apples_kg * assumed[["rev_per_kg"]][["apples"]]))

    ## 2024.01 2024.08 2024.09 2024.12 2025.01 2025.08 2025.09 2025.12 2026.01 2026.08
    ##    2241    3022    3357    2576    2687    3630    4031    3092    3134    4233
    ## 2026.09 2026.12
    ##    4705    3605

Now we apply these figures to our operations object:

    update_operations(saftladen_2024_2026) <- revenue(
        type="Obst",
        category="Obst",
        name="Äpfel",
        valid_types="Saftladen",
        .list=as.list(sales_apples_revenue)
    )
    update_operations(saftladen_2024_2026) <- expense(
        type="Waren",
        category="Obst",
        name="Äpfel",
        valid_types="Saftladen",
        .list=as.list(sales_apples_expense)
    )

    kable_bpR(
        saftladen_2024_2026,
        model=saftladen_inc_stamt,
        resolution="year"
    )

| 2024 | 2025 | 2026
--- | --- | --- | ---
Bruttoeinnahmen
Obst | 37.721 € | 45.474 € | 53.191 €
Gemüse | 30.545 € | 32.755 € | 34.680 €
Merchandise | 350 € | 660 € | 930 €
| 68.616 € | 78.889 € | 88.801 €
Bruttoertrag
Waren | -49.722 € | -56.830 € | -63.901 €
| 18.894 € | 22.059 € | 24.900 €
Betriebsergebnis
Infrastruktur | -450 € | -450 € | 0 €
Abschreibung | -75 € | -112 € | -150 €
| 18.369 € | 21.496 € | 24.750 €
Gewinn vor Steuern
Zinsen | -341 € | -266 € | -191 €
| 18.028 € | 21.231 € | 24.559 €
Nettogewinn
| 18.028 € | 21.231 € | 24.559 €

Our previous revenues and expenses for apples have been replaced by `update_operations()`. Instead of a bunch of numbers that were hard to understand, these sales are now transparently derived from data that we have explained.

# Discussing numbers with Shiny

Finally, you can use functions like the ones we just wrote in a Shiny application. More specifically, if you don’t call methods like `revenue()` or `loan()` directly, but inside a custom function, you can use the same functions in a Shiny app and in your RMarkdown document, making sure that all calculations stay in sync. That is, you write your calculations in separate R script files that can be sourced by both Shiny and RMarkdown.

It can be very revealing to use such a Shiny app to examine the overall impact of changes to the numerical assumptions in your business plan. For example, you could use slider controls for each value defined in `assumed` and explore how your business would be affected if there were fewer customers than expected or if selling prices fell.

We added a permalink to the Shiny app we used to discuss our business model as a team, which included all the configurable parameters. Such a Shiny permalink can be turned into a named list using `businessPlanR`’s `permalink2list()` function, which in turn makes it very easy to copy a custom configuration from Shiny into an RMarkdown document, so that all calculations and tables can be replicated.

The relevant functions of the `businessPlanR` package have been designed to produce output that should be usable in HTML and LaTeX contexts, without the need to adapt the calls.