Browse the docs
Example 4 of 13
Mbira border post
Based on mbira-border-post-model.xlsm
The border post was built by copying the toll road workbook and rewriting the revenue sheets, so 22 of its 26 sheets match the toll model. In Rufineer the shared part becomes a package, and the border model only writes what is different. Design example: written in the full v0.5 language. The compiler builds the working core today, so this one doesn't compile yet. Source: examples/design/border/.
- Workbook
- 26 sheets, same ruler and layout as the toll road
- Revenue
- 6 vehicle classes, incoming and outgoing 2022 crossings, fees stepping up over three construction years
- Other income
- Residential rentals on 5,778 m²
- Capital
- Building and site works USD 17.4m, equipment USD 4.6m, wastewater plant USD 3.5m
- Rebuild shown
- 4 files, 55 lines, plus the shared concession package
Where each sheet goes
| Workbook sheets | Rufineer |
|---|---|
| InpC (border inputs) | inputs.rfn, with tables read from sheets of border_inputs.xlsx |
| Rev | rev.rfn |
| ConCost | cons_cost.rfn (different cost lines) |
| All other sheets | the shared concession package |
Source 4 files shown, 55 lines
/// Everything the toll road and border post have in common
package concession {
requires rev.total: USD, cons_cost.total: USD, ops.maintenance: USD
provides cons_fin, ops, debt, reserves, equity, tax, cash, metrics, statements, checks
}model "Mbira Border Post" {
currency USD
data "border_inputs.xlsx"
include "../concession"
}
timeline {
start 2024-10-01
fiscal_year ends Dec
step quarter
construction 36m detail month
operations until construction.start + inputs.concession
}
list classes = inputs.crossings.rows // abnormal, goods, heavy, bus, minibus, private/// Fees step up over the three construction years
table fee: USD/vehicle sheet "Fees" {
rows phase
columns abnormal "Abnormal load", goods "Goods (commercial)", heavy,
bus "Buses", minibus "Minibuses", private "Private vehicles"
}
/// 2022 crossings, Revenue authority letter of March 2023, with growth per class
table crossings {
rows class "Vehicle class"
columns incoming: vehicle/y, outgoing: vehicle/y, growth: %/y "Growth rate"
}
inputs "Border post" {
concession = 25y
fee_growth = 3.64 %/y
housing_area = 5_778 m2
rental_rate = 0 USD/m2/q // base case; 4 USD/m2/q in the rental cases
rental_growth = 3.64 %/y
operations_cost = 12.5 %
}module rev "Revenue" {
for c in classes {
fee[c]: USD/vehicle = match {
construction.year == 1 => inputs.fee[construction_year_1, c]
construction.year == 2 => inputs.fee[construction_year_2, c]
construction.year == 3 => inputs.fee[construction_year_3, c]
operations => inputs.fee[operations, c]
} | grow inputs.fee_growth yearly in Dec
// vehicle/y declared as vehicle: scaled to the quarter by year_frac
crossings[c]: vehicle = (inputs.crossings[c].incoming + inputs.crossings[c].outgoing)
* grow(inputs.crossings[c].growth, from: timeline.start)
revenue[c]: USD = crossings[c] * fee[c]
}
rentals: USD = inputs.housing_area * inputs.rental_rate
| grow inputs.rental_growth yearly in Dec
| during operations
total: USD = sum(revenue[*]) + rentals
}What this model needed from the language
- Packages with a stated interface.
requireslists the lines a model must supply andprovideslists what the package adds. A fix to the shared debt logic reaches both models in one change. - Guarded
match. Fees step up by construction year. Amatchwithout a subject takes the first true condition.